ARM Support without GLSL
This commit is contained in:
parent
46adbfa9ed
commit
717b976875
344
CMakeLists.txt
344
CMakeLists.txt
|
@ -106,9 +106,22 @@ if(DOLPHIN_IS_STABLE)
|
|||
else()
|
||||
set(DOLPHIN_VERSION_PATCH ${DOLPHIN_WC_REVISION})
|
||||
endif()
|
||||
message(${CMAKE_SYSTEM_PROCESSOR})
|
||||
if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "^arm")
|
||||
set(_M_GENERIC 1)
|
||||
set(_M_ARM 1)
|
||||
add_definitions(-marm -march=armv7-a)
|
||||
add_definitions(-D_M_ARM=1)
|
||||
add_definitions(-D_M_GENERIC=1)
|
||||
endif()
|
||||
|
||||
# Set these next two lines to test generic
|
||||
#set(_M_GENERIC 1)
|
||||
#add_definitions(-D_M_GENERIC=1)
|
||||
# Various compile flags
|
||||
add_definitions(-msse2)
|
||||
if(NOT _M_GENERIC)
|
||||
add_definitions(-msse2)
|
||||
endif()
|
||||
|
||||
include(CheckCXXCompilerFlag)
|
||||
macro(check_and_add_flag var flag)
|
||||
|
@ -258,135 +271,141 @@ if(USE_EGL)
|
|||
endif()
|
||||
add_definitions(-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE)
|
||||
|
||||
option(ANDROID "Enables a build for Android" OFF)
|
||||
if(ANDROID)
|
||||
message("Building for Android")
|
||||
add_definitions(-DANDROID)
|
||||
endif()
|
||||
########################################
|
||||
# Dependency checking
|
||||
#
|
||||
# TODO: We should have options for dependencies included in the externals to
|
||||
# override autodetection of system libraries and force the usage of the
|
||||
# externals.
|
||||
if(NOT ANDROID)
|
||||
include(CheckLib)
|
||||
|
||||
include(CheckLib)
|
||||
|
||||
include(FindOpenGL)
|
||||
include_directories(${OPENGL_INCLUDE_DIR})
|
||||
if(NOT OPENGL_GLU_FOUND)
|
||||
message(FATAL_ERROR "GLU is required but not found")
|
||||
endif()
|
||||
|
||||
if(OPENMP)
|
||||
include(FindOpenMP OPTIONAL)
|
||||
if(OPENMP_FOUND)
|
||||
message("OpenMP parallelization enabled")
|
||||
add_definitions("${OpenMP_CXX_FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_CXX_FLAGS}")
|
||||
include(FindOpenGL)
|
||||
include_directories(${OPENGL_INCLUDE_DIR})
|
||||
if(NOT OPENGL_GLU_FOUND)
|
||||
message(FATAL_ERROR "GLU is required but not found")
|
||||
endif()
|
||||
endif()
|
||||
if(NOT OPENMP_FOUND)
|
||||
add_definitions(-Wno-unknown-pragmas)
|
||||
message("OpenMP parallelization disabled")
|
||||
endif()
|
||||
|
||||
include(FindALSA OPTIONAL)
|
||||
if(ALSA_FOUND)
|
||||
add_definitions(-DHAVE_ALSA=1)
|
||||
message("ALSA found, enabling ALSA sound backend")
|
||||
else()
|
||||
add_definitions(-DHAVE_ALSA=0)
|
||||
message("ALSA NOT found, disabling ALSA sound backend")
|
||||
endif(ALSA_FOUND)
|
||||
if(OPENMP)
|
||||
include(FindOpenMP OPTIONAL)
|
||||
if(OPENMP_FOUND)
|
||||
message("OpenMP parallelization enabled")
|
||||
add_definitions("${OpenMP_CXX_FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_CXX_FLAGS}")
|
||||
endif()
|
||||
endif()
|
||||
if(NOT OPENMP_FOUND)
|
||||
add_definitions(-Wno-unknown-pragmas)
|
||||
message("OpenMP parallelization disabled")
|
||||
endif()
|
||||
|
||||
check_lib(AO ao QUIET)
|
||||
if(AO_FOUND)
|
||||
add_definitions(-DHAVE_AO=1)
|
||||
message("ao found, enabling ao sound backend")
|
||||
else()
|
||||
add_definitions(-DHAVE_AO=0)
|
||||
message("ao NOT found, disabling ao sound backend")
|
||||
endif(AO_FOUND)
|
||||
include(FindALSA OPTIONAL)
|
||||
if(ALSA_FOUND)
|
||||
add_definitions(-DHAVE_ALSA=1)
|
||||
message("ALSA found, enabling ALSA sound backend")
|
||||
else()
|
||||
add_definitions(-DHAVE_ALSA=0)
|
||||
message("ALSA NOT found, disabling ALSA sound backend")
|
||||
endif(ALSA_FOUND)
|
||||
|
||||
check_lib(BLUEZ bluez QUIET)
|
||||
if(BLUEZ_FOUND)
|
||||
add_definitions(-DHAVE_BLUEZ=1)
|
||||
message("bluez found, enabling bluetooth support")
|
||||
else()
|
||||
add_definitions(-DHAVE_BLUEZ=0)
|
||||
message("bluez NOT found, disabling bluetooth support")
|
||||
endif(BLUEZ_FOUND)
|
||||
check_lib(AO ao QUIET)
|
||||
if(AO_FOUND)
|
||||
add_definitions(-DHAVE_AO=1)
|
||||
message("ao found, enabling ao sound backend")
|
||||
else()
|
||||
add_definitions(-DHAVE_AO=0)
|
||||
message("ao NOT found, disabling ao sound backend")
|
||||
endif(AO_FOUND)
|
||||
|
||||
check_lib(PULSEAUDIO libpulse-simple QUIET)
|
||||
if(PULSEAUDIO_FOUND)
|
||||
add_definitions(-DHAVE_PULSEAUDIO=1)
|
||||
message("PulseAudio found, enabling PulseAudio sound backend")
|
||||
else()
|
||||
add_definitions(-DHAVE_PULSEAUDIO=0)
|
||||
message("PulseAudio NOT found, disabling PulseAudio sound backend")
|
||||
endif(PULSEAUDIO_FOUND)
|
||||
check_lib(BLUEZ bluez QUIET)
|
||||
if(BLUEZ_FOUND)
|
||||
add_definitions(-DHAVE_BLUEZ=1)
|
||||
message("bluez found, enabling bluetooth support")
|
||||
else()
|
||||
add_definitions(-DHAVE_BLUEZ=0)
|
||||
message("bluez NOT found, disabling bluetooth support")
|
||||
endif(BLUEZ_FOUND)
|
||||
|
||||
include(FindOpenAL OPTIONAL)
|
||||
if(OPENAL_FOUND)
|
||||
add_definitions(-DHAVE_OPENAL=1)
|
||||
include_directories(${OPENAL_INCLUDE_DIR})
|
||||
message("OpenAL found, enabling OpenAL sound backend")
|
||||
else()
|
||||
add_definitions(-DHAVE_OPENAL=0)
|
||||
message("OpenAL NOT found, disabling OpenAL sound backend")
|
||||
endif(OPENAL_FOUND)
|
||||
check_lib(PULSEAUDIO libpulse QUIET)
|
||||
if(PULSEAUDIO_FOUND)
|
||||
add_definitions(-DHAVE_PULSEAUDIO=1)
|
||||
message("PulseAudio found, enabling PulseAudio sound backend")
|
||||
else()
|
||||
add_definitions(-DHAVE_PULSEAUDIO=0)
|
||||
message("PulseAudio NOT found, disabling PulseAudio sound backend")
|
||||
endif(PULSEAUDIO_FOUND)
|
||||
|
||||
include(FindOpenAL OPTIONAL)
|
||||
if(OPENAL_FOUND)
|
||||
add_definitions(-DHAVE_OPENAL=1)
|
||||
include_directories(${OPENAL_INCLUDE_DIR})
|
||||
message("OpenAL found, enabling OpenAL sound backend")
|
||||
else()
|
||||
add_definitions(-DHAVE_OPENAL=0)
|
||||
message("OpenAL NOT found, disabling OpenAL sound backend")
|
||||
endif(OPENAL_FOUND)
|
||||
|
||||
# Note: We do not need to explicitly check for X11 as it is done in the cmake
|
||||
# FindOpenGL module on linux.
|
||||
if(UNIX AND NOT APPLE)
|
||||
if(UNIX AND NOT APPLE)
|
||||
if(X11_FOUND)
|
||||
add_definitions(-DHAVE_X11=1)
|
||||
include_directories(${X11_INCLUDE_DIR})
|
||||
message("X11 found")
|
||||
else()
|
||||
message(FATAL_ERROR "X11 is required but not found")
|
||||
endif(X11_FOUND)
|
||||
else()
|
||||
add_definitions(-DHAVE_X11=0)
|
||||
endif()
|
||||
|
||||
if(X11_FOUND)
|
||||
add_definitions(-DHAVE_X11=1)
|
||||
include_directories(${X11_INCLUDE_DIR})
|
||||
message("X11 found")
|
||||
check_lib(XRANDR Xrandr)
|
||||
endif()
|
||||
if(XRANDR_FOUND)
|
||||
add_definitions(-DHAVE_XRANDR=1)
|
||||
else()
|
||||
message(FATAL_ERROR "X11 is required but not found")
|
||||
endif(X11_FOUND)
|
||||
else()
|
||||
add_definitions(-DHAVE_X11=0)
|
||||
endif()
|
||||
add_definitions(-DHAVE_XRANDR=0)
|
||||
endif(XRANDR_FOUND)
|
||||
|
||||
if(X11_FOUND)
|
||||
check_lib(XRANDR Xrandr)
|
||||
endif()
|
||||
if(XRANDR_FOUND)
|
||||
add_definitions(-DHAVE_XRANDR=1)
|
||||
else()
|
||||
add_definitions(-DHAVE_XRANDR=0)
|
||||
endif(XRANDR_FOUND)
|
||||
if(ENCODE_FRAMEDUMPS)
|
||||
check_libav()
|
||||
endif()
|
||||
|
||||
if(ENCODE_FRAMEDUMPS)
|
||||
check_libav()
|
||||
endif()
|
||||
|
||||
include(CheckCXXSourceRuns)
|
||||
set(CMAKE_REQUIRED_LIBRARIES portaudio)
|
||||
CHECK_CXX_SOURCE_RUNS(
|
||||
"#include <portaudio.h>
|
||||
int main(int argc, char **argv)
|
||||
{ if(Pa_GetVersion() >= 1890) return 0; else return 1; }"
|
||||
PORTAUDIO)
|
||||
if(PORTAUDIO)
|
||||
message("PortAudio found, enabling mic support")
|
||||
add_definitions(-DHAVE_PORTAUDIO=1)
|
||||
set(PORTAUDIO_FOUND TRUE)
|
||||
else()
|
||||
message("PortAudio not found, disabling mic support")
|
||||
add_definitions(-DHAVE_PORTAUDIO=0)
|
||||
set(PORTAUDIO_FOUND FALSE)
|
||||
endif(PORTAUDIO)
|
||||
|
||||
if(OPROFILING)
|
||||
check_lib(OPROFILE opagent opagent.h)
|
||||
check_lib(BFD bfd bfd.h)
|
||||
if(OPROFILE_FOUND AND BFD_FOUND)
|
||||
message("oprofile found, enabling profiling support")
|
||||
add_definitions(-DUSE_OPROFILE=1)
|
||||
include(CheckCXXSourceRuns)
|
||||
set(CMAKE_REQUIRED_LIBRARIES portaudio)
|
||||
CHECK_CXX_SOURCE_RUNS(
|
||||
"#include <portaudio.h>
|
||||
int main(int argc, char **argv)
|
||||
{ if(Pa_GetVersion() >= 1890) return 0; else return 1; }"
|
||||
PORTAUDIO)
|
||||
if(PORTAUDIO)
|
||||
message("PortAudio found, enabling mic support")
|
||||
add_definitions(-DHAVE_PORTAUDIO=1)
|
||||
set(PORTAUDIO_FOUND TRUE)
|
||||
else()
|
||||
message(FATAL_ERROR "oprofile or bfd not found. Can't build profiling support.")
|
||||
message("PortAudio not found, disabling mic support")
|
||||
add_definitions(-DHAVE_PORTAUDIO=0)
|
||||
set(PORTAUDIO_FOUND FALSE)
|
||||
endif(PORTAUDIO)
|
||||
|
||||
option(OPROFILING "Enable profiling" OFF)
|
||||
if(OPROFILING)
|
||||
check_lib(OPROFILE opagent opagent.h)
|
||||
check_lib(BFD bfd bfd.h)
|
||||
if(OPROFILE_FOUND AND BFD_FOUND)
|
||||
message("oprofile found, enabling profiling support")
|
||||
add_definitions(-DUSE_OPROFILE=1)
|
||||
else()
|
||||
message(FATAL_ERROR "oprofile or bfd not found. Can't build profiling support.")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
########################################
|
||||
# Setup include directories (and make sure they are preferred over the Externals)
|
||||
#
|
||||
|
@ -401,7 +420,6 @@ include_directories(Source/Core/InputCommon/Src)
|
|||
include_directories(Source/Core/VideoCommon/Src)
|
||||
include_directories(Source/Core/VideoUICommon/Src)
|
||||
|
||||
|
||||
########################################
|
||||
# Process externals and setup their include directories
|
||||
#
|
||||
|
@ -415,7 +433,7 @@ include_directories(Source/Core/VideoUICommon/Src)
|
|||
add_subdirectory(Externals/Bochs_disasm)
|
||||
include_directories(Externals/Bochs_disasm)
|
||||
|
||||
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND NOT ANDROID)
|
||||
check_lib(LZO lzo2 lzo/lzo1x.h QUIET)
|
||||
endif()
|
||||
if(LZO_FOUND)
|
||||
|
@ -440,15 +458,16 @@ if(OPENAL_FOUND)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
include(FindSDL2 OPTIONAL)
|
||||
endif()
|
||||
if(SDL2_FOUND)
|
||||
message("Using shared SDL2")
|
||||
include_directories(${SDL2_INCLUDE_DIR})
|
||||
else(SDL2_FOUND)
|
||||
# SDL2 not found, try SDL
|
||||
if(NOT ANDROID)
|
||||
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
include(FindSDL2 OPTIONAL)
|
||||
endif()
|
||||
if(SDL2_FOUND)
|
||||
message("Using shared SDL2")
|
||||
include_directories(${SDL2_INCLUDE_DIR})
|
||||
else(SDL2_FOUND)
|
||||
# SDL2 not found, try SDL
|
||||
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
include(FindSDL OPTIONAL)
|
||||
endif()
|
||||
if(SDL_FOUND)
|
||||
|
@ -460,12 +479,13 @@ else(SDL2_FOUND)
|
|||
include_directories(Externals/SDL/SDL Externals/SDL Externals/SDL/include)
|
||||
add_subdirectory(Externals/SDL)
|
||||
endif(SDL_FOUND)
|
||||
endif(SDL2_FOUND)
|
||||
endif(SDL2_FOUND)
|
||||
endif()
|
||||
|
||||
set(SFML_FIND_VERSION TRUE)
|
||||
set(SFML_FIND_VERSION_MAJOR 1)
|
||||
set(SFML_FIND_VERSION_MINOR 5)
|
||||
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND NOT ANDROID)
|
||||
include(FindSFML OPTIONAL)
|
||||
endif()
|
||||
if(SFML_FOUND AND NOT SFML_VERSION_MAJOR) # SFML 1.x doesn't define SFML_VERSION_MAJOR
|
||||
|
@ -476,7 +496,7 @@ else()
|
|||
include_directories(Externals/SFML/include)
|
||||
endif()
|
||||
|
||||
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND NOT ANDROID)
|
||||
check_lib(SOIL SOIL SOIL/SOIL.h QUIET)
|
||||
endif()
|
||||
if(SOIL_FOUND)
|
||||
|
@ -506,26 +526,19 @@ if(WIN32)
|
|||
find_library(GLEW glew32s PATHS Externals/GLew)
|
||||
include_directories(Externals/GLew/include)
|
||||
else()
|
||||
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
check_lib(GLEW GLEW GL/glew.h)
|
||||
if(NOT ANDROID)
|
||||
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
check_lib(GLEW GLEW GL/glew.h)
|
||||
endif()
|
||||
if(NOT GLEW_FOUND)
|
||||
message("Using static GLEW from Externals")
|
||||
add_subdirectory(Externals/GLew)
|
||||
include_directories(Externals/GLew/include)
|
||||
endif(NOT GLEW_FOUND)
|
||||
endif()
|
||||
if(NOT GLEW_FOUND)
|
||||
message("Using static GLEW from Externals")
|
||||
add_subdirectory(Externals/GLew)
|
||||
include_directories(Externals/GLew/include)
|
||||
endif(NOT GLEW_FOUND)
|
||||
|
||||
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
check_lib(CG Cg Cg/cg.h)
|
||||
endif()
|
||||
if(NOT CG_FOUND)
|
||||
message("Using static Cg from Externals")
|
||||
include_directories(Externals)
|
||||
endif(NOT CG_FOUND)
|
||||
check_lib(CGGL CgGL Cg/cgGL.h)
|
||||
endif()
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND NOT ANDROID)
|
||||
find_library(CL OpenCL)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-weak_framework,OpenCL")
|
||||
else()
|
||||
|
@ -533,10 +546,25 @@ else()
|
|||
add_subdirectory(Externals/CLRun)
|
||||
endif()
|
||||
|
||||
if(NOT DISABLE_WX)
|
||||
if(NOT DISABLE_WX AND NOT ANDROID)
|
||||
include(FindwxWidgets OPTIONAL)
|
||||
FIND_PACKAGE(wxWidgets COMPONENTS core aui adv)
|
||||
|
||||
if(wxWidgets_FOUND)
|
||||
EXECUTE_PROCESS(
|
||||
COMMAND sh "${wxWidgets_CONFIG_EXECUTABLE}"
|
||||
${wxWidgets_CONFIG_OPTIONS} --version
|
||||
OUTPUT_VARIABLE wxWidgets_VERSION
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_QUIET
|
||||
)
|
||||
message("Found wxWidgets version ${wxWidgets_VERSION}")
|
||||
if(${wxWidgets_VERSION} VERSION_LESS "2.8.9")
|
||||
message("At least 2.8.9 is required; ignoring found version")
|
||||
unset(wxWidgets_FOUND)
|
||||
endif()
|
||||
endif(wxWidgets_FOUND)
|
||||
|
||||
if(wxWidgets_FOUND)
|
||||
EXECUTE_PROCESS(
|
||||
COMMAND sh "${wxWidgets_CONFIG_EXECUTABLE}"
|
||||
|
@ -557,26 +585,26 @@ if(NOT DISABLE_WX)
|
|||
endif()
|
||||
endif(wxWidgets_FOUND)
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
# There is a bug in the FindGTK module in cmake version 2.8.2 that
|
||||
# does not find gdk-pixbuf-2.0. On the other hand some 2.8.3
|
||||
# users have complained that pkg-config does not find
|
||||
# gdk-pixbuf-2.0. On yet another hand, cmake version 2.8.3 in
|
||||
# Ubuntu Natty does not find the glib libraries correctly.
|
||||
# Ugly!!!
|
||||
execute_process(COMMAND lsb_release -c -s
|
||||
OUTPUT_VARIABLE DIST_NAME
|
||||
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}
|
||||
VERSION_EQUAL 2.8.2 OR "${DIST_NAME}" STREQUAL "natty")
|
||||
check_lib(GTK2 gtk+-2.0 gtk.h REQUIRED)
|
||||
else()
|
||||
include(FindGTK2)
|
||||
if(GTK2_FOUND)
|
||||
include_directories(${GTK2_INCLUDE_DIRS})
|
||||
if(UNIX AND NOT APPLE)
|
||||
# There is a bug in the FindGTK module in cmake version 2.8.2 that
|
||||
# does not find gdk-pixbuf-2.0. On the other hand some 2.8.3
|
||||
# users have complained that pkg-config does not find
|
||||
# gdk-pixbuf-2.0. On yet another hand, cmake version 2.8.3 in
|
||||
# Ubuntu Natty does not find the glib libraries correctly.
|
||||
# Ugly!!!
|
||||
execute_process(COMMAND lsb_release -c -s
|
||||
OUTPUT_VARIABLE DIST_NAME
|
||||
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}
|
||||
VERSION_EQUAL 2.8.2 OR "${DIST_NAME}" STREQUAL "natty")
|
||||
check_lib(GTK2 gtk+-2.0 gtk.h REQUIRED)
|
||||
else()
|
||||
include(FindGTK2)
|
||||
if(GTK2_FOUND)
|
||||
include_directories(${GTK2_INCLUDE_DIRS})
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(wxWidgets_FOUND)
|
||||
include(${wxWidgets_USE_FILE})
|
||||
|
@ -608,7 +636,7 @@ if(NOT DISABLE_WX)
|
|||
set(wxWidgets_LIBRARIES "wx")
|
||||
endif(wxWidgets_FOUND)
|
||||
add_definitions(-DHAVE_WX=1)
|
||||
endif(NOT DISABLE_WX)
|
||||
endif(NOT DISABLE_WX AND NOT ANDROID)
|
||||
|
||||
|
||||
########################################
|
||||
|
@ -682,4 +710,4 @@ list(APPEND CPACK_SOURCE_IGNORE_FILES "${CMAKE_BINARY_DIR}")
|
|||
|
||||
# CPack must be included after the CPACK_* variables are set in order for those
|
||||
# variables to take effect.
|
||||
include(CPack)
|
||||
Include(CPack)
|
||||
|
|
|
@ -6,6 +6,11 @@ set(SRCS Src/AudioCommon.cpp
|
|||
|
||||
set(LIBS "")
|
||||
|
||||
if(ANDROID)
|
||||
set(SRCS ${SRCS} Src/OpenSLESStream.cpp)
|
||||
set(LIBS ${LIBS} OpenSLES)
|
||||
endif(ANDROID)
|
||||
|
||||
if(ALSA_FOUND)
|
||||
set(SRCS ${SRCS} Src/AlsaSoundStream.cpp)
|
||||
set(LIBS ${LIBS} ${ALSA_LIBRARIES})
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "CoreAudioSoundStream.h"
|
||||
#include "OpenALStream.h"
|
||||
#include "PulseAudioStream.h"
|
||||
#include "OpenSLESStream.h"
|
||||
#include "../../Core/Src/Movie.h"
|
||||
#include "../../Core/Src/ConfigManager.h"
|
||||
|
||||
|
@ -55,7 +56,8 @@ namespace AudioCommon
|
|||
soundStream = new CoreAudioSound(mixer);
|
||||
else if (backend == BACKEND_PULSEAUDIO && PulseAudio::isValid())
|
||||
soundStream = new PulseAudio(mixer);
|
||||
|
||||
else if (backend == BACKEND_OPENSLES && OpenSLESStream::isValid())
|
||||
soundStream = new OpenSLESStream(mixer);
|
||||
if (soundStream != NULL)
|
||||
{
|
||||
UpdateSoundStream();
|
||||
|
@ -116,7 +118,8 @@ namespace AudioCommon
|
|||
backends.push_back(BACKEND_PULSEAUDIO);
|
||||
if (OpenALStream::isValid())
|
||||
backends.push_back(BACKEND_OPENAL);
|
||||
|
||||
if (OpenSLESStream::isValid())
|
||||
backends.push_back(BACKEND_OPENSLES);
|
||||
return backends;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifdef ANDROID
|
||||
#include "Common.h"
|
||||
#include <assert.h>
|
||||
#include "OpenSLESStream.h"
|
||||
|
||||
#include <SLES/OpenSLES.h>
|
||||
#include <SLES/OpenSLES_Android.h>
|
||||
|
||||
// engine interfaces
|
||||
static SLObjectItf engineObject;
|
||||
static SLEngineItf engineEngine;
|
||||
static SLObjectItf outputMixObject;
|
||||
|
||||
// buffer queue player interfaces
|
||||
static SLObjectItf bqPlayerObject = NULL;
|
||||
static SLPlayItf bqPlayerPlay;
|
||||
static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;
|
||||
static SLMuteSoloItf bqPlayerMuteSolo;
|
||||
static SLVolumeItf bqPlayerVolume;
|
||||
static CMixer *g_mixer;
|
||||
#define BUFFER_SIZE 512
|
||||
#define BUFFER_SIZE_IN_SAMPLES (BUFFER_SIZE / 2)
|
||||
|
||||
// Double buffering.
|
||||
static short buffer[2][BUFFER_SIZE];
|
||||
static int curBuffer = 0;
|
||||
|
||||
static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) {
|
||||
assert(bq == bqPlayerBufferQueue);
|
||||
assert(NULL == context);
|
||||
|
||||
short *nextBuffer = buffer[curBuffer];
|
||||
int nextSize = sizeof(buffer[0]);
|
||||
|
||||
SLresult result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, nextBuffer, nextSize);
|
||||
|
||||
// Comment from sample code:
|
||||
// the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT,
|
||||
// which for this code example would indicate a programming error
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
|
||||
curBuffer ^= 1; // Switch buffer
|
||||
// Render to the fresh buffer
|
||||
g_mixer->Mix(reinterpret_cast<short *>(buffer[curBuffer]), BUFFER_SIZE_IN_SAMPLES);
|
||||
}
|
||||
bool OpenSLESStream::Start()
|
||||
{
|
||||
SLresult result;
|
||||
// create engine
|
||||
result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, 0, 0);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
|
||||
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
|
||||
SLDataFormat_PCM format_pcm = {
|
||||
SL_DATAFORMAT_PCM,
|
||||
2,
|
||||
SL_SAMPLINGRATE_44_1,
|
||||
SL_PCMSAMPLEFORMAT_FIXED_16,
|
||||
SL_PCMSAMPLEFORMAT_FIXED_16,
|
||||
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
|
||||
SL_BYTEORDER_LITTLEENDIAN
|
||||
};
|
||||
|
||||
SLDataSource audioSrc = {&loc_bufq, &format_pcm};
|
||||
|
||||
// configure audio sink
|
||||
SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
|
||||
SLDataSink audioSnk = {&loc_outmix, NULL};
|
||||
|
||||
// create audio player
|
||||
const SLInterfaceID ids[2] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME};
|
||||
const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
|
||||
result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk, 2, ids, req);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
|
||||
result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE,
|
||||
&bqPlayerBufferQueue);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, NULL);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
|
||||
// Render and enqueue a first buffer. (or should we just play the buffer empty?)
|
||||
curBuffer = 0;
|
||||
|
||||
result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer[curBuffer], sizeof(buffer[curBuffer]));
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
return false;
|
||||
}
|
||||
curBuffer ^= 1;
|
||||
g_mixer = m_mixer;
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenSLESStream::Stop()
|
||||
{
|
||||
if (bqPlayerObject != NULL) {
|
||||
(*bqPlayerObject)->Destroy(bqPlayerObject);
|
||||
bqPlayerObject = NULL;
|
||||
bqPlayerPlay = NULL;
|
||||
bqPlayerBufferQueue = NULL;
|
||||
bqPlayerMuteSolo = NULL;
|
||||
bqPlayerVolume = NULL;
|
||||
}
|
||||
if (outputMixObject != NULL) {
|
||||
(*outputMixObject)->Destroy(outputMixObject);
|
||||
outputMixObject = NULL;
|
||||
}
|
||||
if (engineObject != NULL) {
|
||||
(*engineObject)->Destroy(engineObject);
|
||||
engineObject = NULL;
|
||||
engineEngine = NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,48 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _OPENSLSTREAM_H_
|
||||
#define _OPENSLSTREAM_H_
|
||||
|
||||
#include "Thread.h"
|
||||
#include "SoundStream.h"
|
||||
|
||||
class OpenSLESStream : public SoundStream
|
||||
{
|
||||
#ifdef ANDROID
|
||||
public:
|
||||
OpenSLESStream(CMixer *mixer, void *hWnd = NULL)
|
||||
: SoundStream(mixer)
|
||||
{};
|
||||
|
||||
virtual ~OpenSLESStream() {};
|
||||
|
||||
virtual bool Start();
|
||||
virtual void Stop();
|
||||
static bool isValid() { return true; }
|
||||
virtual bool usesMixer() const { return true; }
|
||||
|
||||
private:
|
||||
std::thread thread;
|
||||
Common::Event soundSyncEvent;
|
||||
#else
|
||||
public:
|
||||
OpenSLESStream(CMixer *mixer, void *hWnd = NULL): SoundStream(mixer) {}
|
||||
#endif // HAVE_OPENSL
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,9 +1,7 @@
|
|||
set(SRCS Src/ABI.cpp
|
||||
Src/BreakPoints.cpp
|
||||
set(SRCS Src/BreakPoints.cpp
|
||||
Src/CDUtils.cpp
|
||||
Src/ColorUtil.cpp
|
||||
Src/ConsoleListener.cpp
|
||||
Src/CPUDetect.cpp
|
||||
Src/FileSearch.cpp
|
||||
Src/FileUtil.cpp
|
||||
Src/Hash.cpp
|
||||
|
@ -20,10 +18,10 @@ set(SRCS Src/ABI.cpp
|
|||
Src/SymbolDB.cpp
|
||||
Src/SysConf.cpp
|
||||
Src/Thread.cpp
|
||||
Src/Thunk.cpp
|
||||
Src/Timer.cpp
|
||||
Src/Version.cpp
|
||||
Src/VideoBackendBase.cpp
|
||||
Src/x64ABI.cpp
|
||||
Src/x64Analyzer.cpp
|
||||
Src/x64Emitter.cpp
|
||||
Src/Crypto/aes_cbc.cpp
|
||||
|
@ -33,6 +31,23 @@ set(SRCS Src/ABI.cpp
|
|||
Src/Crypto/md5.cpp
|
||||
Src/Crypto/sha1.cpp)
|
||||
|
||||
if(_M_ARM) #ARM
|
||||
set(SRCS ${SRCS}
|
||||
Src/ArmCPUDetect.cpp
|
||||
Src/ArmEmitter.cpp)
|
||||
else()
|
||||
if(NOT _M_GENERIC) #X86
|
||||
set(SRCS ${SRCS}
|
||||
Src/x64FPURoundMode.cpp
|
||||
Src/x64Thunk.cpp
|
||||
)
|
||||
endif()
|
||||
set(SRCS ${SRCS} Src/x64CPUDetect.cpp)
|
||||
endif()
|
||||
if(_M_GENERIC) #Generic
|
||||
set(SRCS ${SRCS}
|
||||
Src/GenericFPURoundMode.cpp)
|
||||
endif()
|
||||
if(WIN32)
|
||||
set(SRCS ${SRCS} Src/ExtendedTrace.cpp)
|
||||
endif(WIN32)
|
||||
|
|
|
@ -158,12 +158,10 @@
|
|||
<Lib />
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Src\ABI.cpp" />
|
||||
<ClCompile Include="Src\BreakPoints.cpp" />
|
||||
<ClCompile Include="Src\CDUtils.cpp" />
|
||||
<ClCompile Include="Src\ColorUtil.cpp" />
|
||||
<ClCompile Include="Src\ConsoleListener.cpp" />
|
||||
<ClCompile Include="Src\CPUDetect.cpp" />
|
||||
<ClCompile Include="Src\Crypto\aes_cbc.cpp" />
|
||||
<ClCompile Include="Src\Crypto\aes_core.cpp" />
|
||||
<ClCompile Include="Src\Crypto\bn.cpp" />
|
||||
|
@ -195,15 +193,17 @@
|
|||
<ClCompile Include="Src\SymbolDB.cpp" />
|
||||
<ClCompile Include="Src\SysConf.cpp" />
|
||||
<ClCompile Include="Src\Thread.cpp" />
|
||||
<ClCompile Include="Src\Thunk.cpp" />
|
||||
<ClCompile Include="Src\Timer.cpp" />
|
||||
<ClCompile Include="Src\Version.cpp" />
|
||||
<ClCompile Include="Src\VideoBackendBase.cpp" />
|
||||
<ClCompile Include="Src\x64ABI.cpp" />
|
||||
<ClCompile Include="Src\x64Analyzer.cpp" />
|
||||
<ClCompile Include="Src\x64CPUDetect.cpp" />
|
||||
<ClCompile Include="Src\x64Emitter.cpp" />
|
||||
<ClCompile Include="Src\x64FPURoundMode.cpp" />
|
||||
<ClCompile Include="Src\x64Thunk.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Src\ABI.h" />
|
||||
<ClInclude Include="Src\Atomic.h" />
|
||||
<ClInclude Include="Src\Atomic_GCC.h" />
|
||||
<ClInclude Include="Src\Atomic_Win32.h" />
|
||||
|
@ -251,6 +251,7 @@
|
|||
<ClInclude Include="Src\Thunk.h" />
|
||||
<ClInclude Include="Src\Timer.h" />
|
||||
<ClInclude Include="Src\VideoBackendBase.h" />
|
||||
<ClInclude Include="Src\x64ABI.h" />
|
||||
<ClInclude Include="Src\x64Analyzer.h" />
|
||||
<ClInclude Include="Src\x64Emitter.h" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Src\ABI.cpp" />
|
||||
<ClCompile Include="Src\BreakPoints.cpp" />
|
||||
<ClCompile Include="Src\CDUtils.cpp" />
|
||||
<ClCompile Include="Src\ColorUtil.cpp" />
|
||||
<ClCompile Include="Src\CPUDetect.cpp" />
|
||||
<ClCompile Include="Src\ExtendedTrace.cpp" />
|
||||
<ClCompile Include="Src\FileSearch.cpp" />
|
||||
<ClCompile Include="Src\FileUtil.cpp" />
|
||||
|
@ -23,7 +21,6 @@
|
|||
<ClCompile Include="Src\SymbolDB.cpp" />
|
||||
<ClCompile Include="Src\SysConf.cpp" />
|
||||
<ClCompile Include="Src\Thread.cpp" />
|
||||
<ClCompile Include="Src\Thunk.cpp" />
|
||||
<ClCompile Include="Src\Timer.cpp" />
|
||||
<ClCompile Include="Src\Version.cpp" />
|
||||
<ClCompile Include="Src\VideoBackendBase.cpp" />
|
||||
|
@ -53,9 +50,12 @@
|
|||
<ClCompile Include="Src\Crypto\sha1.cpp">
|
||||
<Filter>Crypto</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Src\x64ABI.cpp" />
|
||||
<ClCompile Include="Src\x64CPUDetect.cpp" />
|
||||
<ClCompile Include="Src\x64FPURoundMode.cpp" />
|
||||
<ClCompile Include="Src\x64Thunk.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Src\ABI.h" />
|
||||
<ClInclude Include="Src\Atomic.h" />
|
||||
<ClInclude Include="Src\Atomic_GCC.h" />
|
||||
<ClInclude Include="Src\Atomic_Win32.h" />
|
||||
|
@ -121,6 +121,7 @@
|
|||
</ClInclude>
|
||||
<ClInclude Include="Src\StdMutex.h" />
|
||||
<ClInclude Include="Src\StdConditionVariable.h" />
|
||||
<ClInclude Include="Src\x64ABI.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="CMakeLists.txt" />
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "Common.h"
|
||||
#include "CPUDetect.h"
|
||||
#include "StringUtil.h"
|
||||
|
||||
const char procfile[] = "/proc/cpuinfo";
|
||||
|
||||
char *GetCPUString()
|
||||
{
|
||||
const char marker[] = "Hardware\t: ";
|
||||
char *cpu_string = 0;
|
||||
// Count the number of processor lines in /proc/cpuinfo
|
||||
char buf[1024];
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(procfile, "r");
|
||||
if (!fp)
|
||||
return 0;
|
||||
|
||||
while (fgets(buf, sizeof(buf), fp))
|
||||
{
|
||||
if (strncmp(buf, marker, sizeof(marker) - 1))
|
||||
continue;
|
||||
cpu_string = buf + sizeof(marker) - 1;
|
||||
cpu_string = strndup(cpu_string, strlen(cpu_string) - 1); // Strip the newline
|
||||
break;
|
||||
}
|
||||
return cpu_string;
|
||||
}
|
||||
bool CheckCPUFeature(const char *feature)
|
||||
{
|
||||
const char marker[] = "Features\t: ";
|
||||
char buf[1024];
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(procfile, "r");
|
||||
if (!fp)
|
||||
return 0;
|
||||
|
||||
while (fgets(buf, sizeof(buf), fp))
|
||||
{
|
||||
if (strncmp(buf, marker, sizeof(marker) - 1))
|
||||
continue;
|
||||
char *featurestring = buf + sizeof(marker) - 1;
|
||||
char *token = strtok(featurestring, " ");
|
||||
while (token != NULL)
|
||||
{
|
||||
if (strstr(token, feature))
|
||||
return true;
|
||||
token = strtok(NULL, " ");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
int GetCoreCount()
|
||||
{
|
||||
const char marker[] = "processor\t: ";
|
||||
int cores = 0;
|
||||
char buf[1024];
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(procfile, "r");
|
||||
if (!fp)
|
||||
return 0;
|
||||
|
||||
while (fgets(buf, sizeof(buf), fp))
|
||||
{
|
||||
if (strncmp(buf, marker, sizeof(marker) - 1))
|
||||
continue;
|
||||
++cores;
|
||||
}
|
||||
return cores;
|
||||
}
|
||||
|
||||
CPUInfo cpu_info;
|
||||
|
||||
CPUInfo::CPUInfo() {
|
||||
Detect();
|
||||
}
|
||||
|
||||
// Detects the various cpu features
|
||||
void CPUInfo::Detect()
|
||||
{
|
||||
// Set some defaults here
|
||||
// When ARMv8 cpus come out, these need to be updated.
|
||||
HTT = false;
|
||||
OS64bit = false;
|
||||
CPU64bit = false;
|
||||
Mode64bit = false;
|
||||
vendor = VENDOR_ARM;
|
||||
|
||||
// Get the information about the CPU
|
||||
strncpy(cpu_string, GetCPUString(), sizeof(cpu_string));
|
||||
num_cores = GetCoreCount();
|
||||
bSwp = CheckCPUFeature("swp");
|
||||
bHalf = CheckCPUFeature("half");
|
||||
bThumb = CheckCPUFeature("thumb");
|
||||
bFastMult = CheckCPUFeature("fastmult");
|
||||
bVFP = CheckCPUFeature("vfp");
|
||||
bEDSP = CheckCPUFeature("edsp");
|
||||
bThumbEE = CheckCPUFeature("thumbee");
|
||||
bNEON = CheckCPUFeature("neon");
|
||||
bVFPv3 = CheckCPUFeature("vfpv3");
|
||||
bTLS = CheckCPUFeature("tls");
|
||||
bVFPv4 = CheckCPUFeature("vfpv4");
|
||||
bIDIVa = CheckCPUFeature("idiva");
|
||||
bIDIVt = CheckCPUFeature("idivt");
|
||||
// These two are ARMv8 specific.
|
||||
bFP = CheckCPUFeature("fp");
|
||||
bASIMD = CheckCPUFeature("asimd");
|
||||
|
||||
|
||||
#if defined(__ARM_ARCH_7A__)
|
||||
bArmV7 = true;
|
||||
#else
|
||||
bArmV7 = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Turn the cpu info into a string we can show
|
||||
std::string CPUInfo::Summarize()
|
||||
{
|
||||
std::string sum;
|
||||
if (num_cores == 1)
|
||||
sum = StringFromFormat("%s, %i core", cpu_string, num_cores);
|
||||
else
|
||||
sum = StringFromFormat("%s, %i cores", cpu_string, num_cores);
|
||||
|
||||
if (bSwp) sum += ", SWP";
|
||||
if (bHalf) sum += ", Half";
|
||||
if (bThumb) sum += ", Thumb";
|
||||
if (bFastMult) sum += ", FastMult";
|
||||
if (bVFP) sum += ", VFP";
|
||||
if (bEDSP) sum += ", EDSP";
|
||||
if (bThumbEE) sum += ", ThumbEE";
|
||||
if (bNEON) sum += ", NEON";
|
||||
if (bVFPv3) sum += ", VFPv3";
|
||||
if (bTLS) sum += ", TLS";
|
||||
if (bVFPv4) sum += ", VFPv4";
|
||||
if (bIDIVa) sum += ", IDIVa";
|
||||
if (bIDIVt) sum += ", IDIVt";
|
||||
|
||||
return sum;
|
||||
}
|
|
@ -0,0 +1,967 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "Common.h"
|
||||
#include "ArmEmitter.h"
|
||||
#include "CPUDetect.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
// For cache flushing on Symbian/Blackberry
|
||||
#ifdef __SYMBIAN32__
|
||||
#include <e32std.h>
|
||||
#endif
|
||||
|
||||
#ifdef BLACKBERRY
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
namespace ArmGen
|
||||
{
|
||||
|
||||
inline u32 RotR(u32 a, int amount) {
|
||||
if (!amount) return a;
|
||||
return (a >> amount) | (a << (32 - amount));
|
||||
}
|
||||
|
||||
inline u32 RotL(u32 a, int amount) {
|
||||
if (!amount) return a;
|
||||
return (a << amount) | (a >> (32 - amount));
|
||||
}
|
||||
|
||||
bool TryMakeOperand2(u32 imm, Operand2 &op2) {
|
||||
// Just brute force it.
|
||||
for (int i = 0; i < 16; i++) {
|
||||
int mask = RotR(0xFF, i * 2);
|
||||
if ((imm & mask) == imm) {
|
||||
op2 = Operand2((u8)(RotL(imm, i * 2)), (u8)i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TryMakeOperand2_AllowInverse(u32 imm, Operand2 &op2, bool *inverse)
|
||||
{
|
||||
if (!TryMakeOperand2(imm, op2)) {
|
||||
*inverse = true;
|
||||
return TryMakeOperand2(~imm, op2);
|
||||
} else {
|
||||
*inverse = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool TryMakeOperand2_AllowNegation(s32 imm, Operand2 &op2, bool *negated)
|
||||
{
|
||||
if (!TryMakeOperand2(imm, op2)) {
|
||||
*negated = true;
|
||||
return TryMakeOperand2(-imm, op2);
|
||||
} else {
|
||||
*negated = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void ARMXEmitter::MOVI2R(ARMReg reg, u32 val, bool optimize)
|
||||
{
|
||||
Operand2 op2;
|
||||
bool inverse;
|
||||
if (!optimize)
|
||||
{
|
||||
// Only used in backpatch atm
|
||||
// Only support ARMv7 right now
|
||||
if (cpu_info.bArmV7) {
|
||||
MOVW(reg, val & 0xFFFF);
|
||||
MOVT(reg, val, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// ARMv6 version won't use backpatch for now
|
||||
// Run again with optimizations
|
||||
MOVI2R(reg, val);
|
||||
}
|
||||
} else if (TryMakeOperand2_AllowInverse(val, op2, &inverse)) {
|
||||
if (!inverse)
|
||||
MOV(reg, op2);
|
||||
else
|
||||
MVN(reg, op2);
|
||||
} else {
|
||||
if (cpu_info.bArmV7) {
|
||||
// ARMv7 - can use MOVT/MOVW, best choice
|
||||
MOVW(reg, val & 0xFFFF);
|
||||
if(val & 0xFFFF0000)
|
||||
MOVT(reg, val, true);
|
||||
} else {
|
||||
// ARMv6 - fallback sequence.
|
||||
// TODO: Optimize further. Can for example choose negation etc.
|
||||
// Literal pools is another way to do this but much more complicated
|
||||
// so I can't really be bothered for an outdated CPU architecture like ARMv6.
|
||||
bool first = true;
|
||||
int shift = 16;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (val & 0xFF) {
|
||||
if (first) {
|
||||
MOV(reg, Operand2((u8)val, (u8)(shift & 0xF)));
|
||||
first = false;
|
||||
} else {
|
||||
ORR(reg, reg, Operand2((u8)val, (u8)(shift & 0xF)));
|
||||
}
|
||||
}
|
||||
shift -= 4;
|
||||
val >>= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Moves IMM to memory location
|
||||
void ARMXEmitter::ARMABI_MOVI2M(Operand2 op, Operand2 val)
|
||||
{
|
||||
// This moves imm to a memory location
|
||||
MOVW(R14, val); MOVT(R14, val, true);
|
||||
MOVW(R12, op); MOVT(R12, op, true);
|
||||
STR(R12, R14); // R10 is what we want to store
|
||||
}
|
||||
void ARMXEmitter::QuickCallFunction(ARMReg reg, void *func) {
|
||||
MOVI2R(reg, (u32)(func));
|
||||
BL(reg);
|
||||
}
|
||||
|
||||
void ARMXEmitter::SetCodePtr(u8 *ptr)
|
||||
{
|
||||
code = ptr;
|
||||
startcode = code;
|
||||
}
|
||||
|
||||
const u8 *ARMXEmitter::GetCodePtr() const
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
u8 *ARMXEmitter::GetWritableCodePtr()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
void ARMXEmitter::ReserveCodeSpace(u32 bytes)
|
||||
{
|
||||
for (u32 i = 0; i < bytes/4; i++)
|
||||
Write32(0xE1200070); //bkpt 0
|
||||
}
|
||||
|
||||
const u8 *ARMXEmitter::AlignCode16()
|
||||
{
|
||||
ReserveCodeSpace((-(s32)code) & 15);
|
||||
return code;
|
||||
}
|
||||
|
||||
const u8 *ARMXEmitter::AlignCodePage()
|
||||
{
|
||||
ReserveCodeSpace((-(s32)code) & 4095);
|
||||
return code;
|
||||
}
|
||||
|
||||
void ARMXEmitter::FlushIcache()
|
||||
{
|
||||
FlushIcacheSection(lastCacheFlushEnd, code);
|
||||
lastCacheFlushEnd = code;
|
||||
}
|
||||
|
||||
void ARMXEmitter::FlushIcacheSection(u8 *start, u8 *end)
|
||||
{
|
||||
#ifdef __SYMBIAN32__
|
||||
User::IMB_Range( start, end);
|
||||
#elif defined(BLACKBERRY)
|
||||
msync(start, end - start, MS_SYNC | MS_INVALIDATE_ICACHE);
|
||||
#else
|
||||
#ifndef _WIN32
|
||||
#ifdef ANDROID
|
||||
__builtin___clear_cache (start, end);
|
||||
#else
|
||||
// If on Linux, we HAVE to clear from start addr or else everything gets /really/ unstable
|
||||
__builtin___clear_cache (startcode, end);
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void ARMXEmitter::SetCC(CCFlags cond)
|
||||
{
|
||||
condition = cond << 28;
|
||||
}
|
||||
|
||||
void ARMXEmitter::NOP(int count)
|
||||
{
|
||||
for (int i = 0; i < count; i++) {
|
||||
Write32(condition | 0x01A00000);
|
||||
}
|
||||
}
|
||||
|
||||
void ARMXEmitter::SETEND(bool BE)
|
||||
{
|
||||
//SETEND is non-conditional
|
||||
Write32( 0xF1010000 | (BE << 9));
|
||||
}
|
||||
void ARMXEmitter::BKPT(u16 arg)
|
||||
{
|
||||
Write32(condition | 0x01200070 | (arg << 4 & 0x000FFF00) | (arg & 0x0000000F));
|
||||
}
|
||||
void ARMXEmitter::YIELD()
|
||||
{
|
||||
Write32(condition | 0x0320F001);
|
||||
}
|
||||
|
||||
FixupBranch ARMXEmitter::B()
|
||||
{
|
||||
FixupBranch branch;
|
||||
branch.type = 0; // Zero for B
|
||||
branch.ptr = code;
|
||||
branch.condition = condition;
|
||||
//We'll write NOP here for now.
|
||||
Write32(condition | 0x01A00000);
|
||||
return branch;
|
||||
}
|
||||
FixupBranch ARMXEmitter::BL()
|
||||
{
|
||||
FixupBranch branch;
|
||||
branch.type = 1; // Zero for B
|
||||
branch.ptr = code;
|
||||
branch.condition = condition;
|
||||
//We'll write NOP here for now.
|
||||
Write32(condition | 0x01A00000);
|
||||
return branch;
|
||||
}
|
||||
|
||||
FixupBranch ARMXEmitter::B_CC(CCFlags Cond)
|
||||
{
|
||||
FixupBranch branch;
|
||||
branch.type = 0; // Zero for B
|
||||
branch.ptr = code;
|
||||
branch.condition = Cond << 28;
|
||||
//We'll write NOP here for now.
|
||||
Write32(condition | 0x01A00000);
|
||||
return branch;
|
||||
}
|
||||
void ARMXEmitter::B_CC(CCFlags Cond, const void *fnptr)
|
||||
{
|
||||
s32 distance = (s32)fnptr - (s32(code) + 8);
|
||||
_assert_msg_(DYNA_REC, distance > -33554432
|
||||
&& distance <= 33554432,
|
||||
"B_CC out of range (%p calls %p)", code, fnptr);
|
||||
|
||||
Write32((Cond << 28) | 0x0A000000 | ((distance >> 2) & 0x00FFFFFF));
|
||||
}
|
||||
FixupBranch ARMXEmitter::BL_CC(CCFlags Cond)
|
||||
{
|
||||
FixupBranch branch;
|
||||
branch.type = 1; // Zero for B
|
||||
branch.ptr = code;
|
||||
branch.condition = Cond << 28;
|
||||
//We'll write NOP here for now.
|
||||
Write32(condition | 0x01A00000);
|
||||
return branch;
|
||||
}
|
||||
void ARMXEmitter::SetJumpTarget(FixupBranch const &branch)
|
||||
{
|
||||
s32 distance = (s32(code) - 8) - (s32)branch.ptr;
|
||||
_assert_msg_(DYNA_REC, distance > -33554432
|
||||
&& distance <= 33554432,
|
||||
"SetJumpTarget out of range (%p calls %p)", code,
|
||||
branch.ptr);
|
||||
if(branch.type == 0) // B
|
||||
*(u32*)branch.ptr = (u32)(branch.condition | (10 << 24) | ((distance >> 2) &
|
||||
0x00FFFFFF));
|
||||
else // BL
|
||||
*(u32*)branch.ptr = (u32)(branch.condition | 0x0B000000 | ((distance >> 2)
|
||||
& 0x00FFFFFF));
|
||||
}
|
||||
void ARMXEmitter::B (const void *fnptr)
|
||||
{
|
||||
s32 distance = (s32)fnptr - (s32(code) + 8);
|
||||
_assert_msg_(DYNA_REC, distance > -33554432
|
||||
&& distance <= 33554432,
|
||||
"B out of range (%p calls %p)", code, fnptr);
|
||||
|
||||
Write32(condition | 0x0A000000 | ((distance >> 2) & 0x00FFFFFF));
|
||||
}
|
||||
|
||||
void ARMXEmitter::B(ARMReg src)
|
||||
{
|
||||
Write32(condition | 0x12FFF10 | src);
|
||||
}
|
||||
|
||||
void ARMXEmitter::BL(const void *fnptr)
|
||||
{
|
||||
s32 distance = (s32)fnptr - (s32(code) + 8);
|
||||
_assert_msg_(DYNA_REC, distance > -33554432
|
||||
&& distance <= 33554432,
|
||||
"BL out of range (%p calls %p)", code, fnptr);
|
||||
Write32(condition | 0x0B000000 | ((distance >> 2) & 0x00FFFFFF));
|
||||
}
|
||||
void ARMXEmitter::BL(ARMReg src)
|
||||
{
|
||||
Write32(condition | 0x12FFF30 | src);
|
||||
}
|
||||
void ARMXEmitter::PUSH(const int num, ...)
|
||||
{
|
||||
u16 RegList = 0;
|
||||
u8 Reg;
|
||||
int i;
|
||||
va_list vl;
|
||||
va_start(vl, num);
|
||||
for (i=0;i<num;i++)
|
||||
{
|
||||
Reg = va_arg(vl, u32);
|
||||
RegList |= (1 << Reg);
|
||||
}
|
||||
va_end(vl);
|
||||
Write32(condition | (2349 << 16) | RegList);
|
||||
}
|
||||
void ARMXEmitter::POP(const int num, ...)
|
||||
{
|
||||
u16 RegList = 0;
|
||||
u8 Reg;
|
||||
int i;
|
||||
va_list vl;
|
||||
va_start(vl, num);
|
||||
for (i=0;i<num;i++)
|
||||
{
|
||||
Reg = va_arg(vl, u32);
|
||||
RegList |= (1 << Reg);
|
||||
}
|
||||
va_end(vl);
|
||||
Write32(condition | (2237 << 16) | RegList);
|
||||
}
|
||||
|
||||
void ARMXEmitter::WriteShiftedDataOp(u32 op, bool SetFlags, ARMReg dest, ARMReg src, Operand2 op2)
|
||||
{
|
||||
Write32(condition | (13 << 21) | (SetFlags << 20) | (dest << 12) | op2.Imm5() | (op << 4) | src);
|
||||
}
|
||||
void ARMXEmitter::WriteShiftedDataOp(u32 op, bool SetFlags, ARMReg dest, ARMReg src, ARMReg op2)
|
||||
{
|
||||
Write32(condition | (13 << 21) | (SetFlags << 20) | (dest << 12) | (op2 << 8) | (op << 4) | src);
|
||||
}
|
||||
|
||||
// IMM, REG, IMMSREG, RSR
|
||||
// -1 for invalid if the instruction doesn't support that
|
||||
const s32 InstOps[][4] = {{16, 0, 0, 0}, // AND(s)
|
||||
{17, 1, 1, 1}, // EOR(s)
|
||||
{18, 2, 2, 2}, // SUB(s)
|
||||
{19, 3, 3, 3}, // RSB(s)
|
||||
{20, 4, 4, 4}, // ADD(s)
|
||||
{21, 5, 5, 5}, // ADC(s)
|
||||
{22, 6, 6, 6}, // SBC(s)
|
||||
{23, 7, 7, 7}, // RSC(s)
|
||||
{24, 8, 8, 8}, // TST
|
||||
{25, 9, 9, 9}, // TEQ
|
||||
{26, 10, 10, 10}, // CMP
|
||||
{27, 11, 11, 11}, // CMN
|
||||
{28, 12, 12, 12}, // ORR(s)
|
||||
{29, 13, 13, 13}, // MOV(s)
|
||||
{30, 14, 14, 14}, // BIC(s)
|
||||
{31, 15, 15, 15}, // MVN(s)
|
||||
{24, -1, -1, -1}, // MOVW
|
||||
{26, -1, -1, -1}, // MOVT
|
||||
};
|
||||
|
||||
const char *InstNames[] = { "AND",
|
||||
"EOR",
|
||||
"SUB",
|
||||
"RSB",
|
||||
"ADD",
|
||||
"ADC",
|
||||
"SBC",
|
||||
"RSC",
|
||||
"TST",
|
||||
"TEQ",
|
||||
"CMP",
|
||||
"CMN",
|
||||
"ORR",
|
||||
"MOV",
|
||||
"BIC",
|
||||
"MVN"
|
||||
};
|
||||
|
||||
void ARMXEmitter::AND (ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(0, Rd, Rn, Rm); }
|
||||
void ARMXEmitter::ANDS(ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(0, Rd, Rn, Rm, true); }
|
||||
void ARMXEmitter::EOR (ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(1, Rd, Rn, Rm); }
|
||||
void ARMXEmitter::EORS(ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(1, Rd, Rn, Rm, true); }
|
||||
void ARMXEmitter::SUB (ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(2, Rd, Rn, Rm); }
|
||||
void ARMXEmitter::SUBS(ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(2, Rd, Rn, Rm, true); }
|
||||
void ARMXEmitter::RSB (ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(3, Rd, Rn, Rm); }
|
||||
void ARMXEmitter::RSBS(ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(3, Rd, Rn, Rm, true); }
|
||||
void ARMXEmitter::ADD (ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(4, Rd, Rn, Rm); }
|
||||
void ARMXEmitter::ADDS(ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(4, Rd, Rn, Rm, true); }
|
||||
void ARMXEmitter::ADC (ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(5, Rd, Rn, Rm); }
|
||||
void ARMXEmitter::ADCS(ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(5, Rd, Rn, Rm, true); }
|
||||
void ARMXEmitter::SBC (ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(6, Rd, Rn, Rm); }
|
||||
void ARMXEmitter::SBCS(ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(6, Rd, Rn, Rm, true); }
|
||||
void ARMXEmitter::RSC (ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(7, Rd, Rn, Rm); }
|
||||
void ARMXEmitter::RSCS(ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(7, Rd, Rn, Rm, true); }
|
||||
void ARMXEmitter::TST ( ARMReg Rn, Operand2 Rm) { WriteInstruction(8, R0, Rn, Rm, true); }
|
||||
void ARMXEmitter::TEQ ( ARMReg Rn, Operand2 Rm) { WriteInstruction(9, R0, Rn, Rm, true); }
|
||||
void ARMXEmitter::CMP ( ARMReg Rn, Operand2 Rm) { WriteInstruction(10, R0, Rn, Rm, true); }
|
||||
void ARMXEmitter::CMN ( ARMReg Rn, Operand2 Rm) { WriteInstruction(11, R0, Rn, Rm, true); }
|
||||
void ARMXEmitter::ORR (ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(12, Rd, Rn, Rm); }
|
||||
void ARMXEmitter::ORRS(ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(12, Rd, Rn, Rm, true); }
|
||||
void ARMXEmitter::MOV (ARMReg Rd, Operand2 Rm) { WriteInstruction(13, Rd, R0, Rm); }
|
||||
void ARMXEmitter::MOVS(ARMReg Rd, Operand2 Rm) { WriteInstruction(13, Rd, R0, Rm, true); }
|
||||
void ARMXEmitter::BIC (ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(14, Rd, Rn, Rm); }
|
||||
void ARMXEmitter::BICS(ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(14, Rd, Rn, Rm, true); }
|
||||
void ARMXEmitter::MVN (ARMReg Rd, Operand2 Rm) { WriteInstruction(15, Rd, R0, Rm); }
|
||||
void ARMXEmitter::MVNS(ARMReg Rd, Operand2 Rm) { WriteInstruction(15, Rd, R0, Rm, true); }
|
||||
void ARMXEmitter::MOVW(ARMReg Rd, Operand2 Rm) { WriteInstruction(16, Rd, R0, Rm); }
|
||||
void ARMXEmitter::MOVT(ARMReg Rd, Operand2 Rm, bool TopBits) { WriteInstruction(17, Rd, R0, TopBits ? Rm.Value >> 16 : Rm); }
|
||||
|
||||
void ARMXEmitter::WriteInstruction (u32 Op, ARMReg Rd, ARMReg Rn, Operand2 Rm, bool SetFlags) // This can get renamed later
|
||||
{
|
||||
s32 op = InstOps[Op][Rm.GetType()]; // Type always decided by last operand
|
||||
u32 Data = Rm.GetData();
|
||||
if (Rm.GetType() == TYPE_IMM)
|
||||
{
|
||||
switch (Op)
|
||||
{
|
||||
// MOV cases that support IMM16
|
||||
case 16:
|
||||
case 17:
|
||||
Data = Rm.Imm16();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (op == -1)
|
||||
_assert_msg_(DYNA_REC, false, "%s not yet support %d", InstNames[Op], Rm.GetType());
|
||||
Write32(condition | (op << 21) | (SetFlags ? (1 << 20) : 0) | Rn << 16 | Rd << 12 | Data);
|
||||
}
|
||||
|
||||
// Data Operations
|
||||
void ARMXEmitter::WriteSignedMultiply(u32 Op, u32 Op2, u32 Op3, ARMReg dest, ARMReg r1, ARMReg r2)
|
||||
{
|
||||
Write32(condition | (0x7 << 24) | (Op << 20) | (dest << 16) | (Op2 << 12) | (r1 << 8) | (Op3 << 5) | (1 << 4) | r2);
|
||||
}
|
||||
void ARMXEmitter::UDIV(ARMReg dest, ARMReg dividend, ARMReg divisor)
|
||||
{
|
||||
if (!cpu_info.bIDIVa)
|
||||
PanicAlert("Trying to use integer divide on hardware that doesn't support it. Bad programmer.");
|
||||
WriteSignedMultiply(3, 0xF, 0, dest, divisor, dividend);
|
||||
}
|
||||
void ARMXEmitter::SDIV(ARMReg dest, ARMReg dividend, ARMReg divisor)
|
||||
{
|
||||
if (!cpu_info.bIDIVa)
|
||||
PanicAlert("Trying to use integer divide on hardware that doesn't support it. Bad programmer.");
|
||||
WriteSignedMultiply(1, 0xF, 0, dest, divisor, dividend);
|
||||
}
|
||||
void ARMXEmitter::LSL (ARMReg dest, ARMReg src, Operand2 op2) { WriteShiftedDataOp(0, false, dest, src, op2);}
|
||||
void ARMXEmitter::LSLS(ARMReg dest, ARMReg src, Operand2 op2) { WriteShiftedDataOp(0, true, dest, src, op2);}
|
||||
void ARMXEmitter::LSL (ARMReg dest, ARMReg src, ARMReg op2) { WriteShiftedDataOp(1, false, dest, src, op2);}
|
||||
void ARMXEmitter::LSLS(ARMReg dest, ARMReg src, ARMReg op2) { WriteShiftedDataOp(1, true, dest, src, op2);}
|
||||
void ARMXEmitter::MUL (ARMReg dest, ARMReg src, ARMReg op2)
|
||||
{
|
||||
Write32(condition | (dest << 16) | (src << 8) | (9 << 4) | op2);
|
||||
}
|
||||
void ARMXEmitter::MULS(ARMReg dest, ARMReg src, ARMReg op2)
|
||||
{
|
||||
Write32(condition | (1 << 20) | (dest << 16) | (src << 8) | (9 << 4) | op2);
|
||||
}
|
||||
|
||||
void ARMXEmitter::Write4OpMultiply(u32 op, ARMReg destLo, ARMReg destHi, ARMReg rm, ARMReg rn) {
|
||||
Write32(condition | (op << 20) | (destHi << 16) | (destLo << 12) | (rm << 8) | (9 << 4) | rn);
|
||||
}
|
||||
|
||||
void ARMXEmitter::UMULL(ARMReg destLo, ARMReg destHi, ARMReg rm, ARMReg rn)
|
||||
{
|
||||
Write4OpMultiply(0x8, destLo, destHi, rn, rm);
|
||||
}
|
||||
|
||||
void ARMXEmitter::SMULL(ARMReg destLo, ARMReg destHi, ARMReg rm, ARMReg rn)
|
||||
{
|
||||
Write4OpMultiply(0xC, destLo, destHi, rn, rm);
|
||||
}
|
||||
void ARMXEmitter::SXTB (ARMReg dest, ARMReg op2)
|
||||
{
|
||||
Write32(condition | (0x6AF << 16) | (dest << 12) | (7 << 4) | op2);
|
||||
}
|
||||
void ARMXEmitter::SXTH (ARMReg dest, ARMReg op2, u8 rotation)
|
||||
{
|
||||
SXTAH(dest, (ARMReg)15, op2, rotation);
|
||||
}
|
||||
void ARMXEmitter::SXTAH(ARMReg dest, ARMReg src, ARMReg op2, u8 rotation)
|
||||
{
|
||||
// bits ten and 11 are the rotation amount, see 8.8.232 for more
|
||||
// information
|
||||
Write32(condition | (0x6B << 20) | (src << 16) | (dest << 12) | (rotation << 10) | (7 << 4) | op2);
|
||||
}
|
||||
void ARMXEmitter::REV (ARMReg dest, ARMReg src )
|
||||
{
|
||||
Write32(condition | (107 << 20) | (15 << 16) | (dest << 12) | (243 << 4) | src);
|
||||
}
|
||||
void ARMXEmitter::REV16(ARMReg dest, ARMReg src)
|
||||
{
|
||||
Write32(condition | (0x3DF << 16) | (dest << 12) | (0xFD << 4) | src);
|
||||
}
|
||||
|
||||
void ARMXEmitter::_MSR (bool write_nzcvq, bool write_g, Operand2 op2)
|
||||
{
|
||||
Write32(condition | (0x320F << 12) | (write_nzcvq << 19) | (write_g << 18) | op2.Imm12Mod());
|
||||
}
|
||||
void ARMXEmitter::_MSR (bool write_nzcvq, bool write_g, ARMReg src)
|
||||
{
|
||||
Write32(condition | (0x120F << 12) | (write_nzcvq << 19) | (write_g << 18) | src);
|
||||
}
|
||||
void ARMXEmitter::MRS (ARMReg dest)
|
||||
{
|
||||
Write32(condition | (16 << 20) | (15 << 16) | (dest << 12));
|
||||
}
|
||||
void ARMXEmitter::WriteStoreOp(u32 op, ARMReg dest, ARMReg src, Operand2 op2)
|
||||
{
|
||||
if (op2.GetData() == 0) // Don't index
|
||||
Write32(condition | 0x01800000 | (op << 20) | (dest << 16) | (src << 12) | op2.Imm12());
|
||||
else
|
||||
Write32(condition | (op << 20) | (3 << 23) | (dest << 16) | (src << 12) | op2.Imm12());
|
||||
}
|
||||
void ARMXEmitter::STR (ARMReg dest, ARMReg src, Operand2 op) { WriteStoreOp(0x40, dest, src, op);}
|
||||
void ARMXEmitter::STRB(ARMReg dest, ARMReg src, Operand2 op) { WriteStoreOp(0x44, dest, src, op);}
|
||||
void ARMXEmitter::STR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add)
|
||||
{
|
||||
Write32(condition | (0x60 << 20) | (Index << 24) | (Add << 23) | (dest << 16) | (base << 12) | offset);
|
||||
}
|
||||
void ARMXEmitter::LDREX(ARMReg dest, ARMReg base)
|
||||
{
|
||||
Write32(condition | (25 << 20) | (base << 16) | (dest << 12) | 0xF9F);
|
||||
}
|
||||
void ARMXEmitter::STREX(ARMReg dest, ARMReg base, ARMReg op)
|
||||
{
|
||||
_assert_msg_(DYNA_REC, (dest != base && dest != op), "STREX dest can't be other two registers");
|
||||
Write32(condition | (24 << 20) | (base << 16) | (dest << 12) | (0xF9 << 4) | op);
|
||||
}
|
||||
void ARMXEmitter::DMB ()
|
||||
{
|
||||
Write32(0xF57FF05E);
|
||||
}
|
||||
void ARMXEmitter::SVC(Operand2 op)
|
||||
{
|
||||
Write32(condition | (0x0F << 24) | op.Imm24());
|
||||
}
|
||||
|
||||
void ARMXEmitter::LDR (ARMReg dest, ARMReg src, Operand2 op) { WriteStoreOp(0x41, src, dest, op);}
|
||||
void ARMXEmitter::LDRH(ARMReg dest, ARMReg src, Operand2 op)
|
||||
{
|
||||
u8 Imm = op.Imm8();
|
||||
Write32(condition | (0x05 << 20) | (src << 16) | (dest << 12) | ((Imm >> 4) << 8) | (0xB << 4) | (Imm & 0x0F));
|
||||
}
|
||||
void ARMXEmitter::LDRB(ARMReg dest, ARMReg src, Operand2 op) { WriteStoreOp(0x45, src, dest, op);}
|
||||
|
||||
void ARMXEmitter::LDR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add)
|
||||
{
|
||||
Write32(condition | (0x61 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (dest << 12) | offset);
|
||||
}
|
||||
void ARMXEmitter::WriteRegStoreOp(u32 op, ARMReg dest, bool WriteBack, u16 RegList)
|
||||
{
|
||||
Write32(condition | (op << 20) | (WriteBack << 21) | (dest << 16) | RegList);
|
||||
}
|
||||
void ARMXEmitter::STMFD(ARMReg dest, bool WriteBack, const int Regnum, ...)
|
||||
{
|
||||
u16 RegList = 0;
|
||||
u8 Reg;
|
||||
int i;
|
||||
va_list vl;
|
||||
va_start(vl, Regnum);
|
||||
for (i=0;i<Regnum;i++)
|
||||
{
|
||||
Reg = va_arg(vl, u32);
|
||||
RegList |= (1 << Reg);
|
||||
}
|
||||
va_end(vl);
|
||||
WriteRegStoreOp(0x90, dest, WriteBack, RegList);
|
||||
}
|
||||
void ARMXEmitter::LDMFD(ARMReg dest, bool WriteBack, const int Regnum, ...)
|
||||
{
|
||||
u16 RegList = 0;
|
||||
u8 Reg;
|
||||
int i;
|
||||
va_list vl;
|
||||
va_start(vl, Regnum);
|
||||
for (i=0;i<Regnum;i++)
|
||||
{
|
||||
Reg = va_arg(vl, u32);
|
||||
RegList |= (1 << Reg);
|
||||
}
|
||||
va_end(vl);
|
||||
WriteRegStoreOp(0x89, dest, WriteBack, RegList);
|
||||
}
|
||||
|
||||
ARMReg ARMXEmitter::SubBase(ARMReg Reg)
|
||||
{
|
||||
if (Reg >= S0)
|
||||
{
|
||||
if (Reg >= D0)
|
||||
{
|
||||
if (Reg >= Q0)
|
||||
return (ARMReg)((Reg - Q0) * 2); // Always gets encoded as a double register
|
||||
return (ARMReg)(Reg - D0);
|
||||
}
|
||||
return (ARMReg)(Reg - S0);
|
||||
}
|
||||
return Reg;
|
||||
}
|
||||
// NEON Specific
|
||||
void ARMXEmitter::VADD(IntegerSize Size, ARMReg Vd, ARMReg Vn, ARMReg Vm)
|
||||
{
|
||||
_assert_msg_(DYNA_REC, Vd >= D0, "Pass invalid register to VADD(integer)");
|
||||
_assert_msg_(DYNA_REC, cpu_info.bNEON, "Can't use VADD(integer) when CPU doesn't support it");
|
||||
|
||||
bool register_quad = Vd >= Q0;
|
||||
|
||||
// Gets encoded as a double register
|
||||
Vd = SubBase(Vd);
|
||||
Vn = SubBase(Vn);
|
||||
Vm = SubBase(Vm);
|
||||
|
||||
Write32((0xF2 << 24) | ((Vd & 0x10) << 18) | (Size << 20) | ((Vn & 0xF) << 16) \
|
||||
| ((Vd & 0xF) << 12) | (0x8 << 8) | ((Vn & 0x10) << 3) | (register_quad << 6) \
|
||||
| ((Vm & 0x10) << 2) | (Vm & 0xF));
|
||||
|
||||
}
|
||||
void ARMXEmitter::VSUB(IntegerSize Size, ARMReg Vd, ARMReg Vn, ARMReg Vm)
|
||||
{
|
||||
_assert_msg_(DYNA_REC, Vd >= Q0, "Pass invalid register to VSUB(integer)");
|
||||
_assert_msg_(DYNA_REC, cpu_info.bNEON, "Can't use VSUB(integer) when CPU doesn't support it");
|
||||
|
||||
// Gets encoded as a double register
|
||||
Vd = SubBase(Vd);
|
||||
Vn = SubBase(Vn);
|
||||
Vm = SubBase(Vm);
|
||||
|
||||
Write32((0xF3 << 24) | ((Vd & 0x10) << 18) | (Size << 20) | ((Vn & 0xF) << 16) \
|
||||
| ((Vd & 0xF) << 12) | (0x8 << 8) | ((Vn & 0x10) << 3) | (1 << 6) \
|
||||
| ((Vm & 0x10) << 2) | (Vm & 0xF));
|
||||
|
||||
}
|
||||
|
||||
// VFP Specific
|
||||
|
||||
void ARMXEmitter::VLDR(ARMReg Dest, ARMReg Base, u16 op)
|
||||
{
|
||||
_assert_msg_(DYNA_REC, Dest >= S0 && Dest <= D31, "Passed Invalid dest register to VLDR");
|
||||
_assert_msg_(DYNA_REC, Base <= R15, "Passed invalid Base register to VLDR");
|
||||
_assert_msg_(DYNA_REC, !(op & 3), "Offset needs to be word aligned");
|
||||
bool single_reg = Dest < D0;
|
||||
|
||||
Dest = SubBase(Dest);
|
||||
|
||||
if (single_reg)
|
||||
{
|
||||
Write32(NO_COND | (0x1B << 23) | ((Dest & 0x1) << 22) | (1 << 20) | (Base << 16) \
|
||||
| ((Dest & 0x1E) << 11) | (10 << 8) | (op >> 2));
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Write32(NO_COND | (0x1B << 23) | ((Dest & 0x10) << 18) | (1 << 20) | (Base << 16) \
|
||||
| ((Dest & 0xF) << 12) | (11 << 8) | (op >> 2));
|
||||
}
|
||||
}
|
||||
void ARMXEmitter::VSTR(ARMReg Src, ARMReg Base, u16 op)
|
||||
{
|
||||
_assert_msg_(DYNA_REC, Src >= S0 && Src <= D31, "Passed invalid src register to VSTR");
|
||||
_assert_msg_(DYNA_REC, Base <= R15, "Passed invalid base register to VSTR");
|
||||
_assert_msg_(DYNA_REC, !(op & 3), "Offset needs to be word aligned");
|
||||
bool single_reg = Src < D0;
|
||||
|
||||
Src = SubBase(Src);
|
||||
|
||||
if (single_reg)
|
||||
{
|
||||
Write32(NO_COND | (0x1B << 23) | ((Src & 0x1) << 22) | (Base << 16) \
|
||||
| ((Src & 0x1E) << 11) | (10 << 8) | (op >> 2));
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Write32(NO_COND | (0x1B << 23) | ((Src & 0x10) << 18) | (Base << 16) \
|
||||
| ((Src & 0xF) << 12) | (11 << 8) | (op >> 2));
|
||||
}
|
||||
}
|
||||
void ARMXEmitter::VCMP(ARMReg Vd, ARMReg Vm)
|
||||
{
|
||||
_assert_msg_(DYNA_REC, Vd < Q0, "Passed invalid Vd to VCMP");
|
||||
bool single_reg = Vd < D0;
|
||||
|
||||
Vd = SubBase(Vd);
|
||||
Vm = SubBase(Vm);
|
||||
|
||||
if (single_reg)
|
||||
{
|
||||
Write32(NO_COND | (0x1D << 23) | ((Vd & 0x1) << 22) | (0x34 << 16) | ((Vd & 0x1E) << 11) \
|
||||
| (0x2B << 6) | ((Vm & 0x1) << 5) | (Vm >> 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
Write32(NO_COND | (0x1D << 23) | ((Vd & 0x10) << 18) | (0x34 << 16) | ((Vd & 0xF) << 12) \
|
||||
| (0x2F << 6) | ((Vm & 0x10) << 1) | (Vm & 0xF));
|
||||
}
|
||||
}
|
||||
void ARMXEmitter::VCMP(ARMReg Vd)
|
||||
{
|
||||
_assert_msg_(DYNA_REC, Vd < Q0, "Passed invalid Vd to VCMP");
|
||||
bool single_reg = Vd < D0;
|
||||
|
||||
Vd = SubBase(Vd);
|
||||
|
||||
if (single_reg)
|
||||
{
|
||||
Write32(NO_COND | (0x1D << 23) | ((Vd & 0x1) << 22) | (0x35 << 16) | ((Vd & 0x1E) << 11) \
|
||||
| (0x2B << 6));
|
||||
}
|
||||
else
|
||||
{
|
||||
Write32(NO_COND | (0x1D << 23) | ((Vd & 0x10) << 18) | (0x35 << 16) | ((Vd & 0xF) << 12) \
|
||||
| (0x2F << 6));
|
||||
}
|
||||
}
|
||||
void ARMXEmitter::VDIV(ARMReg Vd, ARMReg Vn, ARMReg Vm)
|
||||
{
|
||||
_assert_msg_(DYNA_REC, Vd < Q0, "Pased invalid dest register to VSQRT");
|
||||
_assert_msg_(DYNA_REC, Vn < Q0, "Passed invalid Vn to VSQRT");
|
||||
_assert_msg_(DYNA_REC, Vm < Q0, "Passed invalid Vm to VSQRT");
|
||||
bool single_reg = Vd < D0;
|
||||
|
||||
Vd = SubBase(Vd);
|
||||
Vn = SubBase(Vn);
|
||||
Vm = SubBase(Vm);
|
||||
|
||||
if (single_reg)
|
||||
{
|
||||
Write32(NO_COND | (0x1D << 23) | ((Vd & 0x1) << 22) | ((Vn & 0x1E) << 15) \
|
||||
| ((Vd & 0x1E) << 11) | (0xA << 8) | ((Vn & 0x1) << 7) | ((Vm & 0x1) << 5) \
|
||||
| (Vm >> 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
Write32(NO_COND | (0x1D << 23) | ((Vd & 0x10) << 18) | ((Vn & 0xF) << 16) \
|
||||
| ((Vd & 0xF) << 12) | (0xB << 8) | ((Vn & 0x10) << 3) | ((Vm & 0x10) << 2) \
|
||||
| (Vm & 0xF));
|
||||
}
|
||||
}
|
||||
void ARMXEmitter::VSQRT(ARMReg Vd, ARMReg Vm)
|
||||
{
|
||||
_assert_msg_(DYNA_REC, Vd < Q0, "Pased invalid dest register to VSQRT");
|
||||
_assert_msg_(DYNA_REC, Vm < Q0, "Passed invalid Vm to VSQRT");
|
||||
bool single_reg = Vd < D0;
|
||||
|
||||
Vd = SubBase(Vd);
|
||||
Vm = SubBase(Vm);
|
||||
|
||||
if (single_reg)
|
||||
{
|
||||
Write32(NO_COND | (0x1D << 23) | ((Vd & 0x1) << 22) | (0x31 << 16) \
|
||||
| ((Vd & 0x1E) << 11) | (0x2B << 6) | ((Vm & 0x1) << 5) | (Vm >> 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
Write32(NO_COND | (0x1D << 23) | ((Vd & 0x10) << 18) | (0x31 << 16) \
|
||||
| ((Vd & 0xF) << 12) | (0x2F << 6) | ((Vm & 0x10) << 2) | (Vm & 0xF));
|
||||
}
|
||||
}
|
||||
// VFP and ASIMD
|
||||
void ARMXEmitter::VABS(ARMReg Vd, ARMReg Vm)
|
||||
{
|
||||
_assert_msg_(DYNA_REC, Vd < Q0, "VABS doesn't currently support Quad reg");
|
||||
_assert_msg_(DYNA_REC, Vd >= S0, "VABS doesn't support ARM Regs");
|
||||
bool single_reg = Vd < D0;
|
||||
|
||||
Vd = SubBase(Vd);
|
||||
Vm = SubBase(Vm);
|
||||
|
||||
if (single_reg)
|
||||
{
|
||||
Write32(NO_COND | (0xEB << 20) | ((Vd & 0x1) << 6) | ((Vd & 0x1E) << 11) \
|
||||
| (0xAC << 4) | ((Vm & 0x1) << 5) | (Vm >> 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
Write32(NO_COND | (0xEB << 20) | ((Vd & 0x10) << 18) | ((Vd & 0xF) << 12) \
|
||||
| (0xBC << 4) | ((Vm & 0x10) << 1) | (Vm & 0xF));
|
||||
}
|
||||
}
|
||||
void ARMXEmitter::VADD(ARMReg Vd, ARMReg Vn, ARMReg Vm)
|
||||
{
|
||||
_assert_msg_(DYNA_REC, Vd >= S0, "Passed invalid dest register to VADD");
|
||||
_assert_msg_(DYNA_REC, Vn >= S0, "Passed invalid Vn to VADD");
|
||||
_assert_msg_(DYNA_REC, Vm >= S0, "Passed invalid Vm to VADD");
|
||||
bool single_reg = Vd < D0;
|
||||
bool double_reg = Vd < Q0;
|
||||
|
||||
Vd = SubBase(Vd);
|
||||
Vn = SubBase(Vn);
|
||||
Vm = SubBase(Vm);
|
||||
|
||||
if (single_reg)
|
||||
{
|
||||
Write32(NO_COND | (0x1C << 23) | ((Vd & 0x1) << 22) | (0x3 << 20) \
|
||||
| ((Vn & 0x1E) << 15) | ((Vd & 0x1E) << 11) | (0x5 << 9) \
|
||||
| ((Vn & 0x1) << 7) | ((Vm & 0x1) << 5) | (Vm >> 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (double_reg)
|
||||
{
|
||||
Write32(NO_COND | (0x1C << 23) | ((Vd & 0x10) << 18) | (0x3 << 20) \
|
||||
| ((Vn & 0xF) << 16) | ((Vd & 0xF) << 12) | (0xB << 8) \
|
||||
| ((Vn & 0x10) << 3) | ((Vm & 0x10) << 2) | (Vm & 0xF));
|
||||
}
|
||||
else
|
||||
{
|
||||
_assert_msg_(DYNA_REC, cpu_info.bNEON, "Trying to use VADD with Quad Reg without support!");
|
||||
Write32((0xF2 << 24) | ((Vd & 0x10) << 18) | ((Vn & 0xF) << 16) \
|
||||
| ((Vd & 0xF) << 12) | (0xD << 8) | ((Vn & 0x10) << 3) \
|
||||
| (1 << 6) | ((Vm & 0x10) << 2) | (Vm & 0xF));
|
||||
}
|
||||
}
|
||||
}
|
||||
void ARMXEmitter::VSUB(ARMReg Vd, ARMReg Vn, ARMReg Vm)
|
||||
{
|
||||
_assert_msg_(DYNA_REC, Vd >= S0, "Passed invalid dest register to VSUB");
|
||||
_assert_msg_(DYNA_REC, Vn >= S0, "Passed invalid Vn to VSUB");
|
||||
_assert_msg_(DYNA_REC, Vm >= S0, "Passed invalid Vm to VSUB");
|
||||
bool single_reg = Vd < D0;
|
||||
bool double_reg = Vd < Q0;
|
||||
|
||||
Vd = SubBase(Vd);
|
||||
Vn = SubBase(Vn);
|
||||
Vm = SubBase(Vm);
|
||||
|
||||
if (single_reg)
|
||||
{
|
||||
Write32(NO_COND | (0x1C << 23) | ((Vd & 0x1) << 22) | (0x3 << 20) \
|
||||
| ((Vn & 0x1E) << 15) | ((Vd & 0x1E) << 11) | (0x5 << 9) \
|
||||
| ((Vn & 0x1) << 7) | (1 << 6) | ((Vm & 0x1) << 5) | (Vm >> 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (double_reg)
|
||||
{
|
||||
Write32(NO_COND | (0x1C << 23) | ((Vd & 0x10) << 18) | (0x3 << 20) \
|
||||
| ((Vn & 0xF) << 16) | ((Vd & 0xF) << 12) | (0xB << 8) \
|
||||
| ((Vn & 0x10) << 3) | (1 << 6) | ((Vm & 0x10) << 2) | (Vm & 0xF));
|
||||
}
|
||||
else
|
||||
{
|
||||
_assert_msg_(DYNA_REC, cpu_info.bNEON, "Trying to use VADD with Quad Reg without support!");
|
||||
Write32((0xF2 << 24) | (1 << 21) | ((Vd & 0x10) << 18) | ((Vn & 0xF) << 16) \
|
||||
| ((Vd & 0xF) << 12) | (0xD << 8) | ((Vn & 0x10) << 3) \
|
||||
| (1 << 6) | ((Vm & 0x10) << 2) | (Vm & 0xF));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src, bool high)
|
||||
{
|
||||
_assert_msg_(DYNA_REC, Src < S0, "This VMOV doesn't support SRC other than ARM Reg");
|
||||
_assert_msg_(DYNA_REC, Dest >= D0, "This VMOV doesn't support DEST other than VFP");
|
||||
|
||||
Dest = SubBase(Dest);
|
||||
|
||||
Write32(NO_COND | (0xE << 24) | (high << 21) | ((Dest & 0xF) << 16) | (Src << 12) \
|
||||
| (11 << 8) | ((Dest & 0x10) << 3) | (1 << 4));
|
||||
}
|
||||
void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src)
|
||||
{
|
||||
if (Dest > R15)
|
||||
{
|
||||
if (Src < S0)
|
||||
{
|
||||
if (Dest < D0)
|
||||
{
|
||||
// Moving to a Neon register FROM ARM Reg
|
||||
Dest = (ARMReg)(Dest - S0);
|
||||
Write32(NO_COND | (0xE0 << 20) | ((Dest & 0x1E) << 15) | (Src << 12) \
|
||||
| (0xA << 8) | ((Dest & 0x1) << 7) | (1 << 4));
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Move 64bit from Arm reg
|
||||
_assert_msg_(DYNA_REC, false, "This VMOV doesn't support moving 64bit ARM to NEON");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Src > R15)
|
||||
{
|
||||
if (Src < D0)
|
||||
{
|
||||
// Moving to ARM Reg from Neon Register
|
||||
Src = (ARMReg)(Src - S0);
|
||||
Write32(NO_COND | (0xE1 << 20) | ((Src & 0x1E) << 15) | (Dest << 12) \
|
||||
| (0xA << 8) | ((Src & 0x1) << 7) | (1 << 4));
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Move 64bit To Arm reg
|
||||
_assert_msg_(DYNA_REC, false, "This VMOV doesn't support moving 64bit ARM From NEON");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Move Arm reg to Arm reg
|
||||
_assert_msg_(DYNA_REC, false, "VMOV doesn't support moving ARM registers");
|
||||
}
|
||||
}
|
||||
// Moving NEON registers
|
||||
int SrcSize = Src < D0 ? 1 : Src < Q0 ? 2 : 4;
|
||||
int DestSize = Dest < D0 ? 1 : Dest < Q0 ? 2 : 4;
|
||||
bool Single = DestSize == 1;
|
||||
bool Quad = DestSize == 4;
|
||||
|
||||
_assert_msg_(DYNA_REC, SrcSize == DestSize, "VMOV doesn't support moving different register sizes");
|
||||
|
||||
Dest = SubBase(Dest);
|
||||
Src = SubBase(Src);
|
||||
|
||||
if (Single)
|
||||
{
|
||||
Write32(NO_COND | (0x1D << 23) | ((Dest & 0x1) << 22) | (0x3 << 20) | ((Dest & 0x1E) << 11) \
|
||||
| (0x5 << 9) | (1 << 6) | ((Src & 0x1) << 5) | ((Src & 0x1E) >> 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Double and quad
|
||||
if (Quad)
|
||||
{
|
||||
_assert_msg_(DYNA_REC, cpu_info.bNEON, "Trying to use quad registers when you don't support ASIMD.");
|
||||
// Gets encoded as a Double register
|
||||
Write32((0xF2 << 24) | ((Dest & 0x10) << 18) | (2 << 20) | ((Src & 0xF) << 16) \
|
||||
| ((Dest & 0xF) << 12) | (1 << 8) | ((Src & 0x10) << 3) | (1 << 6) \
|
||||
| ((Src & 0x10) << 1) | (1 << 4) | (Src & 0xF));
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Write32(NO_COND | (0x1D << 23) | ((Dest & 0x10) << 18) | (0x3 << 20) | ((Dest & 0xF) << 12) \
|
||||
| (0x2D << 6) | ((Src & 0x10) << 1) | (Src & 0xF));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,587 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
// WARNING - THIS LIBRARY IS NOT THREAD SAFE!!!
|
||||
|
||||
#ifndef _DOLPHIN_ARM_CODEGEN_
|
||||
#define _DOLPHIN_ARM_CODEGEN_
|
||||
|
||||
#include "Common.h"
|
||||
#include "MemoryUtil.h"
|
||||
#if defined(__SYMBIAN32__) || defined(PANDORA)
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
#undef _IP
|
||||
#undef R0
|
||||
#undef _SP
|
||||
#undef _LR
|
||||
#undef _PC
|
||||
|
||||
namespace ArmGen
|
||||
{
|
||||
enum ARMReg
|
||||
{
|
||||
// GPRs
|
||||
R0 = 0, R1, R2, R3, R4, R5,
|
||||
R6, R7, R8, R9, R10, R11,
|
||||
|
||||
// SPRs
|
||||
// R13 - R15 are SP, LR, and PC.
|
||||
// Almost always referred to by name instead of register number
|
||||
R12 = 12, R13 = 13, R14 = 14, R15 = 15,
|
||||
_IP = 12, _SP = 13, _LR = 14, _PC = 15,
|
||||
|
||||
|
||||
// VFP single precision registers
|
||||
S0, S1, S2, S3, S4, S5, S6,
|
||||
S7, S8, S9, S10, S11, S12, S13,
|
||||
S14, S15, S16, S17, S18, S19, S20,
|
||||
S21, S22, S23, S24, S25, S26, S27,
|
||||
S28, S29, S30, S31,
|
||||
|
||||
// VFP Double Precision registers
|
||||
D0, D1, D2, D3, D4, D5, D6, D7,
|
||||
D8, D9, D10, D11, D12, D13, D14, D15,
|
||||
D16, D17, D18, D19, D20, D21, D22, D23,
|
||||
D24, D25, D26, D27, D28, D29, D30, D31,
|
||||
|
||||
// ASIMD Quad-Word registers
|
||||
Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7,
|
||||
Q8, Q9, Q10, Q11, Q12, Q13, Q14, Q15,
|
||||
INVALID_REG = 0xFFFFFFFF
|
||||
};
|
||||
|
||||
enum CCFlags
|
||||
{
|
||||
CC_EQ = 0, // Equal
|
||||
CC_NEQ, // Not equal
|
||||
CC_CS, // Carry Set
|
||||
CC_CC, // Carry Clear
|
||||
CC_MI, // Minus (Negative)
|
||||
CC_PL, // Plus
|
||||
CC_VS, // Overflow
|
||||
CC_VC, // No Overflow
|
||||
CC_HI, // Unsigned higher
|
||||
CC_LS, // Unsigned lower or same
|
||||
CC_GE, // Signed greater than or equal
|
||||
CC_LT, // Signed less than
|
||||
CC_GT, // Signed greater than
|
||||
CC_LE, // Signed less than or equal
|
||||
CC_AL, // Always (unconditional) 14
|
||||
CC_HS = CC_CS, // Alias of CC_CS Unsigned higher or same
|
||||
CC_LO = CC_CC, // Alias of CC_CC Unsigned lower
|
||||
};
|
||||
const u32 NO_COND = 0xE0000000;
|
||||
|
||||
enum ShiftType
|
||||
{
|
||||
ST_LSL = 0,
|
||||
ST_ASL = 0,
|
||||
ST_LSR = 1,
|
||||
ST_ASR = 2,
|
||||
ST_ROR = 3,
|
||||
ST_RRX = 4
|
||||
};
|
||||
enum IntegerSize
|
||||
{
|
||||
I_I8 = 0,
|
||||
I_I16,
|
||||
I_I32,
|
||||
I_I64
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
NUMGPRs = 13,
|
||||
};
|
||||
|
||||
class ARMXEmitter;
|
||||
|
||||
enum OpType
|
||||
{
|
||||
TYPE_IMM = 0,
|
||||
TYPE_REG,
|
||||
TYPE_IMMSREG,
|
||||
TYPE_RSR,
|
||||
TYPE_MEM
|
||||
};
|
||||
|
||||
// This is no longer a proper operand2 class. Need to split up.
|
||||
class Operand2
|
||||
{
|
||||
friend class ARMXEmitter;
|
||||
protected:
|
||||
u32 Value;
|
||||
|
||||
private:
|
||||
OpType Type;
|
||||
|
||||
// IMM types
|
||||
u8 Rotation; // Only for u8 values
|
||||
|
||||
// Register types
|
||||
u8 IndexOrShift;
|
||||
ShiftType Shift;
|
||||
public:
|
||||
OpType GetType()
|
||||
{
|
||||
return Type;
|
||||
}
|
||||
Operand2() {}
|
||||
Operand2(u32 imm, OpType type = TYPE_IMM)
|
||||
{
|
||||
Type = type;
|
||||
Value = imm;
|
||||
Rotation = 0;
|
||||
}
|
||||
|
||||
Operand2(ARMReg Reg)
|
||||
{
|
||||
Type = TYPE_REG;
|
||||
Value = Reg;
|
||||
Rotation = 0;
|
||||
}
|
||||
Operand2(u8 imm, u8 rotation)
|
||||
{
|
||||
Type = TYPE_IMM;
|
||||
Value = imm;
|
||||
Rotation = rotation;
|
||||
}
|
||||
Operand2(ARMReg base, ShiftType type, ARMReg shift) // RSR
|
||||
{
|
||||
Type = TYPE_RSR;
|
||||
_assert_msg_(DYNA_REC, type != ST_RRX, "Invalid Operand2: RRX does not take a register shift amount");
|
||||
IndexOrShift = shift;
|
||||
Shift = type;
|
||||
Value = base;
|
||||
}
|
||||
|
||||
Operand2(u8 shift, ShiftType type, ARMReg base)// For IMM shifted register
|
||||
{
|
||||
if(shift == 32) shift = 0;
|
||||
switch (type)
|
||||
{
|
||||
case ST_LSL:
|
||||
_assert_msg_(DYNA_REC, shift < 32, "Invalid Operand2: LSL %u", shift);
|
||||
break;
|
||||
case ST_LSR:
|
||||
_assert_msg_(DYNA_REC, shift <= 32, "Invalid Operand2: LSR %u", shift);
|
||||
if (!shift)
|
||||
type = ST_LSL;
|
||||
if (shift == 32)
|
||||
shift = 0;
|
||||
break;
|
||||
case ST_ASR:
|
||||
_assert_msg_(DYNA_REC, shift < 32, "Invalid Operand2: LSR %u", shift);
|
||||
if (!shift)
|
||||
type = ST_LSL;
|
||||
if (shift == 32)
|
||||
shift = 0;
|
||||
break;
|
||||
case ST_ROR:
|
||||
_assert_msg_(DYNA_REC, shift < 32, "Invalid Operand2: ROR %u", shift);
|
||||
if (!shift)
|
||||
type = ST_LSL;
|
||||
break;
|
||||
case ST_RRX:
|
||||
_assert_msg_(DYNA_REC, shift == 0, "Invalid Operand2: RRX does not take an immediate shift amount");
|
||||
type = ST_ROR;
|
||||
break;
|
||||
}
|
||||
IndexOrShift = shift;
|
||||
Shift = type;
|
||||
Value = base;
|
||||
Type = TYPE_IMMSREG;
|
||||
}
|
||||
const u32 GetData()
|
||||
{
|
||||
switch(Type)
|
||||
{
|
||||
case TYPE_IMM:
|
||||
return Imm12Mod(); // This'll need to be changed later
|
||||
case TYPE_REG:
|
||||
return Rm();
|
||||
case TYPE_IMMSREG:
|
||||
return IMMSR();
|
||||
case TYPE_RSR:
|
||||
return RSR();
|
||||
default:
|
||||
_assert_msg_(DYNA_REC, false, "GetData with Invalid Type");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
const u32 IMMSR() // IMM shifted register
|
||||
{
|
||||
_assert_msg_(DYNA_REC, Type = TYPE_IMMSREG, "IMMSR must be imm shifted register");
|
||||
return ((IndexOrShift & 0x1f) << 7 | (Shift << 5) | Value);
|
||||
}
|
||||
const u32 RSR() // Register shifted register
|
||||
{
|
||||
_assert_msg_(DYNA_REC, Type == TYPE_RSR, "RSR must be RSR Of Course");
|
||||
return (IndexOrShift << 8) | (Shift << 5) | 0x10 | Value;
|
||||
}
|
||||
const u32 Rm()
|
||||
{
|
||||
_assert_msg_(DYNA_REC, Type == TYPE_REG, "Rm must be with Reg");
|
||||
return Value;
|
||||
}
|
||||
|
||||
const u32 Imm5()
|
||||
{
|
||||
_assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm5 not IMM value");
|
||||
return ((Value & 0x0000001F) << 7);
|
||||
}
|
||||
const u32 Imm8()
|
||||
{
|
||||
_assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm8Rot not IMM value");
|
||||
return Value & 0xFF;
|
||||
}
|
||||
const u32 Imm8Rot() // IMM8 with Rotation
|
||||
{
|
||||
_assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm8Rot not IMM value");
|
||||
_assert_msg_(DYNA_REC, (Rotation & 0xE1) != 0, "Invalid Operand2: immediate rotation %u", Rotation);
|
||||
return (1 << 25) | (Rotation << 7) | (Value & 0x000000FF);
|
||||
}
|
||||
const u32 Imm12()
|
||||
{
|
||||
_assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm12 not IMM");
|
||||
return (Value & 0x00000FFF);
|
||||
}
|
||||
|
||||
const u32 Imm12Mod()
|
||||
{
|
||||
// This is a IMM12 with the top four bits being rotation and the
|
||||
// bottom eight being a IMM. This is for instructions that need to
|
||||
// expand a 8bit IMM to a 32bit value and gives you some rotation as
|
||||
// well.
|
||||
// Each rotation rotates to the right by 2 bits
|
||||
_assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm12Mod not IMM");
|
||||
return ((Rotation & 0xF) << 8) | (Value & 0xFF);
|
||||
}
|
||||
const u32 Imm16()
|
||||
{
|
||||
_assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm16 not IMM");
|
||||
return ( (Value & 0xF000) << 4) | (Value & 0x0FFF);
|
||||
}
|
||||
const u32 Imm16Low()
|
||||
{
|
||||
return Imm16();
|
||||
}
|
||||
const u32 Imm16High() // Returns high 16bits
|
||||
{
|
||||
_assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm16 not IMM");
|
||||
return ( ((Value >> 16) & 0xF000) << 4) | ((Value >> 16) & 0x0FFF);
|
||||
}
|
||||
const u32 Imm24()
|
||||
{
|
||||
_assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm16 not IMM");
|
||||
return (Value & 0x0FFFFFFF);
|
||||
}
|
||||
// NEON and ASIMD specific
|
||||
const u32 Imm8ASIMD()
|
||||
{
|
||||
_assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm8ASIMD not IMM");
|
||||
return ((Value & 0x80) << 17) | ((Value & 0x70) << 12) | (Value & 0xF);
|
||||
}
|
||||
const u32 Imm8VFP()
|
||||
{
|
||||
_assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm8VFP not IMM");
|
||||
return ((Value & 0xF0) << 12) | (Value & 0xF);
|
||||
}
|
||||
};
|
||||
|
||||
// Use these when you don't know if an imm can be represented as an operand2.
|
||||
// This lets you generate both an optimal and a fallback solution by checking
|
||||
// the return value, which will be false if these fail to find a Operand2 that
|
||||
// represents your 32-bit imm value.
|
||||
bool TryMakeOperand2(u32 imm, Operand2 &op2);
|
||||
bool TryMakeOperand2_AllowInverse(u32 imm, Operand2 &op2, bool *inverse);
|
||||
bool TryMakeOperand2_AllowNegation(s32 imm, Operand2 &op2, bool *negated);
|
||||
|
||||
inline Operand2 R(ARMReg Reg) { return Operand2(Reg, TYPE_REG); }
|
||||
inline Operand2 IMM(u32 Imm) { return Operand2(Imm, TYPE_IMM); }
|
||||
inline Operand2 Mem(void *ptr) { return Operand2((u32)ptr, TYPE_IMM); }
|
||||
//usage: struct {int e;} s; STRUCT_OFFSET(s,e)
|
||||
#define STRUCT_OFF(str,elem) ((u32)((u32)&(str).elem-(u32)&(str)))
|
||||
|
||||
|
||||
struct FixupBranch
|
||||
{
|
||||
u8 *ptr;
|
||||
u32 condition; // Remembers our codition at the time
|
||||
int type; //0 = B 1 = BL
|
||||
};
|
||||
|
||||
typedef const u8* JumpTarget;
|
||||
|
||||
class ARMXEmitter
|
||||
{
|
||||
friend struct OpArg; // for Write8 etc
|
||||
private:
|
||||
u8 *code, *startcode;
|
||||
u8 *lastCacheFlushEnd;
|
||||
u32 condition;
|
||||
|
||||
void WriteStoreOp(u32 op, ARMReg dest, ARMReg src, Operand2 op2);
|
||||
void WriteRegStoreOp(u32 op, ARMReg dest, bool WriteBack, u16 RegList);
|
||||
void WriteShiftedDataOp(u32 op, bool SetFlags, ARMReg dest, ARMReg src, ARMReg op2);
|
||||
void WriteShiftedDataOp(u32 op, bool SetFlags, ARMReg dest, ARMReg src, Operand2 op2);
|
||||
void WriteSignedMultiply(u32 Op, u32 Op2, u32 Op3, ARMReg dest, ARMReg r1, ARMReg r2);
|
||||
|
||||
void Write4OpMultiply(u32 op, ARMReg destLo, ARMReg destHi, ARMReg rn, ARMReg rm);
|
||||
|
||||
// New Ops
|
||||
void WriteInstruction(u32 op, ARMReg Rd, ARMReg Rn, Operand2 Rm, bool SetFlags = false);
|
||||
|
||||
protected:
|
||||
inline void Write32(u32 value) {*(u32*)code = value; code+=4;}
|
||||
|
||||
public:
|
||||
ARMXEmitter() : code(0), startcode(0), lastCacheFlushEnd(0) {
|
||||
condition = CC_AL << 28;
|
||||
}
|
||||
ARMXEmitter(u8 *code_ptr) {
|
||||
code = code_ptr;
|
||||
lastCacheFlushEnd = code_ptr;
|
||||
startcode = code_ptr;
|
||||
condition = CC_AL << 28;
|
||||
}
|
||||
virtual ~ARMXEmitter() {}
|
||||
|
||||
void SetCodePtr(u8 *ptr);
|
||||
void ReserveCodeSpace(u32 bytes);
|
||||
const u8 *AlignCode16();
|
||||
const u8 *AlignCodePage();
|
||||
const u8 *GetCodePtr() const;
|
||||
void FlushIcache();
|
||||
void FlushIcacheSection(u8 *start, u8 *end);
|
||||
u8 *GetWritableCodePtr();
|
||||
|
||||
void SetCC(CCFlags cond = CC_AL);
|
||||
|
||||
// Special purpose instructions
|
||||
|
||||
// Dynamic Endian Switching
|
||||
void SETEND(bool BE);
|
||||
// Debug Breakpoint
|
||||
void BKPT(u16 arg);
|
||||
|
||||
// Hint instruction
|
||||
void YIELD();
|
||||
|
||||
// Do nothing
|
||||
void NOP(int count = 1); //nop padding - TODO: fast nop slides, for amd and intel (check their manuals)
|
||||
|
||||
#ifdef CALL
|
||||
#undef CALL
|
||||
#endif
|
||||
|
||||
// Branching
|
||||
FixupBranch B();
|
||||
FixupBranch B_CC(CCFlags Cond);
|
||||
void B_CC(CCFlags Cond, const void *fnptr);
|
||||
FixupBranch BL();
|
||||
FixupBranch BL_CC(CCFlags Cond);
|
||||
void SetJumpTarget(FixupBranch const &branch);
|
||||
|
||||
void B (const void *fnptr);
|
||||
void B (ARMReg src);
|
||||
void BL(const void *fnptr);
|
||||
void BL(ARMReg src);
|
||||
|
||||
void PUSH(const int num, ...);
|
||||
void POP(const int num, ...);
|
||||
|
||||
// New Data Ops
|
||||
void AND (ARMReg Rd, ARMReg Rn, Operand2 Rm);
|
||||
void ANDS(ARMReg Rd, ARMReg Rn, Operand2 Rm);
|
||||
void EOR (ARMReg dest, ARMReg src, Operand2 op2);
|
||||
void EORS(ARMReg dest, ARMReg src, Operand2 op2);
|
||||
void SUB (ARMReg dest, ARMReg src, Operand2 op2);
|
||||
void SUBS(ARMReg dest, ARMReg src, Operand2 op2);
|
||||
void RSB (ARMReg dest, ARMReg src, Operand2 op2);
|
||||
void RSBS(ARMReg dest, ARMReg src, Operand2 op2);
|
||||
void ADD (ARMReg dest, ARMReg src, Operand2 op2);
|
||||
void ADDS(ARMReg dest, ARMReg src, Operand2 op2);
|
||||
void ADC (ARMReg dest, ARMReg src, Operand2 op2);
|
||||
void ADCS(ARMReg dest, ARMReg src, Operand2 op2);
|
||||
void LSL (ARMReg dest, ARMReg src, Operand2 op2);
|
||||
void LSL (ARMReg dest, ARMReg src, ARMReg op2);
|
||||
void LSLS(ARMReg dest, ARMReg src, Operand2 op2);
|
||||
void LSLS(ARMReg dest, ARMReg src, ARMReg op2);
|
||||
void SBC (ARMReg dest, ARMReg src, Operand2 op2);
|
||||
void SBCS(ARMReg dest, ARMReg src, Operand2 op2);
|
||||
void REV (ARMReg dest, ARMReg src);
|
||||
void REV16 (ARMReg dest, ARMReg src);
|
||||
void RSC (ARMReg dest, ARMReg src, Operand2 op2);
|
||||
void RSCS(ARMReg dest, ARMReg src, Operand2 op2);
|
||||
void TST ( ARMReg src, Operand2 op2);
|
||||
void TEQ ( ARMReg src, Operand2 op2);
|
||||
void CMP ( ARMReg src, Operand2 op2);
|
||||
void CMN ( ARMReg src, Operand2 op2);
|
||||
void ORR (ARMReg dest, ARMReg src, Operand2 op2);
|
||||
void ORRS(ARMReg dest, ARMReg src, Operand2 op2);
|
||||
void MOV (ARMReg dest, Operand2 op2);
|
||||
void MOVS(ARMReg dest, Operand2 op2);
|
||||
void BIC (ARMReg dest, ARMReg src, Operand2 op2); // BIC = ANDN
|
||||
void BICS(ARMReg dest, ARMReg src, Operand2 op2);
|
||||
void MVN (ARMReg dest, Operand2 op2);
|
||||
void MVNS(ARMReg dest, Operand2 op2);
|
||||
void MOVW(ARMReg dest, Operand2 op2);
|
||||
void MOVT(ARMReg dest, Operand2 op2, bool TopBits = false);
|
||||
|
||||
// UDIV and SDIV are only available on CPUs that have
|
||||
// the idiva hardare capacity
|
||||
void UDIV(ARMReg dest, ARMReg dividend, ARMReg divisor);
|
||||
void SDIV(ARMReg dest, ARMReg dividend, ARMReg divisor);
|
||||
|
||||
void MUL (ARMReg dest, ARMReg src, ARMReg op2);
|
||||
void MULS(ARMReg dest, ARMReg src, ARMReg op2);
|
||||
|
||||
void UMULL(ARMReg destLo, ARMReg destHi, ARMReg rn, ARMReg rm);
|
||||
void SMULL(ARMReg destLo, ARMReg destHi, ARMReg rn, ARMReg rm);
|
||||
|
||||
void SXTB(ARMReg dest, ARMReg op2);
|
||||
void SXTH(ARMReg dest, ARMReg op2, u8 rotation = 0);
|
||||
void SXTAH(ARMReg dest, ARMReg src, ARMReg op2, u8 rotation = 0);
|
||||
// Using just MSR here messes with our defines on the PPC side of stuff (when this code was in dolphin...)
|
||||
// Just need to put an underscore here, bit annoying.
|
||||
void _MSR (bool nzcvq, bool g, Operand2 op2);
|
||||
void _MSR (bool nzcvq, bool g, ARMReg src );
|
||||
void MRS (ARMReg dest);
|
||||
|
||||
// Memory load/store operations
|
||||
void LDR (ARMReg dest, ARMReg src, Operand2 op2 = 0);
|
||||
// Offset adds to the base register in LDR
|
||||
void LDR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add);
|
||||
void LDRH(ARMReg dest, ARMReg src, Operand2 op = 0);
|
||||
void LDRB(ARMReg dest, ARMReg src, Operand2 op2 = 0);
|
||||
void STR (ARMReg dest, ARMReg src, Operand2 op2 = 0);
|
||||
// Offset adds on to the destination register in STR
|
||||
void STR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add);
|
||||
|
||||
void STRB(ARMReg dest, ARMReg src, Operand2 op2 = 0);
|
||||
void STMFD(ARMReg dest, bool WriteBack, const int Regnum, ...);
|
||||
void LDMFD(ARMReg dest, bool WriteBack, const int Regnum, ...);
|
||||
|
||||
// Exclusive Access operations
|
||||
void LDREX(ARMReg dest, ARMReg base);
|
||||
// dest contains the result if the instruction managed to store the value
|
||||
void STREX(ARMReg dest, ARMReg base, ARMReg op);
|
||||
void DMB ();
|
||||
void SVC(Operand2 op);
|
||||
|
||||
// NEON and ASIMD instructions
|
||||
// None of these will be created with conditional since ARM
|
||||
// is deprecating conditional execution of ASIMD instructions.
|
||||
// ASIMD instructions don't even have a conditional encoding.
|
||||
|
||||
// Subtracts the base from the register to give us the real one
|
||||
ARMReg SubBase(ARMReg Reg);
|
||||
// NEON Only
|
||||
void VADD(IntegerSize Size, ARMReg Vd, ARMReg Vn, ARMReg Vm);
|
||||
void VSUB(IntegerSize Size, ARMReg Vd, ARMReg Vn, ARMReg Vm);
|
||||
|
||||
// VFP Only
|
||||
void VLDR(ARMReg Dest, ARMReg Base, u16 op);
|
||||
void VSTR(ARMReg Src, ARMReg Base, u16 op);
|
||||
void VCMP(ARMReg Vd, ARMReg Vm);
|
||||
// Compares against zero
|
||||
void VCMP(ARMReg Vd);
|
||||
void VDIV(ARMReg Vd, ARMReg Vn, ARMReg Vm);
|
||||
void VSQRT(ARMReg Vd, ARMReg Vm);
|
||||
|
||||
// NEON and VFP
|
||||
void VABS(ARMReg Vd, ARMReg Vm);
|
||||
void VADD(ARMReg Vd, ARMReg Vn, ARMReg Vm);
|
||||
void VSUB(ARMReg Vd, ARMReg Vn, ARMReg Vm);
|
||||
|
||||
void VMOV(ARMReg Dest, ARMReg Src, bool high);
|
||||
void VMOV(ARMReg Dest, ARMReg Src);
|
||||
|
||||
void QuickCallFunction(ARMReg scratchreg, void *func);
|
||||
// Utility functions
|
||||
void MOVI2R(ARMReg reg, u32 val, bool optimize = true);
|
||||
void ARMABI_MOVI2M(Operand2 op, Operand2 val);
|
||||
}; // class ARMXEmitter
|
||||
|
||||
|
||||
// Everything that needs to generate X86 code should inherit from this.
|
||||
// You get memory management for free, plus, you can use all the MOV etc functions without
|
||||
// having to prefix them with gen-> or something similar.
|
||||
class ARMXCodeBlock : public ARMXEmitter
|
||||
{
|
||||
protected:
|
||||
u8 *region;
|
||||
size_t region_size;
|
||||
|
||||
public:
|
||||
ARMXCodeBlock() : region(NULL), region_size(0) {}
|
||||
virtual ~ARMXCodeBlock() { if (region) FreeCodeSpace(); }
|
||||
|
||||
// Call this before you generate any code.
|
||||
void AllocCodeSpace(int size)
|
||||
{
|
||||
region_size = size;
|
||||
region = (u8*)AllocateExecutableMemory(region_size);
|
||||
SetCodePtr(region);
|
||||
}
|
||||
|
||||
// Always clear code space with breakpoints, so that if someone accidentally executes
|
||||
// uninitialized, it just breaks into the debugger.
|
||||
void ClearCodeSpace()
|
||||
{
|
||||
// x86/64: 0xCC = breakpoint
|
||||
memset(region, 0xCC, region_size);
|
||||
ResetCodePtr();
|
||||
}
|
||||
|
||||
// Call this when shutting down. Don't rely on the destructor, even though it'll do the job.
|
||||
void FreeCodeSpace()
|
||||
{
|
||||
FreeMemoryPages(region, region_size);
|
||||
region = NULL;
|
||||
region_size = 0;
|
||||
}
|
||||
|
||||
bool IsInSpace(u8 *ptr)
|
||||
{
|
||||
return ptr >= region && ptr < region + region_size;
|
||||
}
|
||||
|
||||
// Cannot currently be undone. Will write protect the entire code region.
|
||||
// Start over if you need to change the code (call FreeCodeSpace(), AllocCodeSpace()).
|
||||
void WriteProtect()
|
||||
{
|
||||
WriteProtectMemory(region, region_size, true);
|
||||
}
|
||||
|
||||
void ResetCodePtr()
|
||||
{
|
||||
SetCodePtr(region);
|
||||
}
|
||||
|
||||
size_t GetSpaceLeft() const
|
||||
{
|
||||
return region_size - (GetCodePtr() - region);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif // _DOLPHIN_INTEL_CODEGEN_
|
|
@ -25,7 +25,8 @@ enum CPUVendor
|
|||
{
|
||||
VENDOR_INTEL = 0,
|
||||
VENDOR_AMD = 1,
|
||||
VENDOR_OTHER = 2,
|
||||
VENDOR_ARM = 2,
|
||||
VENDOR_OTHER = 3,
|
||||
};
|
||||
|
||||
struct CPUInfo
|
||||
|
@ -55,6 +56,26 @@ struct CPUInfo
|
|||
bool bAES;
|
||||
bool bLAHFSAHF64;
|
||||
bool bLongMode;
|
||||
|
||||
// ARM specific CPUInfo
|
||||
bool bSwp;
|
||||
bool bHalf;
|
||||
bool bThumb;
|
||||
bool bFastMult;
|
||||
bool bVFP;
|
||||
bool bEDSP;
|
||||
bool bThumbEE;
|
||||
bool bNEON;
|
||||
bool bVFPv3;
|
||||
bool bTLS;
|
||||
bool bVFPv4;
|
||||
bool bIDIVa;
|
||||
bool bIDIVt;
|
||||
bool bArmV7; // enable MOVT, MOVW etc
|
||||
|
||||
// ARMv8 specific
|
||||
bool bFP;
|
||||
bool bASIMD;
|
||||
|
||||
// Call Detect()
|
||||
explicit CPUInfo();
|
||||
|
|
|
@ -133,7 +133,9 @@ private:
|
|||
// wxWidgets does not have a true dummy macro for this.
|
||||
#define _trans(a) a
|
||||
|
||||
#if defined __GNUC__
|
||||
#if defined _M_GENERIC
|
||||
# define _M_SSE 0x0
|
||||
#elif defined __GNUC__
|
||||
# if defined __SSE4_2__
|
||||
# define _M_SSE 0x402
|
||||
# elif defined __SSE4_1__
|
||||
|
@ -144,7 +146,7 @@ private:
|
|||
# define _M_SSE 0x300
|
||||
# endif
|
||||
#elif (_MSC_VER >= 1500) || __INTEL_COMPILER // Visual Studio 2008
|
||||
# define _M_SSE 0x402
|
||||
# define _M_SSE 0x402
|
||||
#endif
|
||||
|
||||
// Host communication.
|
||||
|
|
|
@ -35,7 +35,7 @@ template<> struct CompileTimeAssert<true> {};
|
|||
#define b32(x) (b16(x) | (b16(x) >>16) )
|
||||
#define ROUND_UP_POW2(x) (b32(x - 1) + 1)
|
||||
|
||||
#if defined __GNUC__ && !defined __SSSE3__
|
||||
#if defined __GNUC__ && !defined __SSSE3__ && !defined _M_GENERIC
|
||||
#include <emmintrin.h>
|
||||
static __inline __m128i __attribute__((__always_inline__))
|
||||
_mm_shuffle_epi8(__m128i a, __m128i mask)
|
||||
|
@ -60,6 +60,8 @@ _mm_shuffle_epi8(__m128i a, __m128i mask)
|
|||
// go to debugger mode
|
||||
#ifdef GEKKO
|
||||
#define Crash()
|
||||
#elif defined _M_GENERIC
|
||||
#define Crash() { exit(1); }
|
||||
#else
|
||||
#define Crash() {asm ("int $3");}
|
||||
#endif
|
||||
|
@ -136,6 +138,15 @@ inline u8 swap8(u8 _data) {return _data;}
|
|||
inline u16 swap16(u16 _data) {return _byteswap_ushort(_data);}
|
||||
inline u32 swap32(u32 _data) {return _byteswap_ulong (_data);}
|
||||
inline u64 swap64(u64 _data) {return _byteswap_uint64(_data);}
|
||||
#elif _M_ARM
|
||||
#ifdef ANDROID
|
||||
#undef swap16
|
||||
#undef swap32
|
||||
#undef swap64
|
||||
#endif
|
||||
inline u16 swap16 (u16 _data) { u32 data = _data; __asm__ ("rev16 %0, %1\n" : "=l" (data) : "l" (data)); return (u16)data;}
|
||||
inline u32 swap32 (u32 _data) {__asm__ ("rev %0, %1\n" : "=l" (_data) : "l" (_data)); return _data;}
|
||||
inline u64 swap64(u64 _data) {return ((u64)swap32(_data) << 32) | swap32(_data >> 32);}
|
||||
#elif __linux__
|
||||
inline u16 swap16(u16 _data) {return bswap_16(_data);}
|
||||
inline u32 swap32(u32 _data) {return bswap_32(_data);}
|
||||
|
@ -161,7 +172,6 @@ inline u64 swap64(u64 data) {return ((u64)swap32(data) << 32) | swap32(data >> 3
|
|||
inline u16 swap16(const u8* _pData) {return swap16(*(const u16*)_pData);}
|
||||
inline u32 swap32(const u8* _pData) {return swap32(*(const u32*)_pData);}
|
||||
inline u64 swap64(const u8* _pData) {return swap64(*(const u64*)_pData);}
|
||||
|
||||
} // Namespace Common
|
||||
|
||||
#endif // _COMMONFUNCS_H_
|
||||
|
|
|
@ -36,6 +36,9 @@
|
|||
// You can use the File::GetUserPath() util for this
|
||||
#define USERDATA_DIR "Contents/Resources/User"
|
||||
#define DOLPHIN_DATA_DIR "Library/Application Support/Dolphin"
|
||||
#elif defined ANDROID
|
||||
#define USERDATA_DIR "user"
|
||||
#define DOLPHIN_DATA_DIR "/sdcard/dolphin-emu"
|
||||
#else
|
||||
#define USERDATA_DIR "user"
|
||||
#ifdef USER_DIR
|
||||
|
@ -52,6 +55,8 @@
|
|||
#define SYSDATA_DIR "Contents/Resources/Sys"
|
||||
#define SHARED_USER_DIR File::GetBundleDirectory() + \
|
||||
DIR_SEP USERDATA_DIR DIR_SEP
|
||||
#elif defined ANDROID
|
||||
#define SYSDATA_DIR "/sdcard/dolphin-emu"
|
||||
#else
|
||||
#ifdef DATA_DIR
|
||||
#define SYSDATA_DIR DATA_DIR "sys"
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
#ifndef FPU_ROUND_MODE_H_
|
||||
#define FPU_ROUND_MODE_H_
|
||||
#include "Common.h"
|
||||
|
||||
namespace FPURoundMode
|
||||
{
|
||||
enum RoundModes
|
||||
{
|
||||
ROUND_NEAR = 0,
|
||||
ROUND_CHOP,
|
||||
ROUND_UP,
|
||||
ROUND_DOWN
|
||||
};
|
||||
enum PrecisionModes {
|
||||
PREC_24 = 0,
|
||||
PREC_53,
|
||||
PREC_64
|
||||
};
|
||||
void SetRoundMode(u32 mode);
|
||||
|
||||
void SetPrecisionMode(u32 mode);
|
||||
|
||||
void SetSIMDMode(u32 mode);
|
||||
|
||||
/*
|
||||
There are two different flavors of float to int conversion:
|
||||
_mm_cvtps_epi32() and _mm_cvttps_epi32(). The first rounds
|
||||
according to the MXCSR rounding bits. The second one always
|
||||
uses round towards zero.
|
||||
*/
|
||||
void SaveSIMDState();
|
||||
void LoadSIMDState();
|
||||
void LoadDefaultSIMDState();
|
||||
}
|
||||
#endif
|
|
@ -668,9 +668,10 @@ std::string &GetUserPath(const unsigned int DirIDX, const std::string &newPath)
|
|||
if (File::Exists(ROOT_DIR DIR_SEP USERDATA_DIR))
|
||||
paths[D_USER_IDX] = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP;
|
||||
else
|
||||
paths[D_USER_IDX] = std::string(getenv("HOME") ? getenv("HOME") : getenv("PWD")) + DIR_SEP DOLPHIN_DATA_DIR DIR_SEP;
|
||||
paths[D_USER_IDX] = std::string(getenv("HOME") ?
|
||||
getenv("HOME") : getenv("PWD") ?
|
||||
getenv("PWD") : "") + DIR_SEP DOLPHIN_DATA_DIR DIR_SEP;
|
||||
#endif
|
||||
INFO_LOG(COMMON, "GetUserPath: Setting user directory to %s:", paths[D_USER_IDX].c_str());
|
||||
|
||||
paths[D_GCUSER_IDX] = paths[D_USER_IDX] + GC_USER_DIR DIR_SEP;
|
||||
paths[D_WIIROOT_IDX] = paths[D_USER_IDX] + WII_USER_DIR;
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "FPURoundMode.h"
|
||||
|
||||
// Generic, do nothing
|
||||
namespace FPURoundMode
|
||||
{
|
||||
void SetRoundMode(u32 mode)
|
||||
{
|
||||
}
|
||||
void SetPrecisionMode(u32 mode)
|
||||
{
|
||||
}
|
||||
void SetSIMDMode(u32 mode)
|
||||
{
|
||||
}
|
||||
void SaveSIMDState()
|
||||
{
|
||||
}
|
||||
void LoadSIMDState()
|
||||
{
|
||||
}
|
||||
void LoadDefaultSIMDState()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -17,6 +17,9 @@
|
|||
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef ANDROID
|
||||
#include "Host.h"
|
||||
#endif
|
||||
#include "LogManager.h"
|
||||
#include "ConsoleListener.h"
|
||||
#include "Timer.h"
|
||||
|
@ -132,7 +135,9 @@ void LogManager::Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type,
|
|||
Common::Timer::GetTimeFormatted().c_str(),
|
||||
file, line, level_to_char[(int)level],
|
||||
log->GetShortName(), temp);
|
||||
|
||||
#ifdef ANDROID
|
||||
Host_SysMessage(msg);
|
||||
#endif
|
||||
log->Trigger(level, msg);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,13 +21,6 @@
|
|||
#include <cmath>
|
||||
#include <numeric>
|
||||
|
||||
namespace {
|
||||
|
||||
static u32 saved_sse_state = _mm_getcsr();
|
||||
static const u32 default_sse_state = _mm_getcsr();
|
||||
|
||||
}
|
||||
|
||||
namespace MathUtil
|
||||
{
|
||||
|
||||
|
@ -114,23 +107,6 @@ u32 ClassifyFloat(float fvalue)
|
|||
|
||||
} // namespace
|
||||
|
||||
void LoadDefaultSSEState()
|
||||
{
|
||||
_mm_setcsr(default_sse_state);
|
||||
}
|
||||
|
||||
|
||||
void LoadSSEState()
|
||||
{
|
||||
_mm_setcsr(saved_sse_state);
|
||||
}
|
||||
|
||||
|
||||
void SaveSSEState()
|
||||
{
|
||||
saved_sse_state = _mm_getcsr();
|
||||
}
|
||||
|
||||
inline void MatrixMul(int n, const float *a, const float *b, float *result)
|
||||
{
|
||||
for (int i = 0; i < n; ++i)
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
|
||||
#include "Common.h"
|
||||
|
||||
#include <xmmintrin.h>
|
||||
#include <vector>
|
||||
#include "FPURoundMode.h"
|
||||
|
||||
namespace MathUtil
|
||||
{
|
||||
|
@ -147,17 +147,6 @@ struct Rectangle
|
|||
inline float pow2f(float x) {return x * x;}
|
||||
inline double pow2(double x) {return x * x;}
|
||||
|
||||
|
||||
/*
|
||||
There are two different flavors of float to int conversion:
|
||||
_mm_cvtps_epi32() and _mm_cvttps_epi32(). The first rounds
|
||||
according to the MXCSR rounding bits. The second one always
|
||||
uses round towards zero.
|
||||
*/
|
||||
|
||||
void SaveSSEState();
|
||||
void LoadSSEState();
|
||||
void LoadDefaultSSEState();
|
||||
float MathFloatVectorSum(const std::vector<float>&);
|
||||
|
||||
#define ROUND_UP(x, a) (((x) + (a) - 1) & ~((a) - 1))
|
||||
|
|
|
@ -27,6 +27,10 @@
|
|||
#include <unistd.h>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#ifdef ANDROID
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/ashmem.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
|
@ -34,11 +38,41 @@ static const char* ram_temp_file = "/tmp/gc_mem.tmp";
|
|||
#elif !defined(_WIN32) // non OSX unixes
|
||||
static const char* ram_temp_file = "/dev/shm/gc_mem.tmp";
|
||||
#endif
|
||||
#ifdef ANDROID
|
||||
#define ASHMEM_DEVICE "/dev/ashmem"
|
||||
|
||||
int AshmemCreateFileMapping(const char *name, size_t size)
|
||||
{
|
||||
int fd, ret;
|
||||
fd = open(ASHMEM_DEVICE, O_RDWR);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
// We don't really care if we can't set the name, it is optional
|
||||
ret = ioctl(fd, ASHMEM_SET_NAME, name);
|
||||
|
||||
ret = ioctl(fd, ASHMEM_SET_SIZE, size);
|
||||
if (ret < 0)
|
||||
{
|
||||
close(fd);
|
||||
NOTICE_LOG(MEMMAP, "Ashmem returned error: 0x%08x", ret);
|
||||
return ret;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
#endif
|
||||
|
||||
void MemArena::GrabLowMemSpace(size_t size)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
hMemoryMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, (DWORD)(size), NULL);
|
||||
#elif defined(ANDROID)
|
||||
fd = AshmemCreateFileMapping("Dolphin-emu", size);
|
||||
if (fd < 0)
|
||||
{
|
||||
NOTICE_LOG(MEMMAP, "Ashmem allocation failed");
|
||||
return;
|
||||
}
|
||||
#else
|
||||
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
|
||||
fd = open(ram_temp_file, O_RDWR | O_CREAT, mode);
|
||||
|
|
|
@ -117,9 +117,12 @@ void* AllocateAlignedMemory(size_t size,size_t alignment)
|
|||
void* ptr = _aligned_malloc(size,alignment);
|
||||
#else
|
||||
void* ptr = NULL;
|
||||
#ifdef ANDROID
|
||||
ptr = memalign(alignment, size);
|
||||
#else
|
||||
if (posix_memalign(&ptr, alignment, size) != 0)
|
||||
ERROR_LOG(MEMMAP, "Failed to allocate aligned memory");
|
||||
;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// printf("Mapped memory at %p (size %ld)\n", ptr,
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#define GCC_VER(x,y,z) ((x) * 10000 + (y) * 100 + (z))
|
||||
#define GCC_VERSION GCC_VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
|
||||
|
||||
#if GCC_VERSION >= GCC_VER(4,4,0) && __GXX_EXPERIMENTAL_CXX0X__
|
||||
#if GCC_VERSION >= GCC_VER(4,4,0) && __GXX_EXPERIMENTAL_CXX0X__ && !ANDROID
|
||||
// GCC 4.4 provides <condition_variable>
|
||||
#include <condition_variable>
|
||||
#else
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#define GCC_VER(x,y,z) ((x) * 10000 + (y) * 100 + (z))
|
||||
#define GCC_VERSION GCC_VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
|
||||
|
||||
#if GCC_VERSION >= GCC_VER(4,4,0) && __GXX_EXPERIMENTAL_CXX0X__
|
||||
#if GCC_VERSION >= GCC_VER(4,4,0) && __GXX_EXPERIMENTAL_CXX0X__ && !ANDROID
|
||||
// GCC 4.4 provides <mutex>
|
||||
#include <mutex>
|
||||
#else
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#define GCC_VER(x,y,z) ((x) * 10000 + (y) * 100 + (z))
|
||||
#define GCC_VERSION GCC_VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
|
||||
|
||||
#if GCC_VERSION >= GCC_VER(4,4,0) && __GXX_EXPERIMENTAL_CXX0X__
|
||||
#if GCC_VERSION >= GCC_VER(4,4,0) && __GXX_EXPERIMENTAL_CXX0X__ && !ANDROID
|
||||
// GCC 4.4 provides <thread>
|
||||
#ifndef _GLIBCXX_USE_SCHED_YIELD
|
||||
#define _GLIBCXX_USE_SCHED_YIELD
|
||||
|
|
|
@ -245,7 +245,7 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st
|
|||
while(1)
|
||||
{
|
||||
const int pos = result.find(src);
|
||||
if (pos == -1) break;
|
||||
if (pos == 16) break;
|
||||
result.replace(pos, src.size(), dest);
|
||||
}
|
||||
return result;
|
||||
|
@ -263,25 +263,25 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st
|
|||
const char HEX2DEC[256] =
|
||||
{
|
||||
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
|
||||
/* 0 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
/* 1 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
/* 2 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
/* 3 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1, -1,-1,-1,-1,
|
||||
/* 0 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
|
||||
/* 1 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
|
||||
/* 2 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
|
||||
/* 3 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,16,16, 16,16,16,16,
|
||||
|
||||
/* 4 */ -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
/* 5 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
/* 6 */ -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
/* 7 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
/* 4 */ 16,10,11,12, 13,14,15,16, 16,16,16,16, 16,16,16,16,
|
||||
/* 5 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
|
||||
/* 6 */ 16,10,11,12, 13,14,15,16, 16,16,16,16, 16,16,16,16,
|
||||
/* 7 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
|
||||
|
||||
/* 8 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
/* 9 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
/* A */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
/* B */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
/* 8 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
|
||||
/* 9 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
|
||||
/* A */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
|
||||
/* B */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
|
||||
|
||||
/* C */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
/* D */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
/* E */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
/* F */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1
|
||||
/* C */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
|
||||
/* D */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
|
||||
/* E */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
|
||||
/* F */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16
|
||||
};
|
||||
|
||||
std::string UriDecode(const std::string & sSrc)
|
||||
|
@ -303,8 +303,8 @@ std::string UriDecode(const std::string & sSrc)
|
|||
if (*pSrc == '%')
|
||||
{
|
||||
char dec1, dec2;
|
||||
if (-1 != (dec1 = HEX2DEC[*(pSrc + 1)])
|
||||
&& -1 != (dec2 = HEX2DEC[*(pSrc + 2)]))
|
||||
if (16 != (dec1 = HEX2DEC[*(pSrc + 1)])
|
||||
&& 16 != (dec2 = HEX2DEC[*(pSrc + 2)]))
|
||||
{
|
||||
*pEnd++ = (dec1 << 4) + dec2;
|
||||
pSrc += 3;
|
||||
|
|
|
@ -105,7 +105,7 @@ void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask)
|
|||
#ifdef __APPLE__
|
||||
thread_policy_set(pthread_mach_thread_np(thread),
|
||||
THREAD_AFFINITY_POLICY, (integer_t *)&mask, 1);
|
||||
#elif defined __linux__ || defined BSD4_4
|
||||
#elif (defined __linux__ || defined BSD4_4) && !(defined ANDROID)
|
||||
cpu_set_t cpu_set;
|
||||
CPU_ZERO(&cpu_set);
|
||||
|
||||
|
|
|
@ -33,8 +33,6 @@
|
|||
#define INFINITE 0xffffffff
|
||||
#endif
|
||||
|
||||
#include <xmmintrin.h>
|
||||
|
||||
//for gettimeofday and struct time(spec|val)
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
#include "Common.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
|
||||
using namespace Gen;
|
||||
|
|
@ -30,7 +30,9 @@
|
|||
#else
|
||||
|
||||
//#include <config/i386/cpuid.h>
|
||||
#ifndef _M_GENERIC
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
|
||||
#if defined __FreeBSD__
|
||||
#include <sys/types.h>
|
||||
|
@ -39,7 +41,9 @@
|
|||
static inline void do_cpuid(unsigned int *eax, unsigned int *ebx,
|
||||
unsigned int *ecx, unsigned int *edx)
|
||||
{
|
||||
#ifdef _LP64
|
||||
#if defined _M_GENERIC
|
||||
(*eax) = (*ebx) = (*ecx) = (*edx) = 0;
|
||||
#elif defined _LP64
|
||||
// Note: EBX is reserved on Mac OS X and in PIC on Linux, so it has to
|
||||
// restored at the end of the asm block.
|
||||
__asm__ (
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
#include "Common.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
#include "CPUDetect.h"
|
||||
|
||||
namespace Gen
|
||||
|
|
|
@ -757,7 +757,7 @@ public:
|
|||
region_size = 0;
|
||||
}
|
||||
|
||||
bool IsInCodeSpace(u8 *ptr)
|
||||
bool IsInSpace(u8 *ptr)
|
||||
{
|
||||
return ptr >= region && ptr < region + region_size;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "Common.h"
|
||||
#include "FPURoundMode.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
static const unsigned short FPU_ROUND_NEAR = 0 << 10;
|
||||
static const unsigned short FPU_ROUND_DOWN = 1 << 10;
|
||||
static const unsigned short FPU_ROUND_UP = 2 << 10;
|
||||
static const unsigned short FPU_ROUND_CHOP = 3 << 10;
|
||||
static const unsigned short FPU_ROUND_MASK = 3 << 10;
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
|
||||
const u32 MASKS = 0x1F80; // mask away the interrupts.
|
||||
const u32 DAZ = 0x40;
|
||||
const u32 FTZ = 0x8000;
|
||||
|
||||
namespace FPURoundMode
|
||||
{
|
||||
// Get the default SSE states here.
|
||||
static u32 saved_sse_state = _mm_getcsr();
|
||||
static const u32 default_sse_state = _mm_getcsr();
|
||||
|
||||
void SetRoundMode(u32 mode)
|
||||
{
|
||||
// Set FPU rounding mode to mimic the PowerPC's
|
||||
#ifdef _M_IX86
|
||||
// This shouldn't really be needed anymore since we use SSE
|
||||
#ifdef _WIN32
|
||||
const int table[4] =
|
||||
{
|
||||
_RC_NEAR,
|
||||
_RC_CHOP,
|
||||
_RC_UP,
|
||||
_RC_DOWN
|
||||
};
|
||||
_set_controlfp(_MCW_RC, table[mode]);
|
||||
#else
|
||||
const unsigned short table[4] =
|
||||
{
|
||||
FPU_ROUND_NEAR,
|
||||
FPU_ROUND_CHOP,
|
||||
FPU_ROUND_UP,
|
||||
FPU_ROUND_DOWN
|
||||
};
|
||||
unsigned short _mode;
|
||||
asm ("fstcw %0" : "=m" (_mode) : );
|
||||
_mode = (_mode & ~FPU_ROUND_MASK) | table[mode];
|
||||
asm ("fldcw %0" : : "m" (_mode));
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void SetPrecisionMode(u32 mode)
|
||||
{
|
||||
#ifdef _M_IX86
|
||||
// sets the floating-point lib to 53-bit
|
||||
// PowerPC has a 53bit floating pipeline only
|
||||
// eg: sscanf is very sensitive
|
||||
#ifdef _WIN32
|
||||
_control87(_PC_53, MCW_PC);
|
||||
#else
|
||||
const unsigned short table[4] = {
|
||||
0 << 8, // FPU_PREC_24
|
||||
2 << 8, // FPU_PREC_53
|
||||
3 << 8, // FPU_PREC_64
|
||||
3 << 8, // FPU_PREC_MASK
|
||||
};
|
||||
unsigned short _mode;
|
||||
asm ("fstcw %0" : : "m" (_mode));
|
||||
_mode = (_mode & ~table[4]) | table[mode];
|
||||
asm ("fldcw %0" : : "m" (_mode));
|
||||
#endif
|
||||
#else
|
||||
//x64 doesn't need this - fpu is done with SSE
|
||||
//but still - set any useful sse options here
|
||||
#endif
|
||||
}
|
||||
void SetSIMDMode(u32 mode)
|
||||
{
|
||||
static const u32 ssetable[4] =
|
||||
{
|
||||
(0 << 13) | MASKS,
|
||||
(3 << 13) | MASKS,
|
||||
(2 << 13) | MASKS,
|
||||
(1 << 13) | MASKS,
|
||||
};
|
||||
u32 csr = ssetable[mode];
|
||||
_mm_setcsr(csr);
|
||||
}
|
||||
|
||||
void SaveSIMDState()
|
||||
{
|
||||
saved_sse_state = _mm_getcsr();
|
||||
}
|
||||
void LoadSIMDState()
|
||||
{
|
||||
_mm_setcsr(saved_sse_state);
|
||||
}
|
||||
void LoadDefaultSIMDState()
|
||||
{
|
||||
_mm_setcsr(default_sse_state);
|
||||
}
|
||||
}
|
|
@ -18,9 +18,8 @@
|
|||
#include <map>
|
||||
|
||||
#include "Common.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "MemoryUtil.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
#include "Thunk.h"
|
||||
|
||||
#define THUNK_ARENA_SIZE 1024*1024*1
|
|
@ -9,7 +9,6 @@ set(SRCS Src/ActionReplay.cpp
|
|||
Src/DSPEmulator.cpp
|
||||
Src/GeckoCodeConfig.cpp
|
||||
Src/GeckoCode.cpp
|
||||
Src/MemTools.cpp
|
||||
Src/Movie.cpp
|
||||
Src/NetPlay.cpp
|
||||
Src/NetPlayClient.cpp
|
||||
|
@ -153,6 +152,7 @@ set(SRCS Src/ActionReplay.cpp
|
|||
Src/PowerPC/PPCTables.cpp
|
||||
Src/PowerPC/Profiler.cpp
|
||||
Src/PowerPC/SignatureDB.cpp
|
||||
Src/PowerPC/JitInterface.cpp
|
||||
Src/PowerPC/Interpreter/Interpreter_Branch.cpp
|
||||
Src/PowerPC/Interpreter/Interpreter.cpp
|
||||
Src/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp
|
||||
|
@ -162,6 +162,15 @@ set(SRCS Src/ActionReplay.cpp
|
|||
Src/PowerPC/Interpreter/Interpreter_Paired.cpp
|
||||
Src/PowerPC/Interpreter/Interpreter_SystemRegisters.cpp
|
||||
Src/PowerPC/Interpreter/Interpreter_Tables.cpp
|
||||
Src/PowerPC/JitCommon/JitAsmCommon.cpp
|
||||
Src/PowerPC/JitCommon/JitBackpatch.cpp
|
||||
Src/PowerPC/JitCommon/JitBase.cpp
|
||||
Src/PowerPC/JitCommon/JitCache.cpp
|
||||
Src/PowerPC/JitCommon/Jit_Util.cpp)
|
||||
|
||||
if(NOT _M_GENERIC)
|
||||
set(SRCS ${SRCS}
|
||||
Src/x64MemTools.cpp
|
||||
Src/PowerPC/Jit64IL/IR.cpp
|
||||
Src/PowerPC/Jit64IL/IR_X86.cpp
|
||||
Src/PowerPC/Jit64IL/JitILAsm.cpp
|
||||
|
@ -186,12 +195,25 @@ set(SRCS Src/ActionReplay.cpp
|
|||
Src/PowerPC/Jit64/Jit_LoadStorePaired.cpp
|
||||
Src/PowerPC/Jit64/Jit_Paired.cpp
|
||||
Src/PowerPC/Jit64/JitRegCache.cpp
|
||||
Src/PowerPC/Jit64/Jit_SystemRegisters.cpp
|
||||
Src/PowerPC/JitCommon/JitAsmCommon.cpp
|
||||
Src/PowerPC/JitCommon/JitBackpatch.cpp
|
||||
Src/PowerPC/JitCommon/JitBase.cpp
|
||||
Src/PowerPC/JitCommon/JitCache.cpp
|
||||
Src/PowerPC/JitCommon/Jit_Util.cpp)
|
||||
Src/PowerPC/Jit64/Jit_SystemRegisters.cpp)
|
||||
endif()
|
||||
if(_M_ARM)
|
||||
set(SRCS ${SRCS}
|
||||
Src/ArmMemTools.cpp
|
||||
Src/PowerPC/JitArm32/Jit.cpp
|
||||
Src/PowerPC/JitArm32/JitAsm.cpp
|
||||
Src/PowerPC/JitArm32/JitArm_BackPatch.cpp
|
||||
Src/PowerPC/JitArm32/JitArm_Tables.cpp
|
||||
Src/PowerPC/JitArm32/JitArmCache.cpp
|
||||
Src/PowerPC/JitArm32/JitRegCache.cpp
|
||||
Src/PowerPC/JitArm32/JitFPRCache.cpp
|
||||
Src/PowerPC/JitArm32/JitArm_Branch.cpp
|
||||
Src/PowerPC/JitArm32/JitArm_Integer.cpp
|
||||
Src/PowerPC/JitArm32/JitArm_LoadStore.cpp
|
||||
Src/PowerPC/JitArm32/JitArm_FloatingPoint.cpp
|
||||
Src/PowerPC/JitArm32/JitArm_SystemRegisters.cpp
|
||||
Src/PowerPC/JitArm32/JitArm_LoadStoreFloating.cpp)
|
||||
endif()
|
||||
|
||||
set(LIBS bdisasm inputcommon videosoftware sfml-network)
|
||||
|
||||
|
|
|
@ -332,7 +332,7 @@
|
|||
<ClCompile Include="Src\IPC_HLE\WII_IPC_HLE_Device_usb.cpp" />
|
||||
<ClCompile Include="Src\IPC_HLE\WII_IPC_HLE_Device_usb_kbd.cpp" />
|
||||
<ClCompile Include="Src\IPC_HLE\WII_IPC_HLE_WiiMote.cpp" />
|
||||
<ClCompile Include="Src\MemTools.cpp" />
|
||||
<ClCompile Include="Src\x64MemTools.cpp" />
|
||||
<ClCompile Include="Src\Movie.cpp" />
|
||||
<ClCompile Include="Src\NetPlay.cpp" />
|
||||
<ClCompile Include="Src\NetPlayClient.cpp" />
|
||||
|
@ -378,6 +378,7 @@
|
|||
<ClCompile Include="Src\PowerPC\JitCommon\JitBase.cpp" />
|
||||
<ClCompile Include="Src\PowerPC\JitCommon\JitCache.cpp" />
|
||||
<ClCompile Include="Src\PowerPC\JitCommon\Jit_Util.cpp" />
|
||||
<ClCompile Include="Src\PowerPC\JitInterface.cpp" />
|
||||
<ClCompile Include="Src\PowerPC\LUT_frsqrtex.cpp" />
|
||||
<ClCompile Include="Src\PowerPC\PowerPC.cpp" />
|
||||
<ClCompile Include="Src\PowerPC\PPCAnalyst.cpp" />
|
||||
|
@ -387,10 +388,10 @@
|
|||
<ClCompile Include="Src\PowerPC\Profiler.cpp" />
|
||||
<ClCompile Include="Src\PowerPC\SignatureDB.cpp" />
|
||||
<ClCompile Include="Src\State.cpp">
|
||||
<OpenMPSupport Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'">false</OpenMPSupport>
|
||||
<OpenMPSupport Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</OpenMPSupport>
|
||||
<OpenMPSupport Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</OpenMPSupport>
|
||||
</ClCompile>
|
||||
<OpenMPSupport Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'">false</OpenMPSupport>
|
||||
<OpenMPSupport Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</OpenMPSupport>
|
||||
<OpenMPSupport Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</OpenMPSupport>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Src\stdafx.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
|
@ -563,6 +564,7 @@
|
|||
<ClInclude Include="Src\PowerPC\JitCommon\JitBase.h" />
|
||||
<ClInclude Include="Src\PowerPC\JitCommon\JitCache.h" />
|
||||
<ClInclude Include="Src\PowerPC\JitCommon\Jit_Util.h" />
|
||||
<ClInclude Include="Src\PowerPC\JitInterface.h" />
|
||||
<ClInclude Include="Src\PowerPC\LUT_frsqrtex.h" />
|
||||
<ClInclude Include="Src\PowerPC\PowerPC.h" />
|
||||
<ClInclude Include="Src\PowerPC\PPCAnalyst.h" />
|
||||
|
|
|
@ -5,9 +5,8 @@
|
|||
<ClCompile Include="Src\Console.cpp" />
|
||||
<ClCompile Include="Src\Core.cpp" />
|
||||
<ClCompile Include="Src\CoreParameter.cpp" />
|
||||
<ClCompile Include="Src\CoreRerecording.cpp" />
|
||||
<ClCompile Include="Src\CoreTiming.cpp" />
|
||||
<ClCompile Include="Src\MemTools.cpp" />
|
||||
<ClCompile Include="Src\x64MemTools.cpp" />
|
||||
<ClCompile Include="Src\PatchEngine.cpp" />
|
||||
<ClCompile Include="Src\DSPEmulator.cpp" />
|
||||
<ClCompile Include="Src\State.cpp" />
|
||||
|
@ -562,6 +561,9 @@
|
|||
<ClCompile Include="Src\HW\GCMemcard.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\GCMemcard</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Src\PowerPC\JitInterface.cpp">
|
||||
<Filter>PowerPC</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Src\ConfigManager.h" />
|
||||
|
@ -1048,6 +1050,9 @@
|
|||
<ClInclude Include="Src\HW\GCMemcard.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\GCMemcard</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Src\PowerPC\JitInterface.h">
|
||||
<Filter>PowerPC</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="CMakeLists.txt" />
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#ifdef ANDROID
|
||||
#include <asm/sigcontext.h>
|
||||
#else
|
||||
#include <sys/ucontext.h> // Look in here for the context definition.
|
||||
#include <execinfo.h>
|
||||
#endif
|
||||
|
||||
#include "Common.h"
|
||||
#include "MemTools.h"
|
||||
#include "HW/Memmap.h"
|
||||
#include "PowerPC/PowerPC.h"
|
||||
#include "PowerPC/JitInterface.h"
|
||||
#include "PowerPC/JitCommon/JitBase.h"
|
||||
|
||||
namespace EMM
|
||||
{
|
||||
#ifdef ANDROID
|
||||
typedef struct sigcontext mcontext_t;
|
||||
typedef struct ucontext {
|
||||
uint32_t uc_flags;
|
||||
struct ucontext* uc_link;
|
||||
stack_t uc_stack;
|
||||
mcontext_t uc_mcontext;
|
||||
// Other fields are not used by Google Breakpad. Don't define them.
|
||||
} ucontext_t;
|
||||
#endif
|
||||
|
||||
void sigsegv_handler(int signal, siginfo_t *info, void *raw_context)
|
||||
{
|
||||
if (signal != SIGSEGV)
|
||||
{
|
||||
// We are not interested in other signals - handle it as usual.
|
||||
return;
|
||||
}
|
||||
ucontext_t *context = (ucontext_t *)raw_context;
|
||||
int sicode = info->si_code;
|
||||
if (sicode != SEGV_MAPERR && sicode != SEGV_ACCERR)
|
||||
{
|
||||
// Huh? Return.
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Get all the information we can out of the context.
|
||||
mcontext_t *ctx = &context->uc_mcontext;
|
||||
|
||||
void *fault_memory_ptr = (void*)ctx->arm_r10;
|
||||
u8 *fault_instruction_ptr = (u8 *)ctx->arm_pc;
|
||||
|
||||
if (!JitInterface::IsInCodeSpace(fault_instruction_ptr)) {
|
||||
// Let's not prevent debugging.
|
||||
return;
|
||||
}
|
||||
|
||||
u64 bad_address = (u64)fault_memory_ptr;
|
||||
u64 memspace_bottom = (u64)Memory::base;
|
||||
if (bad_address < memspace_bottom) {
|
||||
PanicAlertT("Exception handler - access below memory space. %08llx%08llx",
|
||||
bad_address >> 32, bad_address);
|
||||
}
|
||||
|
||||
u32 em_address = (u32)(bad_address - memspace_bottom);
|
||||
|
||||
int access_type = 0;
|
||||
|
||||
CONTEXT fake_ctx;
|
||||
fake_ctx.reg_pc = ctx->arm_pc;
|
||||
const u8 *new_rip = jit->BackPatch(fault_instruction_ptr, access_type, em_address, &fake_ctx);
|
||||
if (new_rip) {
|
||||
ctx->arm_pc = fake_ctx.reg_pc;
|
||||
}
|
||||
}
|
||||
|
||||
void InstallExceptionHandler()
|
||||
{
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = 0;
|
||||
sa.sa_sigaction = &sigsegv_handler;
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sigaction(SIGSEGV, &sa, NULL);
|
||||
}
|
||||
} // namespace
|
|
@ -35,7 +35,7 @@
|
|||
#define BACKEND_OPENAL "OpenAL"
|
||||
#define BACKEND_PULSEAUDIO "Pulse"
|
||||
#define BACKEND_XAUDIO2 "XAudio2"
|
||||
|
||||
#define BACKEND_OPENSLES "OpenSLES"
|
||||
struct SConfig : NonCopyable
|
||||
{
|
||||
// Wii Devices
|
||||
|
|
|
@ -54,7 +54,6 @@
|
|||
#include "IPC_HLE/WII_IPC_HLE_Device_usb.h"
|
||||
|
||||
#include "PowerPC/PowerPC.h"
|
||||
#include "PowerPC/JitCommon/JitBase.h"
|
||||
|
||||
#include "DSPEmulator.h"
|
||||
#include "ConfigManager.h"
|
||||
|
@ -140,7 +139,7 @@ void DisplayMessage(const char *message, int time_in_ms)
|
|||
if (_CoreParameter.bRenderToMain &&
|
||||
SConfig::GetInstance().m_InterfaceStatusbar)
|
||||
{
|
||||
Host_UpdateStatusBar(message);
|
||||
Host_UpdateStatusBar(message);
|
||||
}
|
||||
else
|
||||
Host_UpdateTitle(message);
|
||||
|
@ -189,7 +188,7 @@ bool IsGPUThread()
|
|||
return IsCPUThread();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// This is called from the GUI thread. See the booting call schedule in
|
||||
// BootManager.cpp
|
||||
bool Init()
|
||||
|
@ -310,7 +309,7 @@ void CpuThread()
|
|||
g_video_backend->Video_Prepare();
|
||||
}
|
||||
|
||||
#if defined(_M_X64)
|
||||
#if defined(_M_X64) || _M_ARM
|
||||
EMM::InstallExceptionHandler(); // Let's run under memory watch
|
||||
#endif
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#include "DSPAnalyzer.h"
|
||||
#include "Jit/DSPJitUtil.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
|
||||
#define MAX_BLOCK_SIZE 250
|
||||
#define DSP_IDLE_SKIP_CYCLES 0x1000
|
||||
|
|
|
@ -252,7 +252,9 @@ static void gdsp_idma_out(u16 dsp_addr, u32 addr, u32 size)
|
|||
ERROR_LOG(DSPLLE, "*** idma_out IRAM_DSP (0x%04x) -> RAM (0x%08x) : size (0x%08x)", dsp_addr / 2, addr, size);
|
||||
}
|
||||
|
||||
#if _M_SSE >= 0x301
|
||||
static const __m128i s_mask = _mm_set_epi32(0x0E0F0C0DL, 0x0A0B0809L, 0x06070405L, 0x02030001L);
|
||||
#endif
|
||||
|
||||
// TODO: These should eat clock cycles.
|
||||
static void gdsp_ddma_in(u16 dsp_addr, u32 addr, u32 size)
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#include "DSPJitUtil.h"
|
||||
#endif
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
using namespace Gen;
|
||||
|
||||
// CLR $acR
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
#include "../DSPAnalyzer.h"
|
||||
#include "DSPJitUtil.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
|
||||
using namespace Gen;
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
#include "../DSPEmitter.h"
|
||||
#include "DSPJitUtil.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
using namespace Gen;
|
||||
|
||||
// In: RAX: s64 _Value
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#include "../DSPEmitter.h"
|
||||
#include "DSPJitUtil.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
|
||||
using namespace Gen;
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include "../DSPEmitter.h"
|
||||
#include "DSPJitUtil.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
using namespace Gen;
|
||||
|
||||
// SRS @M, $(0x18+S)
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#include "../DSPEmitter.h"
|
||||
#include "DSPJitUtil.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
using namespace Gen;
|
||||
|
||||
//clobbers:
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
#include "DSPJitUtil.h"
|
||||
#endif
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
using namespace Gen;
|
||||
|
||||
// Returns s64 in RAX
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#include "../DSPEmitter.h"
|
||||
#include "DSPJitUtil.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
|
||||
using namespace Gen;
|
||||
|
||||
|
|
|
@ -419,132 +419,6 @@ u32 Read_Instruction(const u32 em_address)
|
|||
return inst.hex;
|
||||
}
|
||||
|
||||
u32 Read_Opcode_JIT_Uncached(const u32 _Address)
|
||||
{
|
||||
u8* iCache;
|
||||
u32 addr;
|
||||
if (_Address & JIT_ICACHE_VMEM_BIT)
|
||||
{
|
||||
iCache = jit->GetBlockCache()->GetICacheVMEM();
|
||||
addr = _Address & JIT_ICACHE_MASK;
|
||||
}
|
||||
else if (_Address & JIT_ICACHE_EXRAM_BIT)
|
||||
{
|
||||
iCache = jit->GetBlockCache()->GetICacheEx();
|
||||
addr = _Address & JIT_ICACHEEX_MASK;
|
||||
}
|
||||
else
|
||||
{
|
||||
iCache = jit->GetBlockCache()->GetICache();
|
||||
addr = _Address & JIT_ICACHE_MASK;
|
||||
}
|
||||
u32 inst = *(u32*)(iCache + addr);
|
||||
if (inst == JIT_ICACHE_INVALID_WORD)
|
||||
{
|
||||
u32 cache_block_start = addr & ~0x1f;
|
||||
u32 mem_block_start = _Address & ~0x1f;
|
||||
u8 *pMem = Memory::GetPointer(mem_block_start);
|
||||
memcpy(iCache + cache_block_start, pMem, 32);
|
||||
inst = *(u32*)(iCache + addr);
|
||||
}
|
||||
inst = Common::swap32(inst);
|
||||
|
||||
if ((inst & 0xfc000000) == 0)
|
||||
{
|
||||
inst = jit->GetBlockCache()->GetOriginalFirstOp(inst);
|
||||
}
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
u32 Read_Opcode_JIT(u32 _Address)
|
||||
{
|
||||
#ifdef FAST_ICACHE
|
||||
if (bMMU && !bFakeVMEM && (_Address & ADDR_MASK_MEM1))
|
||||
{
|
||||
_Address = Memory::TranslateAddress(_Address, FLAG_OPCODE);
|
||||
if (_Address == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
u32 inst = 0;
|
||||
|
||||
// Bypass the icache for the external interrupt exception handler
|
||||
if ( (_Address & 0x0FFFFF00) == 0x00000500 )
|
||||
inst = Read_Opcode_JIT_Uncached(_Address);
|
||||
else
|
||||
inst = PowerPC::ppcState.iCache.ReadInstruction(_Address);
|
||||
#else
|
||||
u32 inst = Memory::ReadUnchecked_U32(_Address);
|
||||
#endif
|
||||
return inst;
|
||||
}
|
||||
|
||||
// The following function is deprecated in favour of FAST_ICACHE
|
||||
u32 Read_Opcode_JIT_LC(const u32 _Address)
|
||||
{
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
if ((_Address & ~JIT_ICACHE_MASK) != 0x80000000 && (_Address & ~JIT_ICACHE_MASK) != 0x00000000 &&
|
||||
(_Address & ~JIT_ICACHE_MASK) != 0x7e000000 && // TLB area
|
||||
(_Address & ~JIT_ICACHEEX_MASK) != 0x90000000 && (_Address & ~JIT_ICACHEEX_MASK) != 0x10000000)
|
||||
{
|
||||
PanicAlertT("iCacheJIT: Reading Opcode from %x. Please report.", _Address);
|
||||
ERROR_LOG(MEMMAP, "iCacheJIT: Reading Opcode from %x. Please report.", _Address);
|
||||
return 0;
|
||||
}
|
||||
u8* iCache;
|
||||
u32 addr;
|
||||
if (_Address & JIT_ICACHE_VMEM_BIT)
|
||||
{
|
||||
iCache = jit->GetBlockCache()->GetICacheVMEM();
|
||||
addr = _Address & JIT_ICACHE_MASK;
|
||||
}
|
||||
else if (_Address & JIT_ICACHE_EXRAM_BIT)
|
||||
{
|
||||
iCache = jit->GetBlockCache()->GetICacheEx();
|
||||
addr = _Address & JIT_ICACHEEX_MASK;
|
||||
}
|
||||
else
|
||||
{
|
||||
iCache = jit->GetBlockCache()->GetICache();
|
||||
addr = _Address & JIT_ICACHE_MASK;
|
||||
}
|
||||
u32 inst = *(u32*)(iCache + addr);
|
||||
if (inst == JIT_ICACHE_INVALID_WORD)
|
||||
inst = Memory::ReadUnchecked_U32(_Address);
|
||||
else
|
||||
inst = Common::swap32(inst);
|
||||
#else
|
||||
u32 inst = Memory::ReadUnchecked_U32(_Address);
|
||||
#endif
|
||||
if ((inst & 0xfc000000) == 0)
|
||||
{
|
||||
inst = jit->GetBlockCache()->GetOriginalFirstOp(inst);
|
||||
}
|
||||
return inst;
|
||||
}
|
||||
|
||||
// WARNING! No checks!
|
||||
// We assume that _Address is cached
|
||||
void Write_Opcode_JIT(const u32 _Address, const u32 _Value)
|
||||
{
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
if (_Address & JIT_ICACHE_VMEM_BIT)
|
||||
{
|
||||
*(u32*)(jit->GetBlockCache()->GetICacheVMEM() + (_Address & JIT_ICACHE_MASK)) = Common::swap32(_Value);
|
||||
}
|
||||
else if (_Address & JIT_ICACHE_EXRAM_BIT)
|
||||
{
|
||||
*(u32*)(jit->GetBlockCache()->GetICacheEx() + (_Address & JIT_ICACHEEX_MASK)) = Common::swap32(_Value);
|
||||
}
|
||||
else
|
||||
*(u32*)(jit->GetBlockCache()->GetICache() + (_Address & JIT_ICACHE_MASK)) = Common::swap32(_Value);
|
||||
#else
|
||||
Memory::WriteUnchecked_U32(_Value, _Address);
|
||||
#endif
|
||||
}
|
||||
|
||||
void WriteBigEData(const u8 *_pData, const u32 _Address, const u32 _iSize)
|
||||
{
|
||||
memcpy(GetPointer(_Address), _pData, _iSize);
|
||||
|
|
|
@ -119,11 +119,6 @@ inline u32 ReadFast32(const u32 _Address)
|
|||
|
||||
// used by interpreter to read instructions, uses iCache
|
||||
u32 Read_Opcode(const u32 _Address);
|
||||
// used by JIT to read instructions
|
||||
u32 Read_Opcode_JIT(const u32 _Address);
|
||||
// used by JIT. uses iCacheJIT. Reads in the "Locked cache" mode
|
||||
u32 Read_Opcode_JIT_LC(const u32 _Address);
|
||||
void Write_Opcode_JIT(const u32 _Address, const u32 _Value);
|
||||
// this is used by Debugger a lot.
|
||||
// For now, just reads from memory!
|
||||
u32 Read_Instruction(const u32 _Address);
|
||||
|
|
|
@ -28,8 +28,6 @@
|
|||
#undef _interlockedbittestandreset
|
||||
#undef _interlockedbittestandset64
|
||||
#undef _interlockedbittestandreset64
|
||||
#else
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
|
||||
#include "../../Core.h"
|
||||
|
@ -514,4 +512,4 @@ void Interpreter::fsqrtx(UGeckoInstruction _inst)
|
|||
rPS0(_inst.FD) = sqrt(b);
|
||||
UpdateFPRF(rPS0(_inst.FD));
|
||||
if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,8 +24,7 @@
|
|||
#include "Interpreter.h"
|
||||
#include "../../Core.h"
|
||||
|
||||
#include "../JitCommon/JitBase.h"
|
||||
#include "../JitCommon/JitCache.h"
|
||||
#include "../JitInterface.h"
|
||||
|
||||
#include "Interpreter_FPUtils.h"
|
||||
|
||||
|
@ -363,34 +362,24 @@ void Interpreter::dcbf(UGeckoInstruction _inst)
|
|||
{
|
||||
NPC = PC + 12;
|
||||
}*/
|
||||
// Invalidate the jit block cache on dcbf
|
||||
if (jit)
|
||||
{
|
||||
u32 address = Helper_Get_EA_X(_inst);
|
||||
jit->GetBlockCache()->InvalidateICache(address & ~0x1f, 32);
|
||||
}
|
||||
JitInterface::InvalidateICache(address & ~0x1f, 32);
|
||||
}
|
||||
|
||||
void Interpreter::dcbi(UGeckoInstruction _inst)
|
||||
{
|
||||
// Removes a block from data cache. Since we don't emulate the data cache, we don't need to do anything to the data cache
|
||||
// However, we invalidate the jit block cache on dcbi
|
||||
if (jit)
|
||||
{
|
||||
u32 address = Helper_Get_EA_X(_inst);
|
||||
jit->GetBlockCache()->InvalidateICache(address & ~0x1f, 32);
|
||||
}
|
||||
JitInterface::InvalidateICache(address & ~0x1f, 32);
|
||||
}
|
||||
|
||||
void Interpreter::dcbst(UGeckoInstruction _inst)
|
||||
{
|
||||
// Cache line flush. Since we don't emulate the data cache, we don't need to do anything.
|
||||
// Invalidate the jit block cache on dcbst in case new code has been loaded via the data cache
|
||||
if (jit)
|
||||
{
|
||||
u32 address = Helper_Get_EA_X(_inst);
|
||||
jit->GetBlockCache()->InvalidateICache(address & ~0x1f, 32);
|
||||
}
|
||||
JitInterface::InvalidateICache(address & ~0x1f, 32);
|
||||
}
|
||||
|
||||
void Interpreter::dcbt(UGeckoInstruction _inst)
|
||||
|
@ -409,7 +398,7 @@ void Interpreter::dcbz(UGeckoInstruction _inst)
|
|||
// HACK but works... we think
|
||||
if (!Core::g_CoreStartupParameter.bDCBZOFF)
|
||||
Memory::Memset(Helper_Get_EA_X(_inst) & (~31), 0, 32);
|
||||
if (!jit)
|
||||
if (!JitInterface::GetCore())
|
||||
PowerPC::CheckExceptions();
|
||||
}
|
||||
|
||||
|
|
|
@ -26,13 +26,6 @@
|
|||
#undef _interlockedbittestandreset
|
||||
#undef _interlockedbittestandset64
|
||||
#undef _interlockedbittestandreset64
|
||||
#else
|
||||
static const unsigned short FPU_ROUND_NEAR = 0 << 10;
|
||||
static const unsigned short FPU_ROUND_DOWN = 1 << 10;
|
||||
static const unsigned short FPU_ROUND_UP = 2 << 10;
|
||||
static const unsigned short FPU_ROUND_CHOP = 3 << 10;
|
||||
static const unsigned short FPU_ROUND_MASK = 3 << 10;
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
|
||||
#include "CPUDetect.h"
|
||||
|
@ -43,6 +36,7 @@ static const unsigned short FPU_ROUND_MASK = 3 << 10;
|
|||
#include "../../HW/SystemTimers.h"
|
||||
#include "../../Core.h"
|
||||
#include "Interpreter.h"
|
||||
#include "FPURoundMode.h"
|
||||
|
||||
#include "Interpreter_FPUtils.h"
|
||||
|
||||
|
@ -61,38 +55,11 @@ mffsx: 80036650 (huh?)
|
|||
// That is, set rounding mode etc when entering jit code or the interpreter loop
|
||||
// Restore rounding mode when calling anything external
|
||||
|
||||
const u32 MASKS = 0x1F80; // mask away the interrupts.
|
||||
const u32 DAZ = 0x40;
|
||||
const u32 FTZ = 0x8000;
|
||||
|
||||
static void FPSCRtoFPUSettings(UReg_FPSCR fp)
|
||||
{
|
||||
// Set FPU rounding mode to mimic the PowerPC's
|
||||
#ifdef _M_IX86
|
||||
// This shouldn't really be needed anymore since we use SSE
|
||||
#ifdef _WIN32
|
||||
const int table[4] =
|
||||
{
|
||||
_RC_NEAR,
|
||||
_RC_CHOP,
|
||||
_RC_UP,
|
||||
_RC_DOWN
|
||||
};
|
||||
_set_controlfp(_MCW_RC, table[fp.RN]);
|
||||
#else
|
||||
const unsigned short table[4] =
|
||||
{
|
||||
FPU_ROUND_NEAR,
|
||||
FPU_ROUND_CHOP,
|
||||
FPU_ROUND_UP,
|
||||
FPU_ROUND_DOWN
|
||||
};
|
||||
unsigned short mode;
|
||||
asm ("fstcw %0" : "=m" (mode) : );
|
||||
mode = (mode & ~FPU_ROUND_MASK) | table[fp.RN];
|
||||
asm ("fldcw %0" : : "m" (mode));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
FPURoundMode::SetRoundMode(fp.RN);
|
||||
|
||||
if (fp.VE || fp.OE || fp.UE || fp.ZE || fp.XE)
|
||||
{
|
||||
//PanicAlert("FPSCR - exceptions enabled. Please report. VE=%i OE=%i UE=%i ZE=%i XE=%i",
|
||||
|
@ -101,14 +68,6 @@ static void FPSCRtoFPUSettings(UReg_FPSCR fp)
|
|||
}
|
||||
|
||||
// Also corresponding SSE rounding mode setting
|
||||
static const u32 ssetable[4] =
|
||||
{
|
||||
(0 << 13) | MASKS,
|
||||
(3 << 13) | MASKS,
|
||||
(2 << 13) | MASKS,
|
||||
(1 << 13) | MASKS,
|
||||
};
|
||||
u32 csr = ssetable[FPSCR.RN];
|
||||
if (FPSCR.NI)
|
||||
{
|
||||
// Either one of these two breaks Beyond Good & Evil.
|
||||
|
@ -116,7 +75,7 @@ static void FPSCRtoFPUSettings(UReg_FPSCR fp)
|
|||
// csr |= DAZ;
|
||||
// csr |= FTZ;
|
||||
}
|
||||
_mm_setcsr(csr);
|
||||
FPURoundMode::SetSIMDMode(FPSCR.RN);
|
||||
}
|
||||
|
||||
void Interpreter::mtfsb0x(UGeckoInstruction _inst)
|
||||
|
|
|
@ -267,7 +267,7 @@ static GekkoOPTemplate table31[] =
|
|||
{19, Interpreter::mfcr, {"mfcr", OPTYPE_SYSTEM, FL_OUT_D, 0, 0, 0, 0}},
|
||||
{83, Interpreter::mfmsr, {"mfmsr", OPTYPE_SYSTEM, FL_OUT_D, 0, 0, 0, 0}},
|
||||
{144, Interpreter::mtcrf, {"mtcrf", OPTYPE_SYSTEM, 0, 0, 0, 0, 0}},
|
||||
{146, Interpreter::mtmsr, {"mtmsr", OPTYPE_SYSTEM, FL_ENDBLOCK, 0, 0, 0, 0}},
|
||||
{146, Interpreter::mtmsr, {"mtmsr", OPTYPE_SYSTEM, FL_IN_S | FL_ENDBLOCK, 0, 0, 0, 0}},
|
||||
{210, Interpreter::mtsr, {"mtsr", OPTYPE_SYSTEM, 0, 0, 0, 0, 0}},
|
||||
{242, Interpreter::mtsrin, {"mtsrin", OPTYPE_SYSTEM, 0, 0, 0, 0, 0}},
|
||||
{339, Interpreter::mfspr, {"mfspr", OPTYPE_SPR, FL_OUT_D, 0, 0, 0, 0}},
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
#include "Common.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
#include "Thunk.h"
|
||||
#include "../../HLE/HLE.h"
|
||||
#include "../../Core.h"
|
||||
|
@ -357,7 +357,7 @@ void Jit64::WriteExternalExceptionExit()
|
|||
MOV(32, R(EAX), M(&PC));
|
||||
MOV(32, M(&NPC), R(EAX));
|
||||
ABI_CallFunction(reinterpret_cast<void *>(&PowerPC::CheckExternalExceptions));
|
||||
SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount));
|
||||
SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount));
|
||||
JMP(asm_routines.dispatcher, true);
|
||||
}
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
if (js.memcheck) \
|
||||
SetJumpTarget(memException);
|
||||
|
||||
class Jit64 : public JitBase
|
||||
class Jit64 : public Jitx86Base
|
||||
{
|
||||
private:
|
||||
GPRRegCache gpr;
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
#include "x64Emitter.h"
|
||||
|
||||
#include "../../HW/Memmap.h"
|
||||
|
@ -24,7 +24,7 @@
|
|||
#include "../../CoreTiming.h"
|
||||
#include "MemoryUtil.h"
|
||||
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
#include "Jit.h"
|
||||
#include "../JitCommon/JitCache.h"
|
||||
|
||||
|
@ -204,7 +204,7 @@ void Jit64AsmRoutineManager::Generate()
|
|||
MOV(32, M(&NPC), R(EAX));
|
||||
ABI_CallFunction(reinterpret_cast<void *>(&PowerPC::CheckExternalExceptions));
|
||||
SetJumpTarget(noExtException);
|
||||
|
||||
|
||||
TEST(32, M((void*)PowerPC::GetStatePtr()), Imm32(0xFFFFFFFF));
|
||||
J_CC(CC_Z, outerLoop, true);
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
#ifndef _JIT64ASM_H
|
||||
#define _JIT64ASM_H
|
||||
|
||||
#include "x64Emitter.h"
|
||||
#include "../JitCommon/JitAsmCommon.h"
|
||||
|
||||
// In Dolphin, we don't use inline assembly. Instead, we generate all machine-near
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#include "../../HW/Memmap.h"
|
||||
#include "../PPCTables.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
|
||||
#include "Jit.h"
|
||||
#include "JitAsm.h"
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
#include "../PPCTables.h"
|
||||
#include "CPUDetect.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
|
||||
#include "Jit.h"
|
||||
#include "JitAsm.h"
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#include "../PPCTables.h"
|
||||
#include "CPUDetect.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
|
||||
#include "Jit.h"
|
||||
#include "JitAsm.h"
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
#include "../PowerPC.h"
|
||||
#include "../PPCTables.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
#include "Thunk.h"
|
||||
|
||||
#include "Jit.h"
|
||||
|
|
|
@ -1991,8 +1991,10 @@ void JitIL::WriteCode() {
|
|||
}
|
||||
|
||||
void ProfiledReJit() {
|
||||
jit->SetCodePtr(jit->js.rewriteStart);
|
||||
DoWriteCode(&((JitIL *)jit)->ibuild, (JitIL *)jit, true, false);
|
||||
jit->js.curBlock->codeSize = (int)(jit->GetCodePtr() - jit->js.rewriteStart);
|
||||
jit->GetBlockCache()->FinalizeBlock(jit->js.curBlock->blockNum, jit->jo.enableBlocklink, jit->js.curBlock->normalEntry);
|
||||
JitIL *jitil = (JitIL *)jit;
|
||||
jitil->SetCodePtr(jitil->js.rewriteStart);
|
||||
DoWriteCode(&jitil->ibuild, jitil, true, false);
|
||||
jitil->js.curBlock->codeSize = (int)(jitil->GetCodePtr() - jitil->js.rewriteStart);
|
||||
jitil->GetBlockCache()->FinalizeBlock(jitil->js.curBlock->blockNum, jitil->jo.enableBlocklink,
|
||||
jitil->js.curBlock->normalEntry);
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
#include "Common.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
#include "Thunk.h"
|
||||
#include "../../HLE/HLE.h"
|
||||
#include "../../Core.h"
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
#define DISABLE64
|
||||
#endif
|
||||
|
||||
class JitIL : public JitBase
|
||||
class JitIL : public Jitx86Base
|
||||
{
|
||||
private:
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
#include "x64Emitter.h"
|
||||
|
||||
#include "../../HW/Memmap.h"
|
||||
|
@ -25,7 +25,7 @@
|
|||
#include "MemoryUtil.h"
|
||||
#include "CPUDetect.h"
|
||||
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
#include "Thunk.h"
|
||||
|
||||
#include "../../HW/GPFifo.h"
|
||||
|
@ -211,14 +211,14 @@ void JitILAsmRoutineManager::Generate()
|
|||
doTiming = GetCodePtr();
|
||||
|
||||
ABI_CallFunction(reinterpret_cast<void *>(&CoreTiming::Advance));
|
||||
|
||||
|
||||
testExceptions = GetCodePtr();
|
||||
MOV(32, R(EAX), M(&PC));
|
||||
MOV(32, M(&NPC), R(EAX));
|
||||
ABI_CallFunction(reinterpret_cast<void *>(&PowerPC::CheckExceptions));
|
||||
MOV(32, R(EAX), M(&NPC));
|
||||
MOV(32, M(&PC), R(EAX));
|
||||
|
||||
|
||||
TEST(32, M((void*)PowerPC::GetStatePtr()), Imm32(0xFFFFFFFF));
|
||||
J_CC(CC_Z, outer_loop, true);
|
||||
//Landing pad for drec space
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
#include "../../HW/Memmap.h"
|
||||
#include "../PPCTables.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
|
||||
#include "JitIL.h"
|
||||
#include "JitILAsm.h"
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
#include "../PPCTables.h"
|
||||
#include "CPUDetect.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
|
||||
#include "JitIL.h"
|
||||
#include "JitILAsm.h"
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#include "../PPCTables.h"
|
||||
#include "CPUDetect.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
|
||||
#include "JitIL.h"
|
||||
#include "JitILAsm.h"
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
#include "../PowerPC.h"
|
||||
#include "../PPCTables.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
#include "Thunk.h"
|
||||
|
||||
#include "JitIL.h"
|
||||
|
|
|
@ -0,0 +1,498 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "Common.h"
|
||||
#include "../../HLE/HLE.h"
|
||||
#include "../../Core.h"
|
||||
#include "../../PatchEngine.h"
|
||||
#include "../../CoreTiming.h"
|
||||
#include "../../ConfigManager.h"
|
||||
#include "../PowerPC.h"
|
||||
#include "../Profiler.h"
|
||||
#include "../PPCTables.h"
|
||||
#include "../PPCAnalyst.h"
|
||||
#include "../../HW/Memmap.h"
|
||||
#include "../../HW/GPFifo.h"
|
||||
#include "Jit.h"
|
||||
#include "JitArm_Tables.h"
|
||||
#include "ArmEmitter.h"
|
||||
#include "../JitInterface.h"
|
||||
|
||||
using namespace ArmGen;
|
||||
using namespace PowerPC;
|
||||
|
||||
static int CODE_SIZE = 1024*1024*32;
|
||||
namespace CPUCompare
|
||||
{
|
||||
extern u32 m_BlockStart;
|
||||
}
|
||||
|
||||
void JitArm::Init()
|
||||
{
|
||||
AllocCodeSpace(CODE_SIZE);
|
||||
blocks.Init();
|
||||
asm_routines.Init();
|
||||
// TODO: Investigate why the register cache crashes when only doing Init with
|
||||
// the pointer to this. Seems for some reason it doesn't set the emitter pointer
|
||||
// In the class for some reason?
|
||||
gpr.Init(this);
|
||||
gpr.SetEmitter(this);
|
||||
fpr.Init(this);
|
||||
fpr.SetEmitter(this);
|
||||
jo.enableBlocklink = true;
|
||||
jo.optimizeGatherPipe = false;
|
||||
}
|
||||
|
||||
void JitArm::ClearCache()
|
||||
{
|
||||
ClearCodeSpace();
|
||||
blocks.Clear();
|
||||
}
|
||||
|
||||
void JitArm::Shutdown()
|
||||
{
|
||||
FreeCodeSpace();
|
||||
blocks.Shutdown();
|
||||
asm_routines.Shutdown();
|
||||
}
|
||||
|
||||
// This is only called by Default() in this file. It will execute an instruction with the interpreter functions.
|
||||
void JitArm::WriteCallInterpreter(UGeckoInstruction inst)
|
||||
{
|
||||
gpr.Flush();
|
||||
fpr.Flush();
|
||||
Interpreter::_interpreterInstruction instr = GetInterpreterOp(inst);
|
||||
MOVI2R(R0, inst.hex);
|
||||
MOVI2R(R12, (u32)instr);
|
||||
BL(R12);
|
||||
}
|
||||
void JitArm::unknown_instruction(UGeckoInstruction inst)
|
||||
{
|
||||
PanicAlert("unknown_instruction %08x - Fix me ;)", inst.hex);
|
||||
}
|
||||
|
||||
void JitArm::Default(UGeckoInstruction _inst)
|
||||
{
|
||||
WriteCallInterpreter(_inst.hex);
|
||||
}
|
||||
|
||||
void JitArm::HLEFunction(UGeckoInstruction _inst)
|
||||
{
|
||||
gpr.Flush();
|
||||
fpr.Flush();
|
||||
MOVI2R(R0, js.compilerPC);
|
||||
MOVI2R(R1, _inst.hex);
|
||||
QuickCallFunction(R14, (void*)&HLE::Execute);
|
||||
ARMReg rA = gpr.GetReg();
|
||||
LDR(rA, R9, STRUCT_OFF(PowerPC::ppcState, npc));
|
||||
WriteExitDestInR(rA);
|
||||
}
|
||||
|
||||
void JitArm::DoNothing(UGeckoInstruction _inst)
|
||||
{
|
||||
// Yup, just don't do anything.
|
||||
}
|
||||
|
||||
static const bool ImHereDebug = false;
|
||||
static const bool ImHereLog = false;
|
||||
static std::map<u32, int> been_here;
|
||||
|
||||
static void ImHere()
|
||||
{
|
||||
static File::IOFile f;
|
||||
if (ImHereLog)
|
||||
{
|
||||
if (!f)
|
||||
{
|
||||
f.Open("log32.txt", "w");
|
||||
}
|
||||
fprintf(f.GetHandle(), "%08x\n", PC);
|
||||
}
|
||||
if (been_here.find(PC) != been_here.end())
|
||||
{
|
||||
been_here.find(PC)->second++;
|
||||
if ((been_here.find(PC)->second) & 1023)
|
||||
return;
|
||||
}
|
||||
DEBUG_LOG(DYNA_REC, "I'm here - PC = %08x , LR = %08x", PC, LR);
|
||||
been_here[PC] = 1;
|
||||
}
|
||||
|
||||
void JitArm::Cleanup()
|
||||
{
|
||||
if (jo.optimizeGatherPipe && js.fifoBytesThisBlock > 0)
|
||||
QuickCallFunction(R14, (void*)&GPFifo::CheckGatherPipe);
|
||||
}
|
||||
void JitArm::DoDownCount()
|
||||
{
|
||||
ARMReg rA = gpr.GetReg();
|
||||
ARMReg rB = gpr.GetReg();
|
||||
MOVI2R(rA, (u32)&CoreTiming::downcount);
|
||||
LDR(rB, rA);
|
||||
if(js.downcountAmount < 255) // We can enlarge this if we used rotations
|
||||
{
|
||||
SUBS(rB, rB, js.downcountAmount);
|
||||
STR(rA, rB);
|
||||
}
|
||||
else
|
||||
{
|
||||
ARMReg rC = gpr.GetReg(false);
|
||||
MOVI2R(rC, js.downcountAmount);
|
||||
SUBS(rB, rB, rC);
|
||||
STR(rA, rB);
|
||||
}
|
||||
gpr.Unlock(rA, rB);
|
||||
}
|
||||
void JitArm::WriteExitDestInR(ARMReg Reg)
|
||||
{
|
||||
STR(R9, Reg, STRUCT_OFF(PowerPC::ppcState, pc));
|
||||
Cleanup();
|
||||
DoDownCount();
|
||||
MOVI2R(Reg, (u32)asm_routines.dispatcher);
|
||||
B(Reg);
|
||||
gpr.Unlock(Reg);
|
||||
}
|
||||
void JitArm::WriteRfiExitDestInR(ARMReg Reg)
|
||||
{
|
||||
STR(R9, Reg, STRUCT_OFF(PowerPC::ppcState, pc));
|
||||
Cleanup();
|
||||
DoDownCount();
|
||||
|
||||
MOVI2R(Reg, (u32)asm_routines.testExceptions);
|
||||
B(Reg);
|
||||
gpr.Unlock(Reg); // This was locked in the instruction beforehand
|
||||
}
|
||||
void JitArm::WriteExceptionExit()
|
||||
{
|
||||
ARMReg A = gpr.GetReg(false);
|
||||
Cleanup();
|
||||
DoDownCount();
|
||||
|
||||
MOVI2R(A, (u32)asm_routines.testExceptions);
|
||||
B(A);
|
||||
}
|
||||
void JitArm::WriteExit(u32 destination, int exit_num)
|
||||
{
|
||||
Cleanup();
|
||||
|
||||
DoDownCount();
|
||||
//If nobody has taken care of this yet (this can be removed when all branches are done)
|
||||
JitBlock *b = js.curBlock;
|
||||
b->exitAddress[exit_num] = destination;
|
||||
b->exitPtrs[exit_num] = GetWritableCodePtr();
|
||||
|
||||
// Link opportunity!
|
||||
int block = blocks.GetBlockNumberFromStartAddress(destination);
|
||||
if (block >= 0 && jo.enableBlocklink)
|
||||
{
|
||||
// It exists! Joy of joy!
|
||||
B(blocks.GetBlock(block)->checkedEntry);
|
||||
b->linkStatus[exit_num] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ARMReg A = gpr.GetReg(false);
|
||||
MOVI2R(A, destination);
|
||||
STR(R9, A, STRUCT_OFF(PowerPC::ppcState, pc));
|
||||
MOVI2R(A, (u32)asm_routines.dispatcher);
|
||||
B(A);
|
||||
}
|
||||
}
|
||||
|
||||
void STACKALIGN JitArm::Run()
|
||||
{
|
||||
CompiledCode pExecAddr = (CompiledCode)asm_routines.enterCode;
|
||||
pExecAddr();
|
||||
}
|
||||
|
||||
void JitArm::SingleStep()
|
||||
{
|
||||
CompiledCode pExecAddr = (CompiledCode)asm_routines.enterCode;
|
||||
pExecAddr();
|
||||
}
|
||||
|
||||
void JitArm::Trace()
|
||||
{
|
||||
char regs[500] = "";
|
||||
char fregs[750] = "";
|
||||
|
||||
#ifdef JIT_LOG_GPR
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
char reg[50];
|
||||
sprintf(reg, "r%02d: %08x ", i, PowerPC::ppcState.gpr[i]);
|
||||
strncat(regs, reg, 500);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef JIT_LOG_FPR
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
char reg[50];
|
||||
sprintf(reg, "f%02d: %016x ", i, riPS0(i));
|
||||
strncat(fregs, reg, 750);
|
||||
}
|
||||
#endif
|
||||
|
||||
DEBUG_LOG(DYNA_REC, "JITARM PC: %08x SRR0: %08x SRR1: %08x CRfast: %02x%02x%02x%02x%02x%02x%02x%02x FPSCR: %08x MSR: %08x LR: %08x %s %s",
|
||||
PC, SRR0, SRR1, PowerPC::ppcState.cr_fast[0], PowerPC::ppcState.cr_fast[1], PowerPC::ppcState.cr_fast[2], PowerPC::ppcState.cr_fast[3],
|
||||
PowerPC::ppcState.cr_fast[4], PowerPC::ppcState.cr_fast[5], PowerPC::ppcState.cr_fast[6], PowerPC::ppcState.cr_fast[7], PowerPC::ppcState.fpscr,
|
||||
PowerPC::ppcState.msr, PowerPC::ppcState.spr[8], regs, fregs);
|
||||
}
|
||||
|
||||
void JitArm::PrintDebug(UGeckoInstruction inst, u32 level)
|
||||
{
|
||||
if (level > 0)
|
||||
printf("Start: %08x OP '%s' Info\n", (u32)GetCodePtr(), PPCTables::GetInstructionName(inst));
|
||||
if (level > 1)
|
||||
{
|
||||
GekkoOPInfo* Info = GetOpInfo(inst.hex);
|
||||
printf("\tOuts\n");
|
||||
if (Info->flags & FL_OUT_A)
|
||||
printf("\t-OUT_A: %x\n", inst.RA);
|
||||
if(Info->flags & FL_OUT_D)
|
||||
printf("\t-OUT_D: %x\n", inst.RD);
|
||||
printf("\tIns\n");
|
||||
// A, AO, B, C, S
|
||||
if(Info->flags & FL_IN_A)
|
||||
printf("\t-IN_A: %x\n", inst.RA);
|
||||
if(Info->flags & FL_IN_A0)
|
||||
printf("\t-IN_A0: %x\n", inst.RA);
|
||||
if(Info->flags & FL_IN_B)
|
||||
printf("\t-IN_B: %x\n", inst.RB);
|
||||
if(Info->flags & FL_IN_C)
|
||||
printf("\t-IN_C: %x\n", inst.RC);
|
||||
if(Info->flags & FL_IN_S)
|
||||
printf("\t-IN_S: %x\n", inst.RS);
|
||||
}
|
||||
}
|
||||
|
||||
void STACKALIGN JitArm::Jit(u32 em_address)
|
||||
{
|
||||
if (GetSpaceLeft() < 0x10000 || blocks.IsFull() || Core::g_CoreStartupParameter.bJITNoBlockCache)
|
||||
{
|
||||
ClearCache();
|
||||
}
|
||||
|
||||
int block_num = blocks.AllocateBlock(em_address);
|
||||
JitBlock *b = blocks.GetBlock(block_num);
|
||||
const u8* BlockPtr = DoJit(em_address, &code_buffer, b);
|
||||
blocks.FinalizeBlock(block_num, jo.enableBlocklink, BlockPtr);
|
||||
}
|
||||
void JitArm::Break(UGeckoInstruction inst)
|
||||
{
|
||||
BKPT(0x4444);
|
||||
}
|
||||
|
||||
const u8* JitArm::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBlock *b)
|
||||
{
|
||||
int blockSize = code_buf->GetSize();
|
||||
|
||||
// Memory exception on instruction fetch
|
||||
bool memory_exception = false;
|
||||
|
||||
// A broken block is a block that does not end in a branch
|
||||
bool broken_block = false;
|
||||
|
||||
if (Core::g_CoreStartupParameter.bEnableDebugging)
|
||||
{
|
||||
// Comment out the following to disable breakpoints (speed-up)
|
||||
blockSize = 1;
|
||||
broken_block = true;
|
||||
Trace();
|
||||
}
|
||||
|
||||
if (em_address == 0)
|
||||
{
|
||||
Core::SetState(Core::CORE_PAUSE);
|
||||
PanicAlert("ERROR: Compiling at 0. LR=%08x CTR=%08x", LR, CTR);
|
||||
}
|
||||
|
||||
if (Core::g_CoreStartupParameter.bMMU && (em_address & JIT_ICACHE_VMEM_BIT))
|
||||
{
|
||||
if (!Memory::TranslateAddress(em_address, Memory::FLAG_OPCODE))
|
||||
{
|
||||
// Memory exception occurred during instruction fetch
|
||||
memory_exception = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int size = 0;
|
||||
js.isLastInstruction = false;
|
||||
js.blockStart = em_address;
|
||||
js.fifoBytesThisBlock = 0;
|
||||
js.curBlock = b;
|
||||
js.block_flags = 0;
|
||||
js.cancel = false;
|
||||
|
||||
// Analyze the block, collect all instructions it is made of (including inlining,
|
||||
// if that is enabled), reorder instructions for optimal performance, and join joinable instructions.
|
||||
u32 nextPC = em_address;
|
||||
u32 merged_addresses[32];
|
||||
const int capacity_of_merged_addresses = sizeof(merged_addresses) / sizeof(merged_addresses[0]);
|
||||
int size_of_merged_addresses = 0;
|
||||
if (!memory_exception)
|
||||
{
|
||||
// If there is a memory exception inside a block (broken_block==true), compile up to that instruction.
|
||||
nextPC = PPCAnalyst::Flatten(em_address, &size, &js.st, &js.gpa, &js.fpa, broken_block, code_buf, blockSize, merged_addresses, capacity_of_merged_addresses, size_of_merged_addresses);
|
||||
}
|
||||
PPCAnalyst::CodeOp *ops = code_buf->codebuffer;
|
||||
|
||||
const u8 *start = GetCodePtr();
|
||||
b->checkedEntry = start;
|
||||
b->runCount = 0;
|
||||
|
||||
// Downcount flag check, Only valid for linked blocks
|
||||
{
|
||||
FixupBranch skip = B_CC(CC_PL);
|
||||
ARMABI_MOVI2M((u32)&PC, js.blockStart);
|
||||
ARMReg rA = gpr.GetReg(false);
|
||||
MOVI2R(rA, (u32)asm_routines.doTiming);
|
||||
B(rA);
|
||||
SetJumpTarget(skip);
|
||||
}
|
||||
|
||||
const u8 *normalEntry = GetCodePtr();
|
||||
b->normalEntry = normalEntry;
|
||||
|
||||
if(ImHereDebug)
|
||||
QuickCallFunction(R14, (void *)&ImHere); //Used to get a trace of the last few blocks before a crash, sometimes VERY useful
|
||||
|
||||
if (js.fpa.any)
|
||||
{
|
||||
// This block uses FPU - needs to add FP exception bailout
|
||||
ARMReg A = gpr.GetReg();
|
||||
ARMReg C = gpr.GetReg();
|
||||
Operand2 Shift(2, 10); // 1 << 13
|
||||
MOVI2R(C, js.blockStart); // R3
|
||||
LDR(A, R9, STRUCT_OFF(PowerPC::ppcState, msr));
|
||||
TST(A, Shift);
|
||||
FixupBranch b1 = B_CC(CC_NEQ);
|
||||
STR(R9, C, STRUCT_OFF(PowerPC::ppcState, pc));
|
||||
MOVI2R(A, (u32)asm_routines.fpException);
|
||||
B(A);
|
||||
SetJumpTarget(b1);
|
||||
gpr.Unlock(A, C);
|
||||
}
|
||||
// Conditionally add profiling code.
|
||||
if (Profiler::g_ProfileBlocks) {
|
||||
ARMReg rA = gpr.GetReg();
|
||||
ARMReg rB = gpr.GetReg();
|
||||
MOVI2R(rA, (u32)&b->runCount); // Load in to register
|
||||
LDR(rB, rA); // Load the actual value in to R11.
|
||||
ADD(rB, rB, 1); // Add one to the value
|
||||
STR(rA, rB); // Now store it back in the memory location
|
||||
// get start tic
|
||||
PROFILER_QUERY_PERFORMANCE_COUNTER(&b->ticStart);
|
||||
gpr.Unlock(rA, rB);
|
||||
}
|
||||
gpr.Start(js.gpa);
|
||||
fpr.Start(js.fpa);
|
||||
js.downcountAmount = 0;
|
||||
if (!Core::g_CoreStartupParameter.bEnableDebugging)
|
||||
{
|
||||
for (int i = 0; i < size_of_merged_addresses; ++i)
|
||||
{
|
||||
const u32 address = merged_addresses[i];
|
||||
js.downcountAmount += PatchEngine::GetSpeedhackCycles(address);
|
||||
}
|
||||
}
|
||||
|
||||
js.skipnext = false;
|
||||
js.blockSize = size;
|
||||
js.compilerPC = nextPC;
|
||||
// Translate instructions
|
||||
for (int i = 0; i < (int)size; i++)
|
||||
{
|
||||
js.compilerPC = ops[i].address;
|
||||
js.op = &ops[i];
|
||||
js.instructionNumber = i;
|
||||
const GekkoOPInfo *opinfo = ops[i].opinfo;
|
||||
js.downcountAmount += (opinfo->numCyclesMinusOne + 1);
|
||||
|
||||
if (i == (int)size - 1)
|
||||
{
|
||||
// WARNING - cmp->branch merging will screw this up.
|
||||
js.isLastInstruction = true;
|
||||
js.next_inst = 0;
|
||||
if (Profiler::g_ProfileBlocks) {
|
||||
// CAUTION!!! push on stack regs you use, do your stuff, then pop
|
||||
PROFILER_VPUSH;
|
||||
// get end tic
|
||||
PROFILER_QUERY_PERFORMANCE_COUNTER(&b->ticStop);
|
||||
// tic counter += (end tic - start tic)
|
||||
PROFILER_ADD_DIFF_LARGE_INTEGER(&b->ticCounter, &b->ticStop, &b->ticStart);
|
||||
PROFILER_VPOP;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// help peephole optimizations
|
||||
js.next_inst = ops[i + 1].inst;
|
||||
js.next_compilerPC = ops[i + 1].address;
|
||||
}
|
||||
if (jo.optimizeGatherPipe && js.fifoBytesThisBlock >= 32)
|
||||
{
|
||||
js.fifoBytesThisBlock -= 32;
|
||||
// TODO: This needs thunkmanager for ARM
|
||||
//ARMABI_CallFunction(thunks.ProtectFunction((void *)&GPFifo::CheckGatherPipe, 0));
|
||||
}
|
||||
if (Core::g_CoreStartupParameter.bEnableDebugging)
|
||||
{
|
||||
// Add run count
|
||||
ARMReg RA = gpr.GetReg();
|
||||
ARMReg RB = gpr.GetReg();
|
||||
MOVI2R(RA, (u32)&opinfo->runCount);
|
||||
LDR(RB, RA);
|
||||
ADD(RB, RB, 1);
|
||||
STR(RA, RB);
|
||||
gpr.Unlock(RA, RB);
|
||||
}
|
||||
if (!ops[i].skip)
|
||||
{
|
||||
PrintDebug(ops[i].inst, 0);
|
||||
if (js.memcheck && (opinfo->flags & FL_USE_FPU))
|
||||
{
|
||||
// Don't do this yet
|
||||
BKPT(0x7777);
|
||||
}
|
||||
JitArmTables::CompileInstruction(ops[i]);
|
||||
if (js.memcheck && (opinfo->flags & FL_LOADSTORE))
|
||||
{
|
||||
// Don't do this yet
|
||||
BKPT(0x666);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (memory_exception)
|
||||
BKPT(0x500);
|
||||
if (broken_block)
|
||||
{
|
||||
printf("Broken Block going to 0x%08x\n", nextPC);
|
||||
WriteExit(nextPC, 0);
|
||||
}
|
||||
|
||||
b->flags = js.block_flags;
|
||||
b->codeSize = (u32)(GetCodePtr() - normalEntry);
|
||||
b->originalSize = size;
|
||||
FlushIcache();
|
||||
return start;
|
||||
}
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
// ========================
|
||||
// See comments in Jit.cpp.
|
||||
// ========================
|
||||
|
||||
// Mystery: Capcom vs SNK 800aa278
|
||||
|
||||
// CR flags approach:
|
||||
// * Store that "N+Z flag contains CR0" or "S+Z flag contains CR3".
|
||||
// * All flag altering instructions flush this
|
||||
// * A flush simply does a conditional write to the appropriate CRx.
|
||||
// * If flag available, branch code can become absolutely trivial.
|
||||
|
||||
// Settings
|
||||
// ----------
|
||||
#ifndef _JITARM_H
|
||||
#define _JITARM_H
|
||||
#include "../CPUCoreBase.h"
|
||||
#include "../PPCAnalyst.h"
|
||||
#include "JitArmCache.h"
|
||||
#include "JitRegCache.h"
|
||||
#include "JitFPRCache.h"
|
||||
#include "JitAsm.h"
|
||||
#include "../JitCommon/JitBase.h"
|
||||
|
||||
// Use these to control the instruction selection
|
||||
// #define INSTRUCTION_START Default(inst); return;
|
||||
// #define INSTRUCTION_START PPCTables::CountInstruction(inst);
|
||||
#define INSTRUCTION_START
|
||||
#define JITDISABLE(type) \
|
||||
if (Core::g_CoreStartupParameter.bJITOff || \
|
||||
Core::g_CoreStartupParameter.bJIT##type##Off) \
|
||||
{Default(inst); return;}
|
||||
|
||||
class JitArm : public JitBase, public ArmGen::ARMXCodeBlock
|
||||
{
|
||||
private:
|
||||
JitArmBlockCache blocks;
|
||||
|
||||
JitArmAsmRoutineManager asm_routines;
|
||||
|
||||
// TODO: Make arm specific versions of these, shouldn't be too hard to
|
||||
// make it so we allocate some space at the start(?) of code generation
|
||||
// and keep the registers in a cache. Will burn this bridge when we get to
|
||||
// it.
|
||||
ArmRegCache gpr;
|
||||
ArmFPRCache fpr;
|
||||
|
||||
PPCAnalyst::CodeBuffer code_buffer;
|
||||
void DoDownCount();
|
||||
|
||||
void PrintDebug(UGeckoInstruction inst, u32 level);
|
||||
|
||||
void Helper_UpdateCR1(ARMReg value);
|
||||
public:
|
||||
JitArm() : code_buffer(32000) {}
|
||||
~JitArm() {}
|
||||
|
||||
void Init();
|
||||
void Shutdown();
|
||||
|
||||
// Jit!
|
||||
|
||||
void Jit(u32 em_address);
|
||||
const u8* DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBlock *b);
|
||||
|
||||
JitBaseBlockCache *GetBlockCache() { return &blocks; }
|
||||
|
||||
const u8 *BackPatch(u8 *codePtr, int accessType, u32 em_address, void *ctx);
|
||||
|
||||
bool IsInCodeSpace(u8 *ptr) { return IsInSpace(ptr); }
|
||||
|
||||
void Trace();
|
||||
|
||||
void ClearCache();
|
||||
|
||||
const u8 *GetDispatcher() {
|
||||
return asm_routines.dispatcher;
|
||||
}
|
||||
CommonAsmRoutinesBase *GetAsmRoutines() {
|
||||
return &asm_routines;
|
||||
}
|
||||
|
||||
const char *GetName() {
|
||||
return "JITARM";
|
||||
}
|
||||
// Run!
|
||||
|
||||
void Run();
|
||||
void SingleStep();
|
||||
|
||||
// Utilities for use by opcodes
|
||||
|
||||
void WriteExit(u32 destination, int exit_num);
|
||||
void WriteExitDestInR(ARMReg Reg);
|
||||
void WriteRfiExitDestInR(ARMReg Reg);
|
||||
void WriteExceptionExit();
|
||||
void WriteCallInterpreter(UGeckoInstruction _inst);
|
||||
void Cleanup();
|
||||
|
||||
void GenerateRC(int cr = 0);
|
||||
void ComputeRC(int cr = 0);
|
||||
|
||||
// TODO: This shouldn't be here
|
||||
void StoreFromReg(ARMReg dest, ARMReg value, int accessSize, s32 offset);
|
||||
void LoadToReg(ARMReg dest, ARMReg addr, int accessSize, s32 offset);
|
||||
|
||||
// OPCODES
|
||||
void unknown_instruction(UGeckoInstruction _inst);
|
||||
void Default(UGeckoInstruction _inst);
|
||||
void DoNothing(UGeckoInstruction _inst);
|
||||
void HLEFunction(UGeckoInstruction _inst);
|
||||
|
||||
void DynaRunTable4(UGeckoInstruction _inst);
|
||||
void DynaRunTable19(UGeckoInstruction _inst);
|
||||
void DynaRunTable31(UGeckoInstruction _inst);
|
||||
void DynaRunTable59(UGeckoInstruction _inst);
|
||||
void DynaRunTable63(UGeckoInstruction _inst);
|
||||
|
||||
// Breakin shit
|
||||
void Break(UGeckoInstruction _inst);
|
||||
// Branch
|
||||
void bx(UGeckoInstruction _inst);
|
||||
void bcx(UGeckoInstruction _inst);
|
||||
void bclrx(UGeckoInstruction _inst);
|
||||
void sc(UGeckoInstruction _inst);
|
||||
void rfi(UGeckoInstruction _inst);
|
||||
void bcctrx(UGeckoInstruction _inst);
|
||||
|
||||
// Integer
|
||||
void addi(UGeckoInstruction _inst);
|
||||
void addis(UGeckoInstruction _inst);
|
||||
void addx(UGeckoInstruction _inst);
|
||||
void cmp (UGeckoInstruction _inst);
|
||||
void cmpi(UGeckoInstruction _inst);
|
||||
void cmpli(UGeckoInstruction _inst);
|
||||
void negx(UGeckoInstruction _inst);
|
||||
void mulli(UGeckoInstruction _inst);
|
||||
void ori(UGeckoInstruction _inst);
|
||||
void oris(UGeckoInstruction _inst);
|
||||
void orx(UGeckoInstruction _inst);
|
||||
void rlwimix(UGeckoInstruction _inst);
|
||||
void rlwinmx(UGeckoInstruction _inst);
|
||||
void extshx(UGeckoInstruction inst);
|
||||
void extsbx(UGeckoInstruction inst);
|
||||
|
||||
// System Registers
|
||||
void mtmsr(UGeckoInstruction _inst);
|
||||
void mtspr(UGeckoInstruction _inst);
|
||||
void mfspr(UGeckoInstruction _inst);
|
||||
|
||||
// LoadStore
|
||||
void icbi(UGeckoInstruction _inst);
|
||||
void lbz(UGeckoInstruction _inst);
|
||||
void lhz(UGeckoInstruction _inst);
|
||||
void lwz(UGeckoInstruction _inst);
|
||||
void lwzx(UGeckoInstruction _inst);
|
||||
void stw(UGeckoInstruction _inst);
|
||||
void stwu(UGeckoInstruction _inst);
|
||||
|
||||
// Floating point
|
||||
void fabsx(UGeckoInstruction _inst);
|
||||
void faddx(UGeckoInstruction _inst);
|
||||
void fmrx(UGeckoInstruction _inst);
|
||||
|
||||
// Floating point loadStore
|
||||
void lfs(UGeckoInstruction _inst);
|
||||
};
|
||||
|
||||
#endif // _JIT64_H
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
// Enable define below to enable oprofile integration. For this to work,
|
||||
// it requires at least oprofile version 0.9.4, and changing the build
|
||||
// system to link the Dolphin executable against libopagent. Since the
|
||||
// dependency is a little inconvenient and this is possibly a slight
|
||||
// performance hit, it's not enabled by default, but it's useful for
|
||||
// locating performance issues.
|
||||
|
||||
#include "../JitInterface.h"
|
||||
#include "JitArmCache.h"
|
||||
|
||||
|
||||
using namespace ArmGen;
|
||||
|
||||
void JitArmBlockCache::WriteLinkBlock(u8* location, const u8* address)
|
||||
{
|
||||
ARMXEmitter emit(location);
|
||||
emit.B(address);
|
||||
}
|
||||
void JitArmBlockCache::WriteDestroyBlock(const u8* location, u32 address)
|
||||
{
|
||||
ARMXEmitter emit((u8 *)location);
|
||||
emit.MOVI2R(R10, (u32)&PC);
|
||||
emit.MOVI2R(R11, address);
|
||||
emit.MOVI2R(R12, (u32)jit->GetAsmRoutines()->dispatcher);
|
||||
emit.STR(R10, R11);
|
||||
emit.B(R12);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _JITARMCACHE_H
|
||||
#define _JITARMCACHE_H
|
||||
|
||||
#include "../JitCommon/JitCache.h"
|
||||
|
||||
|
||||
typedef void (*CompiledCode)();
|
||||
|
||||
class JitArmBlockCache : public JitBaseBlockCache
|
||||
{
|
||||
private:
|
||||
void WriteLinkBlock(u8* location, const u8* address);
|
||||
void WriteDestroyBlock(const u8* location, u32 address);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,164 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
#include "../../HW/Memmap.h"
|
||||
#include "Jit.h"
|
||||
#include "../JitCommon/JitBackpatch.h"
|
||||
#include "StringUtil.h"
|
||||
|
||||
#ifdef _M_X64
|
||||
static void BackPatchError(const std::string &text, u8 *codePtr, u32 emAddress) {
|
||||
u64 code_addr = (u64)codePtr;
|
||||
disassembler disasm;
|
||||
char disbuf[256];
|
||||
memset(disbuf, 0, 256);
|
||||
#ifdef _M_IX86
|
||||
disasm.disasm32(0, code_addr, codePtr, disbuf);
|
||||
#else
|
||||
disasm.disasm64(0, code_addr, codePtr, disbuf);
|
||||
#endif
|
||||
PanicAlert("%s\n\n"
|
||||
"Error encountered accessing emulated address %08x.\n"
|
||||
"Culprit instruction: \n%s\nat %#llx",
|
||||
text.c_str(), emAddress, disbuf, code_addr);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// This generates some fairly heavy trampolines, but:
|
||||
// 1) It's really necessary. We don't know anything about the context.
|
||||
// 2) It doesn't really hurt. Only instructions that access I/O will get these, and there won't be
|
||||
// that many of them in a typical program/game.
|
||||
bool DisamLoadStore(const u32 inst, ARMReg &rD, u8 &accessSize, bool &Store)
|
||||
{
|
||||
u8 op = (inst >> 20) & 0xFF;
|
||||
printf("op: 0x%08x\n", op);
|
||||
switch (op)
|
||||
{
|
||||
case 0x58: // STR
|
||||
{
|
||||
rD = (ARMReg)((inst >> 16) & 0xF);
|
||||
Store = true;
|
||||
accessSize = 32;
|
||||
}
|
||||
break;
|
||||
case 0x59: // LDR
|
||||
{
|
||||
rD = (ARMReg)((inst >> 16) & 0xF);
|
||||
Store = false;
|
||||
accessSize = 32;
|
||||
}
|
||||
break;
|
||||
case 0x05: // LDRH
|
||||
{
|
||||
rD = (ARMReg)((inst >> 16) & 0xF);
|
||||
Store = false;
|
||||
accessSize = 16;
|
||||
}
|
||||
break;
|
||||
case 0x45 + 0x18: // LDRB
|
||||
{
|
||||
rD = (ARMReg)((inst >> 16) & 0xF);
|
||||
Store = false;
|
||||
accessSize = 8;
|
||||
}
|
||||
break;
|
||||
case 0x44 + 0x18: // STRB
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
const u8 *JitArm::BackPatch(u8 *codePtr, int accessType, u32 emAddress, void *ctx_void)
|
||||
{
|
||||
// TODO: This ctx needs to be filled with our information
|
||||
CONTEXT *ctx = (CONTEXT *)ctx_void;
|
||||
|
||||
// We need to get the destination register before we start
|
||||
u32 Value = *(u32*)codePtr;
|
||||
ARMReg rD;
|
||||
u8 accessSize;
|
||||
bool Store;
|
||||
|
||||
if (!DisamLoadStore(Value, rD, accessSize, Store))
|
||||
{
|
||||
printf("Invalid backpatch at location 0x%08x(0x%08x)\n", ctx->reg_pc, Value);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (Store)
|
||||
{
|
||||
const u32 ARMREGOFFSET = 4 * 7;
|
||||
ARMXEmitter emitter(codePtr - ARMREGOFFSET);
|
||||
switch (accessSize)
|
||||
{
|
||||
case 8: // 8bit
|
||||
//emitter.MOVI2R(R14, (u32)&Memory::Write_U8, false); // 1-2
|
||||
return 0;
|
||||
break;
|
||||
case 16: // 16bit
|
||||
//emitter.MOVI2R(R14, (u32)&Memory::Write_U16, false); // 1-2
|
||||
return 0;
|
||||
break;
|
||||
case 32: // 32bit
|
||||
emitter.MOVI2R(R14, (u32)&Memory::Write_U32, false); // 1-2
|
||||
break;
|
||||
}
|
||||
emitter.PUSH(4, R0, R1, R2, R3); // 3
|
||||
emitter.MOV(R0, rD); // Value - 4
|
||||
emitter.MOV(R1, R10); // Addr- 5
|
||||
emitter.BL(R14); // 6
|
||||
emitter.POP(4, R0, R1, R2, R3); // 7
|
||||
emitter.NOP(1); // 8
|
||||
u32 newPC = ctx->reg_pc - (ARMREGOFFSET + 4 * 4);
|
||||
ctx->reg_pc = newPC;
|
||||
emitter.FlushIcache();
|
||||
return codePtr;
|
||||
}
|
||||
else
|
||||
{
|
||||
const u32 ARMREGOFFSET = 4 * 6;
|
||||
ARMXEmitter emitter(codePtr - ARMREGOFFSET);
|
||||
switch (accessSize)
|
||||
{
|
||||
case 8: // 8bit
|
||||
emitter.MOVI2R(R14, (u32)&Memory::Read_U8, false); // 2
|
||||
break;
|
||||
case 16: // 16bit
|
||||
emitter.MOVI2R(R14, (u32)&Memory::Read_U16, false); // 2
|
||||
break;
|
||||
case 32: // 32bit
|
||||
emitter.MOVI2R(R14, (u32)&Memory::Read_U32, false); // 2
|
||||
break;
|
||||
}
|
||||
emitter.PUSH(4, R0, R1, R2, R3); // 3
|
||||
emitter.MOV(R0, R10); // 4
|
||||
emitter.BL(R14); // 5
|
||||
emitter.MOV(R14, R0); // 6
|
||||
emitter.POP(4, R0, R1, R2, R3); // 7
|
||||
emitter.MOV(rD, R14); // 8
|
||||
ctx->reg_pc -= ARMREGOFFSET + (4 * 4);
|
||||
emitter.FlushIcache();
|
||||
return codePtr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,347 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
#include "Common.h"
|
||||
#include "Thunk.h"
|
||||
|
||||
#include "../../Core.h"
|
||||
#include "../PowerPC.h"
|
||||
#include "../../CoreTiming.h"
|
||||
#include "../PPCTables.h"
|
||||
#include "ArmEmitter.h"
|
||||
|
||||
#include "Jit.h"
|
||||
#include "JitRegCache.h"
|
||||
#include "JitAsm.h"
|
||||
|
||||
// The branches are known good, or at least reasonably good.
|
||||
// No need for a disable-mechanism.
|
||||
|
||||
// If defined, clears CR0 at blr and bl-s. If the assumption that
|
||||
// flags never carry over between functions holds, then the task for
|
||||
// an optimizer becomes much easier.
|
||||
|
||||
// #define ACID_TEST
|
||||
|
||||
// Zelda and many more games seem to pass the Acid Test.
|
||||
|
||||
|
||||
using namespace ArmGen;
|
||||
void JitArm::sc(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Branch)
|
||||
|
||||
gpr.Flush();
|
||||
fpr.Flush();
|
||||
|
||||
ARMABI_MOVI2M((u32)&PC, js.compilerPC + 4); // Destroys R12 and R14
|
||||
ARMReg rA = gpr.GetReg();
|
||||
LDR(rA, R9, STRUCT_OFF(PowerPC::ppcState, Exceptions));
|
||||
ORR(rA, rA, EXCEPTION_SYSCALL);
|
||||
STR(R9, rA, STRUCT_OFF(PowerPC::ppcState, Exceptions));
|
||||
gpr.Unlock(rA);
|
||||
|
||||
WriteExceptionExit();
|
||||
}
|
||||
|
||||
void JitArm::rfi(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Branch)
|
||||
|
||||
gpr.Flush();
|
||||
fpr.Flush();
|
||||
|
||||
// See Interpreter rfi for details
|
||||
const u32 mask = 0x87C0FFFF;
|
||||
const u32 clearMSR13 = 0xFFFBFFFF; // Mask used to clear the bit MSR[13]
|
||||
// MSR = ((MSR & ~mask) | (SRR1 & mask)) & clearMSR13;
|
||||
// R0 = MSR location
|
||||
// R1 = MSR contents
|
||||
// R2 = Mask
|
||||
// R3 = Mask
|
||||
ARMReg rA = gpr.GetReg();
|
||||
ARMReg rB = gpr.GetReg();
|
||||
ARMReg rC = gpr.GetReg();
|
||||
ARMReg rD = gpr.GetReg();
|
||||
MOVI2R(rA, (u32)&MSR);
|
||||
MOVI2R(rB, (~mask) & clearMSR13);
|
||||
MOVI2R(rC, mask & clearMSR13);
|
||||
|
||||
LDR(rD, rA);
|
||||
|
||||
AND(rD, rD, rB); // rD = Masked MSR
|
||||
STR(rA, rD);
|
||||
|
||||
MOVI2R(rB, (u32)&SRR1);
|
||||
LDR(rB, rB); // rB contains SRR1 here
|
||||
|
||||
AND(rB, rB, rC); // rB contains masked SRR1 here
|
||||
ORR(rB, rD, rB); // rB = Masked MSR OR masked SRR1
|
||||
|
||||
STR(rA, rB); // STR rB in to rA
|
||||
|
||||
MOVI2R(rA, (u32)&SRR0);
|
||||
LDR(rA, rA);
|
||||
|
||||
gpr.Unlock(rB, rC, rD);
|
||||
WriteRfiExitDestInR(rA); // rA gets unlocked here
|
||||
//AND(32, M(&MSR), Imm32((~mask) & clearMSR13));
|
||||
//MOV(32, R(EAX), M(&SRR1));
|
||||
//AND(32, R(EAX), Imm32(mask & clearMSR13));
|
||||
//OR(32, M(&MSR), R(EAX));
|
||||
// NPC = SRR0;
|
||||
//MOV(32, R(EAX), M(&SRR0));
|
||||
//WriteRfiExitDestInEAX();
|
||||
}
|
||||
|
||||
void JitArm::bx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Branch)
|
||||
// We must always process the following sentence
|
||||
// even if the blocks are merged by PPCAnalyst::Flatten().
|
||||
if (inst.LK)
|
||||
ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4);
|
||||
|
||||
// If this is not the last instruction of a block,
|
||||
// we will skip the rest process.
|
||||
// Because PPCAnalyst::Flatten() merged the blocks.
|
||||
if (!js.isLastInstruction) {
|
||||
return;
|
||||
}
|
||||
|
||||
gpr.Flush();
|
||||
fpr.Flush();
|
||||
|
||||
u32 destination;
|
||||
if (inst.AA)
|
||||
destination = SignExt26(inst.LI << 2);
|
||||
else
|
||||
destination = js.compilerPC + SignExt26(inst.LI << 2);
|
||||
#ifdef ACID_TEST
|
||||
// TODO: Not implemented yet.
|
||||
//if (inst.LK)
|
||||
//AND(32, M(&PowerPC::ppcState.cr), Imm32(~(0xFF000000)));
|
||||
#endif
|
||||
if (destination == js.compilerPC)
|
||||
{
|
||||
//PanicAlert("Idle loop detected at %08x", destination);
|
||||
// CALL(ProtectFunction(&CoreTiming::Idle, 0));
|
||||
// JMP(Asm::testExceptions, true);
|
||||
// make idle loops go faster
|
||||
js.downcountAmount += 8;
|
||||
}
|
||||
WriteExit(destination, 0);
|
||||
}
|
||||
|
||||
void JitArm::bcx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Branch)
|
||||
// USES_CR
|
||||
_assert_msg_(DYNA_REC, js.isLastInstruction, "bcx not last instruction of block");
|
||||
|
||||
gpr.Flush();
|
||||
fpr.Flush();
|
||||
|
||||
ARMReg rA = gpr.GetReg();
|
||||
ARMReg rB = gpr.GetReg();
|
||||
FixupBranch pCTRDontBranch;
|
||||
if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0) // Decrement and test CTR
|
||||
{
|
||||
MOVI2R(rA, (u32)&CTR);
|
||||
LDR(rB, rA);
|
||||
SUBS(rB, rB, 1);
|
||||
STR(rA, rB);
|
||||
|
||||
//SUB(32, M(&CTR), Imm8(1));
|
||||
if (inst.BO & BO_BRANCH_IF_CTR_0)
|
||||
pCTRDontBranch = B_CC(CC_NEQ);
|
||||
else
|
||||
pCTRDontBranch = B_CC(CC_EQ);
|
||||
}
|
||||
|
||||
FixupBranch pConditionDontBranch;
|
||||
if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0) // Test a CR bit
|
||||
{
|
||||
LDRB(rA, R9, STRUCT_OFF(PowerPC::ppcState, cr_fast) + (inst.BI >> 2));
|
||||
TST(rA, 8 >> (inst.BI & 3));
|
||||
|
||||
//TEST(8, M(&PowerPC::ppcState.cr_fast[inst.BI >> 2]), Imm8(8 >> (inst.BI & 3)));
|
||||
if (inst.BO & BO_BRANCH_IF_TRUE) // Conditional branch
|
||||
pConditionDontBranch = B_CC(CC_EQ); // Zero
|
||||
else
|
||||
pConditionDontBranch = B_CC(CC_NEQ); // Not Zero
|
||||
}
|
||||
gpr.Unlock(rA, rB);
|
||||
if (inst.LK)
|
||||
ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4); // Careful, destroys R14, R12
|
||||
|
||||
u32 destination;
|
||||
if(inst.AA)
|
||||
destination = SignExt16(inst.BD << 2);
|
||||
else
|
||||
destination = js.compilerPC + SignExt16(inst.BD << 2);
|
||||
WriteExit(destination, 0);
|
||||
|
||||
if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0)
|
||||
SetJumpTarget( pConditionDontBranch );
|
||||
if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0)
|
||||
SetJumpTarget( pCTRDontBranch );
|
||||
|
||||
WriteExit(js.compilerPC + 4, 1);
|
||||
}
|
||||
void JitArm::bcctrx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Branch)
|
||||
|
||||
gpr.Flush();
|
||||
fpr.Flush();
|
||||
|
||||
// bcctrx doesn't decrement and/or test CTR
|
||||
_dbg_assert_msg_(POWERPC, inst.BO_2 & BO_DONT_DECREMENT_FLAG, "bcctrx with decrement and test CTR option is invalid!");
|
||||
|
||||
if (inst.BO_2 & BO_DONT_CHECK_CONDITION)
|
||||
{
|
||||
// BO_2 == 1z1zz -> b always
|
||||
|
||||
//NPC = CTR & 0xfffffffc;
|
||||
if(inst.LK_3)
|
||||
ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4);
|
||||
ARMReg rA = gpr.GetReg();
|
||||
ARMReg rB = gpr.GetReg();
|
||||
MOVI2R(rA, (u32)&CTR);
|
||||
MVN(rB, 0x3); // 0xFFFFFFFC
|
||||
LDR(rA, rA);
|
||||
AND(rA, rA, rB);
|
||||
gpr.Unlock(rB);
|
||||
WriteExitDestInR(rA);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Rare condition seen in (just some versions of?) Nintendo's NES Emulator
|
||||
|
||||
// BO_2 == 001zy -> b if false
|
||||
// BO_2 == 011zy -> b if true
|
||||
ARMReg rA = gpr.GetReg();
|
||||
ARMReg rB = gpr.GetReg();
|
||||
|
||||
LDRB(rA, R9, STRUCT_OFF(PowerPC::ppcState, cr_fast) + (inst.BI >> 2));
|
||||
TST(rA, 8 >> (inst.BI & 3));
|
||||
CCFlags branch;
|
||||
if (inst.BO_2 & BO_BRANCH_IF_TRUE)
|
||||
branch = CC_EQ;
|
||||
else
|
||||
branch = CC_NEQ;
|
||||
FixupBranch b = B_CC(branch);
|
||||
|
||||
MOVI2R(rA, (u32)&CTR);
|
||||
LDR(rA, rA);
|
||||
MVN(rB, 0x3); // 0xFFFFFFFC
|
||||
AND(rA, rA, rB);
|
||||
|
||||
if (inst.LK_3){
|
||||
ARMReg rC = gpr.GetReg(false);
|
||||
u32 Jumpto = js.compilerPC + 4;
|
||||
MOVI2R(rB, (u32)&LR);
|
||||
MOVI2R(rC, Jumpto);
|
||||
STR(rB, rC);
|
||||
//ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4);
|
||||
}
|
||||
gpr.Unlock(rB); // rA gets unlocked in WriteExitDestInR
|
||||
WriteExitDestInR(rA);
|
||||
|
||||
SetJumpTarget(b);
|
||||
WriteExit(js.compilerPC + 4, 1);
|
||||
}
|
||||
}
|
||||
void JitArm::bclrx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Branch)
|
||||
if (!js.isLastInstruction &&
|
||||
(inst.BO & (1 << 4)) && (inst.BO & (1 << 2))) {
|
||||
if (inst.LK)
|
||||
{
|
||||
ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4);
|
||||
}
|
||||
return;
|
||||
}
|
||||
gpr.Flush();
|
||||
fpr.Flush();
|
||||
|
||||
ARMReg rA = gpr.GetReg();
|
||||
ARMReg rB = gpr.GetReg();
|
||||
FixupBranch pCTRDontBranch;
|
||||
if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0) // Decrement and test CTR
|
||||
{
|
||||
MOVI2R(rA, (u32)&CTR);
|
||||
LDR(rB, rA);
|
||||
SUBS(rB, rB, 1);
|
||||
STR(rA, rB);
|
||||
|
||||
//SUB(32, M(&CTR), Imm8(1));
|
||||
if (inst.BO & BO_BRANCH_IF_CTR_0)
|
||||
pCTRDontBranch = B_CC(CC_NEQ);
|
||||
else
|
||||
pCTRDontBranch = B_CC(CC_EQ);
|
||||
}
|
||||
|
||||
FixupBranch pConditionDontBranch;
|
||||
if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0) // Test a CR bit
|
||||
{
|
||||
LDRB(rA, R9, STRUCT_OFF(PowerPC::ppcState, cr_fast) + (inst.BI >> 2));
|
||||
TST(rA, 8 >> (inst.BI & 3));
|
||||
//TEST(8, M(&PowerPC::ppcState.cr_fast[inst.BI >> 2]), Imm8(8 >> (inst.BI & 3)));
|
||||
if (inst.BO & BO_BRANCH_IF_TRUE) // Conditional branch
|
||||
pConditionDontBranch = B_CC(CC_EQ); // Zero
|
||||
else
|
||||
pConditionDontBranch = B_CC(CC_NEQ); // Not Zero
|
||||
}
|
||||
|
||||
// This below line can be used to prove that blr "eats flags" in practice.
|
||||
// This observation will let us do a lot of fun observations.
|
||||
#ifdef ACID_TEST
|
||||
// TODO: Not yet implemented
|
||||
// AND(32, M(&PowerPC::ppcState.cr), Imm32(~(0xFF000000)));
|
||||
#endif
|
||||
|
||||
//MOV(32, R(EAX), M(&LR));
|
||||
//AND(32, R(EAX), Imm32(0xFFFFFFFC));
|
||||
MOVI2R(rA, (u32)&LR);
|
||||
MVN(rB, 0x3); // 0xFFFFFFFC
|
||||
LDR(rA, rA);
|
||||
AND(rA, rA, rB);
|
||||
if (inst.LK){
|
||||
ARMReg rC = gpr.GetReg(false);
|
||||
u32 Jumpto = js.compilerPC + 4;
|
||||
MOVI2R(rB, (u32)&LR);
|
||||
MOVI2R(rC, Jumpto);
|
||||
STR(rB, rC);
|
||||
//ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4);
|
||||
}
|
||||
gpr.Unlock(rB); // rA gets unlocked in WriteExitDestInR
|
||||
WriteExitDestInR(rA);
|
||||
|
||||
if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0)
|
||||
SetJumpTarget( pConditionDontBranch );
|
||||
if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0)
|
||||
SetJumpTarget( pCTRDontBranch );
|
||||
WriteExit(js.compilerPC + 4, 1);
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "Common.h"
|
||||
#include "Thunk.h"
|
||||
|
||||
#include "../../Core.h"
|
||||
#include "../PowerPC.h"
|
||||
#include "../../ConfigManager.h"
|
||||
#include "../../CoreTiming.h"
|
||||
#include "../PPCTables.h"
|
||||
#include "ArmEmitter.h"
|
||||
#include "../../HW/Memmap.h"
|
||||
|
||||
|
||||
#include "Jit.h"
|
||||
#include "JitRegCache.h"
|
||||
#include "JitFPRCache.h"
|
||||
#include "JitAsm.h"
|
||||
|
||||
void JitArm::Helper_UpdateCR1(ARMReg value)
|
||||
{
|
||||
// Should just update exception flags, not do any compares.
|
||||
PanicAlert("CR1");
|
||||
}
|
||||
|
||||
void JitArm::fabsx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(FloatingPoint)
|
||||
|
||||
ARMReg vD = fpr.R0(inst.FD);
|
||||
ARMReg vB = fpr.R0(inst.FB);
|
||||
|
||||
VABS(vD, vB);
|
||||
|
||||
if (inst.Rc) Helper_UpdateCR1(vD);
|
||||
}
|
||||
|
||||
void JitArm::faddx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(FloatingPoint)
|
||||
|
||||
ARMReg vD = fpr.R0(inst.FD);
|
||||
ARMReg vA = fpr.R0(inst.FA);
|
||||
ARMReg vB = fpr.R0(inst.FB);
|
||||
|
||||
VADD(vD, vA, vB);
|
||||
if (inst.Rc) Helper_UpdateCR1(vD);
|
||||
}
|
||||
|
||||
void JitArm::fmrx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(FloatingPoint)
|
||||
Default(inst); return;
|
||||
|
||||
ARMReg vD = fpr.R0(inst.FD);
|
||||
ARMReg vB = fpr.R0(inst.FB);
|
||||
|
||||
VMOV(vD, vB);
|
||||
|
||||
if (inst.Rc) Helper_UpdateCR1(vD);
|
||||
}
|
||||
|
|
@ -0,0 +1,297 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
#include "Common.h"
|
||||
#include "Thunk.h"
|
||||
|
||||
#include "../../Core.h"
|
||||
#include "../PowerPC.h"
|
||||
#include "../../CoreTiming.h"
|
||||
#include "../PPCTables.h"
|
||||
#include "ArmEmitter.h"
|
||||
|
||||
#include "Jit.h"
|
||||
#include "JitRegCache.h"
|
||||
#include "JitAsm.h"
|
||||
extern u32 Helper_Mask(u8 mb, u8 me);
|
||||
// ADDI and RLWINMX broken for now
|
||||
|
||||
// Assumes that Sign and Zero flags were set by the last operation. Preserves all flags and registers.
|
||||
// Jit64 ComputerRC is signed
|
||||
// JIT64 GenerateRC is unsigned
|
||||
void JitArm::GenerateRC(int cr) {
|
||||
ARMReg rB = gpr.GetReg();
|
||||
|
||||
MOV(rB, 0x4); // Result > 0
|
||||
SetCC(CC_EQ); MOV(rB, 0x2); // Result == 0
|
||||
SetCC(CC_MI); MOV(rB, 0x8); // Result < 0
|
||||
SetCC();
|
||||
|
||||
STRB(R9, rB, STRUCT_OFF(PowerPC::ppcState, cr_fast) + cr);
|
||||
gpr.Unlock(rB);
|
||||
}
|
||||
void JitArm::ComputeRC(int cr) {
|
||||
ARMReg rB = gpr.GetReg();
|
||||
|
||||
MOV(rB, 0x2); // Result == 0
|
||||
SetCC(CC_LT); MOV(rB, 0x8); // Result < 0
|
||||
SetCC(CC_GT); MOV(rB, 0x4); // Result > 0
|
||||
SetCC();
|
||||
|
||||
STRB(R9, rB, STRUCT_OFF(PowerPC::ppcState, cr_fast) + cr);
|
||||
gpr.Unlock(rB);
|
||||
}
|
||||
|
||||
void JitArm::addi(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
|
||||
ARMReg RD = gpr.R(inst.RD);
|
||||
|
||||
if (inst.RA)
|
||||
{
|
||||
ARMReg rA = gpr.GetReg(false);
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
MOVI2R(rA, (u32)inst.SIMM_16);
|
||||
ADD(RD, RA, rA);
|
||||
}
|
||||
else
|
||||
MOVI2R(RD, inst.SIMM_16);
|
||||
}
|
||||
void JitArm::addis(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
|
||||
ARMReg RD = gpr.R(inst.RD);
|
||||
if (inst.RA)
|
||||
{
|
||||
ARMReg rA = gpr.GetReg(false);
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
MOVI2R(rA, inst.SIMM_16 << 16);
|
||||
ADD(RD, RA, rA);
|
||||
}
|
||||
else
|
||||
MOVI2R(RD, inst.SIMM_16 << 16);
|
||||
}
|
||||
void JitArm::addx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ARMReg RB = gpr.R(inst.RB);
|
||||
ARMReg RD = gpr.R(inst.RD);
|
||||
ADDS(RD, RA, RB);
|
||||
if (inst.Rc) ComputeRC();
|
||||
}
|
||||
// Wrong - 28/10/2012
|
||||
void JitArm::mulli(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
|
||||
Default(inst); return;
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ARMReg RD = gpr.R(inst.RD);
|
||||
ARMReg rA = gpr.GetReg();
|
||||
MOVI2R(rA, inst.SIMM_16);
|
||||
MUL(RD, RA, rA);
|
||||
gpr.Unlock(rA);
|
||||
}
|
||||
void JitArm::ori(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ARMReg RS = gpr.R(inst.RS);
|
||||
ARMReg rA = gpr.GetReg();
|
||||
MOVI2R(rA, inst.UIMM);
|
||||
ORR(RA, RS, rA);
|
||||
gpr.Unlock(rA);
|
||||
}
|
||||
void JitArm::oris(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ARMReg RS = gpr.R(inst.RS);
|
||||
ARMReg rA = gpr.GetReg();
|
||||
MOVI2R(rA, inst.UIMM << 16);
|
||||
ORR(RA, RS, rA);
|
||||
gpr.Unlock(rA);
|
||||
}
|
||||
void JitArm::orx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
|
||||
ARMReg rA = gpr.R(inst.RA);
|
||||
ARMReg rS = gpr.R(inst.RS);
|
||||
ARMReg rB = gpr.R(inst.RB);
|
||||
ORRS(rA, rS, rB);
|
||||
if (inst.Rc)
|
||||
ComputeRC();
|
||||
}
|
||||
|
||||
void JitArm::extshx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
ARMReg RA, RS;
|
||||
RA = gpr.R(inst.RA);
|
||||
RS = gpr.R(inst.RS);
|
||||
SXTH(RA, RS);
|
||||
if (inst.Rc){
|
||||
CMP(RA, 0);
|
||||
ComputeRC();
|
||||
}
|
||||
}
|
||||
void JitArm::extsbx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
ARMReg RA, RS;
|
||||
RA = gpr.R(inst.RA);
|
||||
RS = gpr.R(inst.RS);
|
||||
SXTB(RA, RS);
|
||||
if (inst.Rc){
|
||||
CMP(RA, 0);
|
||||
ComputeRC();
|
||||
}
|
||||
}
|
||||
void JitArm::cmp (UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ARMReg RB = gpr.R(inst.RB);
|
||||
int crf = inst.CRFD;
|
||||
CMP(RA, RB);
|
||||
ComputeRC(crf);
|
||||
}
|
||||
void JitArm::cmpi(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ARMReg rA = gpr.GetReg();
|
||||
int crf = inst.CRFD;
|
||||
MOVI2R(rA, inst.SIMM_16);
|
||||
CMP(RA, rA);
|
||||
gpr.Unlock(rA);
|
||||
ComputeRC(crf);
|
||||
}
|
||||
void JitArm::cmpli(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ARMReg rA = gpr.GetReg();
|
||||
int crf = inst.CRFD;
|
||||
MOVI2R(rA, (u32)inst.UIMM);
|
||||
CMP(RA, rA);
|
||||
|
||||
// Unsigned GenerateRC()
|
||||
|
||||
MOV(rA, 0x2); // Result == 0
|
||||
SetCC(CC_LO); MOV(rA, 0x8); // Result < 0
|
||||
SetCC(CC_HI); MOV(rA, 0x4); // Result > 0
|
||||
SetCC();
|
||||
|
||||
STRB(R9, rA, STRUCT_OFF(PowerPC::ppcState, cr_fast) + crf);
|
||||
gpr.Unlock(rA);
|
||||
|
||||
}
|
||||
// Wrong - 27/10/2012
|
||||
void JitArm::negx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
|
||||
Default(inst);return;
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ARMReg RD = gpr.R(inst.RD);
|
||||
|
||||
RSBS(RD, RA, 0);
|
||||
if (inst.Rc)
|
||||
{
|
||||
GenerateRC();
|
||||
}
|
||||
if (inst.OE)
|
||||
{
|
||||
BKPT(0x333);
|
||||
//GenerateOverflow();
|
||||
}
|
||||
}
|
||||
void JitArm::rlwimix(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
|
||||
u32 mask = Helper_Mask(inst.MB,inst.ME);
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ARMReg RS = gpr.R(inst.RS);
|
||||
ARMReg rA = gpr.GetReg();
|
||||
ARMReg rB = gpr.GetReg();
|
||||
MOVI2R(rA, mask);
|
||||
|
||||
Operand2 Shift(32 - inst.SH, ST_ROR, RS); // This rotates left, while ARM has only rotate right, so swap it.
|
||||
if (inst.Rc)
|
||||
{
|
||||
BIC (rB, RA, rA); // RA & ~mask
|
||||
AND (rA, rA, Shift);
|
||||
ORRS(RA, rB, rA);
|
||||
GenerateRC();
|
||||
}
|
||||
else
|
||||
{
|
||||
BIC (rB, RA, rA); // RA & ~mask
|
||||
AND (rA, rA, Shift);
|
||||
ORR(RA, rB, rA);
|
||||
}
|
||||
gpr.Unlock(rA, rB);
|
||||
}
|
||||
void JitArm::rlwinmx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
|
||||
u32 mask = Helper_Mask(inst.MB,inst.ME);
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ARMReg RS = gpr.R(inst.RS);
|
||||
ARMReg rA = gpr.GetReg();
|
||||
MOVI2R(rA, mask);
|
||||
|
||||
Operand2 Shift(32 - inst.SH, ST_ROR, RS); // This rotates left, while ARM has only rotate right, so swap it.
|
||||
if (inst.Rc)
|
||||
{
|
||||
ANDS(RA, rA, Shift);
|
||||
GenerateRC();
|
||||
}
|
||||
else
|
||||
AND (RA, rA, Shift);
|
||||
gpr.Unlock(rA);
|
||||
|
||||
//m_GPR[inst.RA] = _rotl(m_GPR[inst.RS],inst.SH) & mask;
|
||||
}
|
||||
|
|
@ -0,0 +1,414 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "Common.h"
|
||||
#include "Thunk.h"
|
||||
|
||||
#include "../../Core.h"
|
||||
#include "../PowerPC.h"
|
||||
#include "../../ConfigManager.h"
|
||||
#include "../../CoreTiming.h"
|
||||
#include "../PPCTables.h"
|
||||
#include "ArmEmitter.h"
|
||||
#include "../../HW/Memmap.h"
|
||||
|
||||
|
||||
#include "Jit.h"
|
||||
#include "JitRegCache.h"
|
||||
#include "JitAsm.h"
|
||||
|
||||
#ifdef ANDROID
|
||||
#define FASTMEM 0
|
||||
#else
|
||||
#define FASTMEM 0
|
||||
#endif
|
||||
void JitArm::stw(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(LoadStore)
|
||||
|
||||
ARMReg RS = gpr.R(inst.RS);
|
||||
#if FASTMEM
|
||||
// R10 contains the dest address
|
||||
ARMReg Value = R11;
|
||||
ARMReg RA;
|
||||
if (inst.RA)
|
||||
RA = gpr.R(inst.RA);
|
||||
MOV(Value, RS);
|
||||
if (inst.RA)
|
||||
{
|
||||
MOVI2R(R10, inst.SIMM_16, false);
|
||||
ADD(R10, R10, RA);
|
||||
}
|
||||
else
|
||||
{
|
||||
MOVI2R(R10, (u32)inst.SIMM_16, false);
|
||||
NOP(1);
|
||||
}
|
||||
StoreFromReg(R10, Value, 32, 0);
|
||||
#else
|
||||
ARMReg ValueReg = gpr.GetReg();
|
||||
ARMReg Addr = gpr.GetReg();
|
||||
ARMReg Function = gpr.GetReg();
|
||||
|
||||
MOV(ValueReg, RS);
|
||||
if (inst.RA)
|
||||
{
|
||||
MOVI2R(Addr, inst.SIMM_16);
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ADD(Addr, Addr, RA);
|
||||
}
|
||||
else
|
||||
MOVI2R(Addr, (u32)inst.SIMM_16);
|
||||
|
||||
MOVI2R(Function, (u32)&Memory::Write_U32);
|
||||
PUSH(4, R0, R1, R2, R3);
|
||||
MOV(R0, ValueReg);
|
||||
MOV(R1, Addr);
|
||||
BL(Function);
|
||||
POP(4, R0, R1, R2, R3);
|
||||
gpr.Unlock(ValueReg, Addr, Function);
|
||||
#endif
|
||||
}
|
||||
void JitArm::stwu(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(LoadStore)
|
||||
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ARMReg RS = gpr.R(inst.RS);
|
||||
ARMReg ValueReg = gpr.GetReg();
|
||||
ARMReg Addr = gpr.GetReg();
|
||||
ARMReg Function = gpr.GetReg();
|
||||
|
||||
MOVI2R(Addr, inst.SIMM_16);
|
||||
ADD(Addr, Addr, RA);
|
||||
|
||||
// Check and set the update before writing since calling a function can
|
||||
// mess with the "special registers R11+ which may cause some issues.
|
||||
LDR(Function, R9, STRUCT_OFF(PowerPC::ppcState, Exceptions));
|
||||
CMP(Function, EXCEPTION_DSI);
|
||||
FixupBranch DoNotWrite = B_CC(CC_EQ);
|
||||
MOV(RA, Addr);
|
||||
SetJumpTarget(DoNotWrite);
|
||||
|
||||
MOV(ValueReg, RS);
|
||||
|
||||
MOVI2R(Function, (u32)&Memory::Write_U32);
|
||||
PUSH(4, R0, R1, R2, R3);
|
||||
MOV(R0, ValueReg);
|
||||
MOV(R1, Addr);
|
||||
BL(Function);
|
||||
POP(4, R0, R1, R2, R3);
|
||||
|
||||
gpr.Unlock(ValueReg, Addr, Function);
|
||||
}
|
||||
void JitArm::StoreFromReg(ARMReg dest, ARMReg value, int accessSize, s32 offset)
|
||||
{
|
||||
ARMReg rA = gpr.GetReg();
|
||||
|
||||
// All this gets replaced on backpatch
|
||||
MOVI2R(rA, Memory::MEMVIEW32_MASK, false); // 1-2
|
||||
AND(dest, dest, rA); // 3
|
||||
MOVI2R(rA, (u32)Memory::base, false); // 4-5
|
||||
ADD(dest, dest, rA); // 6
|
||||
switch (accessSize)
|
||||
{
|
||||
case 32:
|
||||
REV(value, value); // 7
|
||||
break;
|
||||
case 16:
|
||||
REV16(value, value);
|
||||
break;
|
||||
case 8:
|
||||
NOP(1);
|
||||
break;
|
||||
}
|
||||
switch (accessSize)
|
||||
{
|
||||
case 32:
|
||||
STR(dest, value); // 8
|
||||
break;
|
||||
case 16:
|
||||
// Not implemented
|
||||
break;
|
||||
case 8:
|
||||
// Not implemented
|
||||
break;
|
||||
}
|
||||
gpr.Unlock(rA);
|
||||
}
|
||||
void JitArm::LoadToReg(ARMReg dest, ARMReg addr, int accessSize, s32 offset)
|
||||
{
|
||||
ARMReg rA = gpr.GetReg();
|
||||
MOVI2R(rA, offset, false); // -3
|
||||
ADD(addr, addr, rA); // - 1
|
||||
|
||||
// All this gets replaced on backpatch
|
||||
MOVI2R(rA, Memory::MEMVIEW32_MASK, false); // 2
|
||||
AND(addr, addr, rA); // 3
|
||||
MOVI2R(rA, (u32)Memory::base, false); // 5
|
||||
ADD(addr, addr, rA); // 6
|
||||
switch (accessSize)
|
||||
{
|
||||
case 32:
|
||||
LDR(dest, addr); // 7
|
||||
break;
|
||||
case 16:
|
||||
LDRH(dest, addr);
|
||||
break;
|
||||
case 8:
|
||||
LDRB(dest, addr);
|
||||
break;
|
||||
}
|
||||
switch (accessSize)
|
||||
{
|
||||
case 32:
|
||||
REV(dest, dest); // 9
|
||||
break;
|
||||
case 16:
|
||||
REV16(dest, dest);
|
||||
break;
|
||||
case 8:
|
||||
NOP(1);
|
||||
break;
|
||||
|
||||
}
|
||||
gpr.Unlock(rA);
|
||||
}
|
||||
void JitArm::lbz(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(LoadStore)
|
||||
|
||||
ARMReg rA = gpr.GetReg();
|
||||
ARMReg rB = gpr.GetReg();
|
||||
ARMReg RD = gpr.R(inst.RD);
|
||||
LDR(rA, R9, STRUCT_OFF(PowerPC::ppcState, Exceptions));
|
||||
CMP(rA, EXCEPTION_DSI);
|
||||
FixupBranch DoNotLoad = B_CC(CC_EQ);
|
||||
#if FASTMEM
|
||||
// Backpatch route
|
||||
// Gets loaded in to RD
|
||||
// Address is in R10
|
||||
gpr.Unlock(rA, rB);
|
||||
if (inst.RA)
|
||||
{
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
MOV(R10, RA); // - 4
|
||||
}
|
||||
else
|
||||
MOV(R10, 0); // - 4
|
||||
LoadToReg(RD, R10, 8, inst.SIMM_16);
|
||||
#else
|
||||
|
||||
if (inst.RA)
|
||||
{
|
||||
MOVI2R(rB, inst.SIMM_16);
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ADD(rB, rB, RA);
|
||||
}
|
||||
else
|
||||
MOVI2R(rB, (u32)inst.SIMM_16);
|
||||
|
||||
MOVI2R(rA, (u32)&Memory::Read_U8);
|
||||
PUSH(4, R0, R1, R2, R3);
|
||||
MOV(R0, rB);
|
||||
BL(rA);
|
||||
MOV(rA, R0);
|
||||
POP(4, R0, R1, R2, R3);
|
||||
MOV(RD, rA);
|
||||
gpr.Unlock(rA, rB);
|
||||
#endif
|
||||
SetJumpTarget(DoNotLoad);
|
||||
}
|
||||
|
||||
void JitArm::lhz(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(LoadStore)
|
||||
|
||||
ARMReg rA = gpr.GetReg();
|
||||
ARMReg rB = gpr.GetReg();
|
||||
ARMReg RD = gpr.R(inst.RD);
|
||||
LDR(rA, R9, STRUCT_OFF(PowerPC::ppcState, Exceptions));
|
||||
CMP(rA, EXCEPTION_DSI);
|
||||
FixupBranch DoNotLoad = B_CC(CC_EQ);
|
||||
#if 0 // FASTMEM
|
||||
// Backpatch route
|
||||
// Gets loaded in to RD
|
||||
// Address is in R10
|
||||
gpr.Unlock(rA, rB);
|
||||
if (inst.RA)
|
||||
{
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
printf("lhz jump to here: 0x%08x\n", (u32)GetCodePtr());
|
||||
MOV(R10, RA); // - 4
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("lhz jump to here: 0x%08x\n", (u32)GetCodePtr());
|
||||
MOV(R10, 0); // - 4
|
||||
}
|
||||
LoadToReg(RD, R10, 16, (u32)inst.SIMM_16);
|
||||
#else
|
||||
|
||||
if (inst.RA)
|
||||
{
|
||||
MOVI2R(rB, inst.SIMM_16);
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ADD(rB, rB, RA);
|
||||
}
|
||||
else
|
||||
MOVI2R(rB, (u32)inst.SIMM_16);
|
||||
|
||||
MOVI2R(rA, (u32)&Memory::Read_U16);
|
||||
PUSH(4, R0, R1, R2, R3);
|
||||
MOV(R0, rB);
|
||||
BL(rA);
|
||||
MOV(rA, R0);
|
||||
POP(4, R0, R1, R2, R3);
|
||||
MOV(RD, rA);
|
||||
gpr.Unlock(rA, rB);
|
||||
#endif
|
||||
SetJumpTarget(DoNotLoad);
|
||||
}
|
||||
void JitArm::lwz(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(LoadStore)
|
||||
|
||||
ARMReg rA = gpr.GetReg();
|
||||
ARMReg rB = gpr.GetReg();
|
||||
ARMReg RD = gpr.R(inst.RD);
|
||||
LDR(rA, R9, STRUCT_OFF(PowerPC::ppcState, Exceptions));
|
||||
CMP(rA, EXCEPTION_DSI);
|
||||
FixupBranch DoNotLoad = B_CC(CC_EQ);
|
||||
|
||||
#if FASTMEM
|
||||
// Backpatch route
|
||||
// Gets loaded in to RD
|
||||
// Address is in R10
|
||||
gpr.Unlock(rA, rB);
|
||||
if (inst.RA)
|
||||
{
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
MOV(R10, RA); // - 4
|
||||
}
|
||||
else
|
||||
MOV(R10, 0); // - 4
|
||||
LoadToReg(RD, R10, 32, (u32)inst.SIMM_16);
|
||||
#else
|
||||
if (inst.RA)
|
||||
{
|
||||
MOVI2R(rB, inst.SIMM_16);
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ADD(rB, rB, RA);
|
||||
}
|
||||
else
|
||||
MOVI2R(rB, (u32)inst.SIMM_16);
|
||||
|
||||
MOVI2R(rA, (u32)&Memory::Read_U32);
|
||||
PUSH(4, R0, R1, R2, R3);
|
||||
MOV(R0, rB);
|
||||
BL(rA);
|
||||
MOV(rA, R0);
|
||||
POP(4, R0, R1, R2, R3);
|
||||
MOV(RD, rA);
|
||||
gpr.Unlock(rA, rB);
|
||||
#endif
|
||||
SetJumpTarget(DoNotLoad);
|
||||
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bSkipIdle &&
|
||||
(inst.hex & 0xFFFF0000) == 0x800D0000 &&
|
||||
(Memory::ReadUnchecked_U32(js.compilerPC + 4) == 0x28000000 ||
|
||||
(SConfig::GetInstance().m_LocalCoreStartupParameter.bWii && Memory::ReadUnchecked_U32(js.compilerPC + 4) == 0x2C000000)) &&
|
||||
Memory::ReadUnchecked_U32(js.compilerPC + 8) == 0x4182fff8)
|
||||
{
|
||||
gpr.Flush();
|
||||
fpr.Flush();
|
||||
|
||||
// if it's still 0, we can wait until the next event
|
||||
TST(RD, RD);
|
||||
FixupBranch noIdle = B_CC(CC_NEQ);
|
||||
rA = gpr.GetReg();
|
||||
|
||||
MOVI2R(rA, (u32)&PowerPC::OnIdle);
|
||||
MOVI2R(R0, PowerPC::ppcState.gpr[inst.RA] + (s32)(s16)inst.SIMM_16);
|
||||
BL(rA);
|
||||
|
||||
gpr.Unlock(rA);
|
||||
WriteExceptionExit();
|
||||
|
||||
SetJumpTarget(noIdle);
|
||||
|
||||
//js.compilerPC += 8;
|
||||
return;
|
||||
}
|
||||
}
|
||||
void JitArm::lwzx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(LoadStore)
|
||||
|
||||
ARMReg rA = gpr.GetReg();
|
||||
ARMReg rB = gpr.GetReg();
|
||||
|
||||
ARMReg RB = gpr.R(inst.RB);
|
||||
ARMReg RD = gpr.R(inst.RD);
|
||||
LDR(rA, R9, STRUCT_OFF(PowerPC::ppcState, Exceptions));
|
||||
CMP(rA, EXCEPTION_DSI);
|
||||
FixupBranch DoNotLoad = B_CC(CC_EQ);
|
||||
#if FASTMEM
|
||||
// Backpatch route
|
||||
// Gets loaded in to RD
|
||||
// Address is in R10
|
||||
gpr.Unlock(rA, rB);
|
||||
if (inst.RA)
|
||||
{
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ADD(R10, RA, RB); // - 4
|
||||
}
|
||||
else
|
||||
MOV(R10, RB); // -4
|
||||
LoadToReg(RD, R10, 32, 0);
|
||||
#else
|
||||
if (inst.RA)
|
||||
{
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ADD(rB, RA, RB);
|
||||
}
|
||||
else
|
||||
MOV(rB, RB);
|
||||
|
||||
MOVI2R(rA, (u32)&Memory::Read_U32);
|
||||
PUSH(4, R0, R1, R2, R3);
|
||||
MOV(R0, rB);
|
||||
BL(rA);
|
||||
MOV(rA, R0);
|
||||
POP(4, R0, R1, R2, R3);
|
||||
MOV(RD, rA);
|
||||
gpr.Unlock(rA, rB);
|
||||
#endif
|
||||
SetJumpTarget(DoNotLoad);
|
||||
//// u32 temp = Memory::Read_U32(_inst.RA ? (m_GPR[_inst.RA] + m_GPR[_inst.RB]) : m_GPR[_inst.RB]);
|
||||
}
|
||||
void JitArm::icbi(UGeckoInstruction inst)
|
||||
{
|
||||
Default(inst);
|
||||
WriteExit(js.compilerPC + 4, 0);
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "Common.h"
|
||||
#include "Thunk.h"
|
||||
|
||||
#include "../../Core.h"
|
||||
#include "../PowerPC.h"
|
||||
#include "../../ConfigManager.h"
|
||||
#include "../../CoreTiming.h"
|
||||
#include "../PPCTables.h"
|
||||
#include "ArmEmitter.h"
|
||||
#include "../../HW/Memmap.h"
|
||||
|
||||
|
||||
#include "Jit.h"
|
||||
#include "JitRegCache.h"
|
||||
#include "JitFPRCache.h"
|
||||
#include "JitAsm.h"
|
||||
|
||||
void JitArm::lfs(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(LoadStoreFloating)
|
||||
Default(inst); return;
|
||||
|
||||
ARMReg rA = gpr.GetReg();
|
||||
ARMReg rB = gpr.GetReg();
|
||||
LDR(rA, R9, STRUCT_OFF(PowerPC::ppcState, Exceptions));
|
||||
CMP(rA, EXCEPTION_DSI);
|
||||
FixupBranch DoNotLoad = B_CC(CC_EQ);
|
||||
|
||||
if (inst.RA)
|
||||
{
|
||||
MOVI2R(rB, inst.SIMM_16);
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ADD(rB, rB, RA);
|
||||
}
|
||||
else
|
||||
MOVI2R(rB, (u32)inst.SIMM_16);
|
||||
|
||||
MOVI2R(rA, (u32)&Memory::Read_U32);
|
||||
PUSH(4, R0, R1, R2, R3);
|
||||
MOV(R0, rB);
|
||||
BL(rA);
|
||||
MOV(rA, R0);
|
||||
POP(4, R0, R1, R2, R3);
|
||||
|
||||
ARMReg v0 = fpr.R0(inst.FD, false);
|
||||
ARMReg v1 = fpr.R1(inst.FD, false);
|
||||
|
||||
VMOV(v0, rA, false);
|
||||
VMOV(v1, rA, false);
|
||||
|
||||
gpr.Unlock(rA, rB);
|
||||
SetJumpTarget(DoNotLoad);
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
#include "Common.h"
|
||||
#include "Thunk.h"
|
||||
|
||||
#include "../../Core.h"
|
||||
#include "../PowerPC.h"
|
||||
#include "../../CoreTiming.h"
|
||||
#include "../PPCTables.h"
|
||||
#include "ArmEmitter.h"
|
||||
|
||||
#include "Jit.h"
|
||||
#include "JitRegCache.h"
|
||||
#include "JitAsm.h"
|
||||
|
||||
void JitArm::mtspr(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(SystemRegisters)
|
||||
|
||||
u32 iIndex = (inst.SPRU << 5) | (inst.SPRL & 0x1F);
|
||||
ARMReg RD = gpr.R(inst.RD);
|
||||
|
||||
switch (iIndex)
|
||||
{
|
||||
case SPR_LR:
|
||||
case SPR_CTR:
|
||||
case SPR_XER:
|
||||
// These are safe to do the easy way, see the bottom of this function.
|
||||
break;
|
||||
|
||||
case SPR_GQR0:
|
||||
case SPR_GQR0 + 1:
|
||||
case SPR_GQR0 + 2:
|
||||
case SPR_GQR0 + 3:
|
||||
case SPR_GQR0 + 4:
|
||||
case SPR_GQR0 + 5:
|
||||
case SPR_GQR0 + 6:
|
||||
case SPR_GQR0 + 7:
|
||||
// Prevent recompiler from compiling in old quantizer values.
|
||||
// If the value changed, destroy all blocks using this quantizer
|
||||
// This will create a little bit of block churn, but hopefully not too bad.
|
||||
{
|
||||
/*
|
||||
MOV(32, R(EAX), M(&PowerPC::ppcState.spr[iIndex])); // Load old value
|
||||
CMP(32, R(EAX), gpr.R(inst.RD));
|
||||
FixupBranch skip_destroy = J_CC(CC_E, false);
|
||||
int gqr = iIndex - SPR_GQR0;
|
||||
ABI_CallFunctionC(ProtectFunction(&Jit64::DestroyBlocksWithFlag, 1), (u32)BLOCK_USE_GQR0 << gqr);
|
||||
SetJumpTarget(skip_destroy);*/
|
||||
}
|
||||
// TODO - break block if quantizers are written to.
|
||||
default:
|
||||
Default(inst);
|
||||
return;
|
||||
}
|
||||
|
||||
// OK, this is easy.
|
||||
ARMReg rA = gpr.GetReg(false);
|
||||
MOVI2R(rA, (u32)&PowerPC::ppcState.spr);
|
||||
STR(rA, RD, iIndex * 4);
|
||||
}
|
||||
|
||||
void JitArm::mfspr(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(SystemRegisters)
|
||||
|
||||
u32 iIndex = (inst.SPRU << 5) | (inst.SPRL & 0x1F);
|
||||
ARMReg RD = gpr.R(inst.RD);
|
||||
switch (iIndex)
|
||||
{
|
||||
case SPR_WPAR:
|
||||
case SPR_DEC:
|
||||
case SPR_TL:
|
||||
case SPR_TU:
|
||||
Default(inst);
|
||||
return;
|
||||
default:
|
||||
ARMReg rA = gpr.GetReg(false);
|
||||
MOVI2R(rA, (u32)&PowerPC::ppcState.spr);
|
||||
LDR(RD, rA, iIndex * 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
void JitArm::mtmsr(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
// Don't interpret this, if we do we get thrown out
|
||||
//JITDISABLE(SystemRegisters)
|
||||
|
||||
ARMReg rA = gpr.GetReg();
|
||||
MOVI2R(rA, (u32)&MSR);
|
||||
STR(rA, gpr.R(inst.RS));
|
||||
gpr.Unlock(rA);
|
||||
WriteExit(js.compilerPC + 4, 0);
|
||||
}
|
|
@ -0,0 +1,502 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "Jit.h"
|
||||
#include "../JitInterface.h"
|
||||
#include "JitArm_Tables.h"
|
||||
|
||||
// Should be moved in to the Jit class
|
||||
typedef void (JitArm::*_Instruction) (UGeckoInstruction instCode);
|
||||
|
||||
static _Instruction dynaOpTable[64];
|
||||
static _Instruction dynaOpTable4[1024];
|
||||
static _Instruction dynaOpTable19[1024];
|
||||
static _Instruction dynaOpTable31[1024];
|
||||
static _Instruction dynaOpTable59[32];
|
||||
static _Instruction dynaOpTable63[1024];
|
||||
|
||||
void JitArm::DynaRunTable4(UGeckoInstruction _inst) {(this->*dynaOpTable4 [_inst.SUBOP10])(_inst);}
|
||||
void JitArm::DynaRunTable19(UGeckoInstruction _inst) {(this->*dynaOpTable19[_inst.SUBOP10])(_inst);}
|
||||
void JitArm::DynaRunTable31(UGeckoInstruction _inst) {(this->*dynaOpTable31[_inst.SUBOP10])(_inst);}
|
||||
void JitArm::DynaRunTable59(UGeckoInstruction _inst) {(this->*dynaOpTable59[_inst.SUBOP5 ])(_inst);}
|
||||
void JitArm::DynaRunTable63(UGeckoInstruction _inst) {(this->*dynaOpTable63[_inst.SUBOP10])(_inst);}
|
||||
|
||||
struct GekkoOPTemplate
|
||||
{
|
||||
int opcode;
|
||||
_Instruction Inst;
|
||||
//GekkoOPInfo opinfo; // Doesn't need opinfo, Interpreter fills it out
|
||||
};
|
||||
|
||||
static GekkoOPTemplate primarytable[] =
|
||||
{
|
||||
{4, &JitArm::DynaRunTable4}, //"RunTable4", OPTYPE_SUBTABLE | (4<<24), 0}},
|
||||
{19, &JitArm::DynaRunTable19}, //"RunTable19", OPTYPE_SUBTABLE | (19<<24), 0}},
|
||||
{31, &JitArm::DynaRunTable31}, //"RunTable31", OPTYPE_SUBTABLE | (31<<24), 0}},
|
||||
{59, &JitArm::DynaRunTable59}, //"RunTable59", OPTYPE_SUBTABLE | (59<<24), 0}},
|
||||
{63, &JitArm::DynaRunTable63}, //"RunTable63", OPTYPE_SUBTABLE | (63<<24), 0}},
|
||||
|
||||
{16, &JitArm::bcx}, //"bcx", OPTYPE_SYSTEM, FL_ENDBLOCK}},
|
||||
{18, &JitArm::bx}, //"bx", OPTYPE_SYSTEM, FL_ENDBLOCK}},
|
||||
|
||||
{1, &JitArm::HLEFunction}, //"HLEFunction", OPTYPE_SYSTEM, FL_ENDBLOCK}},
|
||||
{2, &JitArm::Default}, //"DynaBlock", OPTYPE_SYSTEM, 0}},
|
||||
{3, &JitArm::Break}, //"twi", OPTYPE_SYSTEM, FL_ENDBLOCK}},
|
||||
{17, &JitArm::sc}, //"sc", OPTYPE_SYSTEM, FL_ENDBLOCK, 1}},
|
||||
|
||||
{7, &JitArm::mulli}, //"mulli", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_RC_BIT, 2}},
|
||||
{8, &JitArm::Default}, //"subfic", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_SET_CA}},
|
||||
{10, &JitArm::cmpli}, //"cmpli", OPTYPE_INTEGER, FL_IN_A | FL_SET_CRn}},
|
||||
{11, &JitArm::cmpi}, //"cmpi", OPTYPE_INTEGER, FL_IN_A | FL_SET_CRn}},
|
||||
{12, &JitArm::Default}, //"addic", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_SET_CA}},
|
||||
{13, &JitArm::Default}, //"addic_rc", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_SET_CR0}},
|
||||
{14, &JitArm::addi}, //"addi", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A0}},
|
||||
{15, &JitArm::addis}, //"addis", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A0}},
|
||||
|
||||
{20, &JitArm::rlwimix}, //"rlwimix", OPTYPE_INTEGER, FL_OUT_A | FL_IN_A | FL_IN_S | FL_RC_BIT}},
|
||||
{21, &JitArm::rlwinmx}, //"rlwinmx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_RC_BIT}},
|
||||
{23, &JitArm::Default}, //"rlwnmx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_IN_B | FL_RC_BIT}},
|
||||
|
||||
{24, &JitArm::ori}, //"ori", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S}},
|
||||
{25, &JitArm::oris}, //"oris", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S}},
|
||||
{26, &JitArm::Default}, //"xori", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S}},
|
||||
{27, &JitArm::Default}, //"xoris", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S}},
|
||||
{28, &JitArm::Default}, //"andi_rc", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_SET_CR0}},
|
||||
{29, &JitArm::Default}, //"andis_rc", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_SET_CR0}},
|
||||
|
||||
{32, &JitArm::lwz}, //"lwz", OPTYPE_LOAD, FL_OUT_D | FL_IN_A}},
|
||||
{33, &JitArm::Default}, //"lwzu", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A}},
|
||||
{34, &JitArm::lbz}, //"lbz", OPTYPE_LOAD, FL_OUT_D | FL_IN_A}},
|
||||
{35, &JitArm::Default}, //"lbzu", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A}},
|
||||
{40, &JitArm::lhz}, //"lhz", OPTYPE_LOAD, FL_OUT_D | FL_IN_A}},
|
||||
{41, &JitArm::Default}, //"lhzu", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A}},
|
||||
{42, &JitArm::Default}, //"lha", OPTYPE_LOAD, FL_OUT_D | FL_IN_A}},
|
||||
{43, &JitArm::Default}, //"lhau", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A}},
|
||||
|
||||
{44, &JitArm::Default}, //"sth", OPTYPE_STORE, FL_IN_A | FL_IN_S}},
|
||||
{45, &JitArm::Default}, //"sthu", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_S}},
|
||||
{36, &JitArm::stw}, //"stw", OPTYPE_STORE, FL_IN_A | FL_IN_S}},
|
||||
{37, &JitArm::stwu}, //"stwu", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_S}},
|
||||
{38, &JitArm::Default}, //"stb", OPTYPE_STORE, FL_IN_A | FL_IN_S}},
|
||||
{39, &JitArm::Default}, //"stbu", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_S}},
|
||||
|
||||
{46, &JitArm::Default}, //"lmw", OPTYPE_SYSTEM, FL_EVIL, 10}},
|
||||
{47, &JitArm::Default}, //"stmw", OPTYPE_SYSTEM, FL_EVIL, 10}},
|
||||
|
||||
{48, &JitArm::lfs}, //"lfs", OPTYPE_LOADFP, FL_IN_A}},
|
||||
{49, &JitArm::Default}, //"lfsu", OPTYPE_LOADFP, FL_OUT_A | FL_IN_A}},
|
||||
{50, &JitArm::Default}, //"lfd", OPTYPE_LOADFP, FL_IN_A}},
|
||||
{51, &JitArm::Default}, //"lfdu", OPTYPE_LOADFP, FL_OUT_A | FL_IN_A}},
|
||||
|
||||
{52, &JitArm::Default}, //"stfs", OPTYPE_STOREFP, FL_IN_A}},
|
||||
{53, &JitArm::Default}, //"stfsu", OPTYPE_STOREFP, FL_OUT_A | FL_IN_A}},
|
||||
{54, &JitArm::Default}, //"stfd", OPTYPE_STOREFP, FL_IN_A}},
|
||||
{55, &JitArm::Default}, //"stfdu", OPTYPE_STOREFP, FL_OUT_A | FL_IN_A}},
|
||||
|
||||
{56, &JitArm::Default}, //"psq_l", OPTYPE_PS, FL_IN_A}},
|
||||
{57, &JitArm::Default}, //"psq_lu", OPTYPE_PS, FL_OUT_A | FL_IN_A}},
|
||||
{60, &JitArm::Default}, //"psq_st", OPTYPE_PS, FL_IN_A}},
|
||||
{61, &JitArm::Default}, //"psq_stu", OPTYPE_PS, FL_OUT_A | FL_IN_A}},
|
||||
|
||||
//missing: 0, 5, 6, 9, 22, 30, 62, 58
|
||||
{0, &JitArm::Default}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}},
|
||||
{5, &JitArm::Default}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}},
|
||||
{6, &JitArm::Default}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}},
|
||||
{9, &JitArm::Default}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}},
|
||||
{22, &JitArm::Default}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}},
|
||||
{30, &JitArm::Default}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}},
|
||||
{62, &JitArm::Default}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}},
|
||||
{58, &JitArm::Default}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}},
|
||||
};
|
||||
|
||||
static GekkoOPTemplate table4[] =
|
||||
{ //SUBOP10
|
||||
{0, &JitArm::Default}, //"ps_cmpu0", OPTYPE_PS, FL_SET_CRn}},
|
||||
{32, &JitArm::Default}, //"ps_cmpo0", OPTYPE_PS, FL_SET_CRn}},
|
||||
{40, &JitArm::Default}, //"ps_neg", OPTYPE_PS, FL_RC_BIT}},
|
||||
{136, &JitArm::Default}, //"ps_nabs", OPTYPE_PS, FL_RC_BIT}},
|
||||
{264, &JitArm::Default}, //"ps_abs", OPTYPE_PS, FL_RC_BIT}},
|
||||
{64, &JitArm::Default}, //"ps_cmpu1", OPTYPE_PS, FL_RC_BIT}},
|
||||
{72, &JitArm::Default}, //"ps_mr", OPTYPE_PS, FL_RC_BIT}},
|
||||
{96, &JitArm::Default}, //"ps_cmpo1", OPTYPE_PS, FL_RC_BIT}},
|
||||
{528, &JitArm::Default}, //"ps_merge00", OPTYPE_PS, FL_RC_BIT}},
|
||||
{560, &JitArm::Default}, //"ps_merge01", OPTYPE_PS, FL_RC_BIT}},
|
||||
{592, &JitArm::Default}, //"ps_merge10", OPTYPE_PS, FL_RC_BIT}},
|
||||
{624, &JitArm::Default}, //"ps_merge11", OPTYPE_PS, FL_RC_BIT}},
|
||||
|
||||
{1014, &JitArm::Default}, //"dcbz_l", OPTYPE_SYSTEM, 0}},
|
||||
};
|
||||
|
||||
static GekkoOPTemplate table4_2[] =
|
||||
{
|
||||
{10, &JitArm::Default}, //"ps_sum0", OPTYPE_PS, 0}},
|
||||
{11, &JitArm::Default}, //"ps_sum1", OPTYPE_PS, 0}},
|
||||
{12, &JitArm::Default}, //"ps_muls0", OPTYPE_PS, 0}},
|
||||
{13, &JitArm::Default}, //"ps_muls1", OPTYPE_PS, 0}},
|
||||
{14, &JitArm::Default}, //"ps_madds0", OPTYPE_PS, 0}},
|
||||
{15, &JitArm::Default}, //"ps_madds1", OPTYPE_PS, 0}},
|
||||
{18, &JitArm::Default}, //"ps_div", OPTYPE_PS, 0, 16}},
|
||||
{20, &JitArm::Default}, //"ps_sub", OPTYPE_PS, 0}},
|
||||
{21, &JitArm::Default}, //"ps_add", OPTYPE_PS, 0}},
|
||||
{23, &JitArm::Default}, //"ps_sel", OPTYPE_PS, 0}},
|
||||
{24, &JitArm::Default}, //"ps_res", OPTYPE_PS, 0}},
|
||||
{25, &JitArm::Default}, //"ps_mul", OPTYPE_PS, 0}},
|
||||
{26, &JitArm::Default}, //"ps_rsqrte", OPTYPE_PS, 0, 1}},
|
||||
{28, &JitArm::Default}, //"ps_msub", OPTYPE_PS, 0}},
|
||||
{29, &JitArm::Default}, //"ps_madd", OPTYPE_PS, 0}},
|
||||
{30, &JitArm::Default}, //"ps_nmsub", OPTYPE_PS, 0}},
|
||||
{31, &JitArm::Default}, //"ps_nmadd", OPTYPE_PS, 0}},
|
||||
};
|
||||
|
||||
|
||||
static GekkoOPTemplate table4_3[] =
|
||||
{
|
||||
{6, &JitArm::Default}, //"psq_lx", OPTYPE_PS, 0}},
|
||||
{7, &JitArm::Default}, //"psq_stx", OPTYPE_PS, 0}},
|
||||
{38, &JitArm::Default}, //"psq_lux", OPTYPE_PS, 0}},
|
||||
{39, &JitArm::Default}, //"psq_stux", OPTYPE_PS, 0}},
|
||||
};
|
||||
|
||||
static GekkoOPTemplate table19[] =
|
||||
{
|
||||
{528, &JitArm::bcctrx}, //"bcctrx", OPTYPE_BRANCH, FL_ENDBLOCK}},
|
||||
{16, &JitArm::bclrx}, //"bclrx", OPTYPE_BRANCH, FL_ENDBLOCK}},
|
||||
{257, &JitArm::Default}, //"crand", OPTYPE_CR, FL_EVIL}},
|
||||
{129, &JitArm::Default}, //"crandc", OPTYPE_CR, FL_EVIL}},
|
||||
{289, &JitArm::Default}, //"creqv", OPTYPE_CR, FL_EVIL}},
|
||||
{225, &JitArm::Default}, //"crnand", OPTYPE_CR, FL_EVIL}},
|
||||
{33, &JitArm::Default}, //"crnor", OPTYPE_CR, FL_EVIL}},
|
||||
{449, &JitArm::Default}, //"cror", OPTYPE_CR, FL_EVIL}},
|
||||
{417, &JitArm::Default}, //"crorc", OPTYPE_CR, FL_EVIL}},
|
||||
{193, &JitArm::Default}, //"crxor", OPTYPE_CR, FL_EVIL}},
|
||||
|
||||
{150, &JitArm::DoNothing}, //"isync", OPTYPE_ICACHE, FL_EVIL}},
|
||||
{0, &JitArm::Default}, //"mcrf", OPTYPE_SYSTEM, FL_EVIL}},
|
||||
|
||||
{50, &JitArm::rfi}, //"rfi", OPTYPE_SYSTEM, FL_ENDBLOCK | FL_CHECKEXCEPTIONS, 1}},
|
||||
{18, &JitArm::Break}, //"rfid", OPTYPE_SYSTEM, FL_ENDBLOCK | FL_CHECKEXCEPTIONS}}
|
||||
};
|
||||
|
||||
|
||||
static GekkoOPTemplate table31[] =
|
||||
{
|
||||
{28, &JitArm::Default}, //"andx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
|
||||
{60, &JitArm::Default}, //"andcx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
|
||||
{444, &JitArm::orx}, //"orx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
|
||||
{124, &JitArm::Default}, //"norx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
|
||||
{316, &JitArm::Default}, //"xorx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
|
||||
{412, &JitArm::Default}, //"orcx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
|
||||
{476, &JitArm::Default}, //"nandx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
|
||||
{284, &JitArm::Default}, //"eqvx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
|
||||
{0, &JitArm::cmp}, //"cmp", OPTYPE_INTEGER, FL_IN_AB | FL_SET_CRn}},
|
||||
{32, &JitArm::Default}, //"cmpl", OPTYPE_INTEGER, FL_IN_AB | FL_SET_CRn}},
|
||||
{26, &JitArm::Default}, //"cntlzwx",OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_RC_BIT}},
|
||||
{922, &JitArm::extshx}, //"extshx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_RC_BIT}},
|
||||
{954, &JitArm::extsbx}, //"extsbx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_RC_BIT}},
|
||||
{536, &JitArm::Default}, //"srwx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}},
|
||||
{792, &JitArm::Default}, //"srawx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}},
|
||||
{824, &JitArm::Default}, //"srawix", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}},
|
||||
{24, &JitArm::Default}, //"slwx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}},
|
||||
|
||||
{54, &JitArm::Default}, //"dcbst", OPTYPE_DCACHE, 0, 4}},
|
||||
{86, &JitArm::Default}, //"dcbf", OPTYPE_DCACHE, 0, 4}},
|
||||
{246, &JitArm::Default}, //"dcbtst", OPTYPE_DCACHE, 0, 1}},
|
||||
{278, &JitArm::Default}, //"dcbt", OPTYPE_DCACHE, 0, 1}},
|
||||
{470, &JitArm::Default}, //"dcbi", OPTYPE_DCACHE, 0, 4}},
|
||||
{758, &JitArm::Default}, //"dcba", OPTYPE_DCACHE, 0, 4}},
|
||||
{1014, &JitArm::Default}, //"dcbz", OPTYPE_DCACHE, 0, 4}},
|
||||
|
||||
//load word
|
||||
{23, &JitArm::lwzx}, //"lwzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
|
||||
{55, &JitArm::Default}, //"lwzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}},
|
||||
|
||||
//load halfword
|
||||
{279, &JitArm::Default}, //"lhzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
|
||||
{311, &JitArm::Default}, //"lhzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}},
|
||||
|
||||
//load halfword signextend
|
||||
{343, &JitArm::Default}, //"lhax", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
|
||||
{375, &JitArm::Default}, //"lhaux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}},
|
||||
|
||||
//load byte
|
||||
{87, &JitArm::Default}, //"lbzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
|
||||
{119, &JitArm::Default}, //"lbzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}},
|
||||
|
||||
//load byte reverse
|
||||
{534, &JitArm::Default}, //"lwbrx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
|
||||
{790, &JitArm::Default}, //"lhbrx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
|
||||
|
||||
// Conditional load/store (Wii SMP)
|
||||
{150, &JitArm::Default}, //"stwcxd", OPTYPE_STORE, FL_EVIL | FL_SET_CR0}},
|
||||
{20, &JitArm::Default}, //"lwarx", OPTYPE_LOAD, FL_EVIL | FL_OUT_D | FL_IN_A0B | FL_SET_CR0}},
|
||||
|
||||
//load string (interpret these)
|
||||
{533, &JitArm::Default}, //"lswx", OPTYPE_LOAD, FL_EVIL | FL_IN_A | FL_OUT_D}},
|
||||
{597, &JitArm::Default}, //"lswi", OPTYPE_LOAD, FL_EVIL | FL_IN_AB | FL_OUT_D}},
|
||||
|
||||
//store word
|
||||
{151, &JitArm::Default}, //"stwx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}},
|
||||
{183, &JitArm::Default}, //"stwux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B}},
|
||||
|
||||
//store halfword
|
||||
{407, &JitArm::Default}, //"sthx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}},
|
||||
{439, &JitArm::Default}, //"sthux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B}},
|
||||
|
||||
//store byte
|
||||
{215, &JitArm::Default}, //"stbx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}},
|
||||
{247, &JitArm::Default}, //"stbux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B}},
|
||||
|
||||
//store bytereverse
|
||||
{662, &JitArm::Default}, //"stwbrx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}},
|
||||
{918, &JitArm::Default}, //"sthbrx", OPTYPE_STORE, FL_IN_A | FL_IN_B}},
|
||||
|
||||
{661, &JitArm::Default}, //"stswx", OPTYPE_STORE, FL_EVIL}},
|
||||
{725, &JitArm::Default}, //"stswi", OPTYPE_STORE, FL_EVIL}},
|
||||
|
||||
// fp load/store
|
||||
{535, &JitArm::Default}, //"lfsx", OPTYPE_LOADFP, FL_IN_A0 | FL_IN_B}},
|
||||
{567, &JitArm::Default}, //"lfsux", OPTYPE_LOADFP, FL_IN_A | FL_IN_B}},
|
||||
{599, &JitArm::Default}, //"lfdx", OPTYPE_LOADFP, FL_IN_A0 | FL_IN_B}},
|
||||
{631, &JitArm::Default}, //"lfdux", OPTYPE_LOADFP, FL_IN_A | FL_IN_B}},
|
||||
|
||||
{663, &JitArm::Default}, //"stfsx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B}},
|
||||
{695, &JitArm::Default}, //"stfsux", OPTYPE_STOREFP, FL_IN_A | FL_IN_B}},
|
||||
{727, &JitArm::Default}, //"stfdx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B}},
|
||||
{759, &JitArm::Default}, //"stfdux", OPTYPE_STOREFP, FL_IN_A | FL_IN_B}},
|
||||
{983, &JitArm::Default}, //"stfiwx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B}},
|
||||
|
||||
{19, &JitArm::Default}, //"mfcr", OPTYPE_SYSTEM, FL_OUT_D}},
|
||||
{83, &JitArm::Default}, //"mfmsr", OPTYPE_SYSTEM, FL_OUT_D}},
|
||||
{144, &JitArm::Default}, //"mtcrf", OPTYPE_SYSTEM, 0}},
|
||||
{146, &JitArm::mtmsr}, //"mtmsr", OPTYPE_SYSTEM, FL_ENDBLOCK}},
|
||||
{210, &JitArm::Default}, //"mtsr", OPTYPE_SYSTEM, 0}},
|
||||
{242, &JitArm::Default}, //"mtsrin", OPTYPE_SYSTEM, 0}},
|
||||
{339, &JitArm::mfspr}, //"mfspr", OPTYPE_SPR, FL_OUT_D}},
|
||||
{467, &JitArm::mtspr}, //"mtspr", OPTYPE_SPR, 0, 2}},
|
||||
{371, &JitArm::Default}, //"mftb", OPTYPE_SYSTEM, FL_OUT_D | FL_TIMER}},
|
||||
{512, &JitArm::Default}, //"mcrxr", OPTYPE_SYSTEM, 0}},
|
||||
{595, &JitArm::Default}, //"mfsr", OPTYPE_SYSTEM, FL_OUT_D, 2}},
|
||||
{659, &JitArm::Default}, //"mfsrin", OPTYPE_SYSTEM, FL_OUT_D, 2}},
|
||||
|
||||
{4, &JitArm::Break}, //"tw", OPTYPE_SYSTEM, FL_ENDBLOCK, 1}},
|
||||
{598, &JitArm::Default}, //"sync", OPTYPE_SYSTEM, 0, 2}},
|
||||
{982, &JitArm::icbi}, //"icbi", OPTYPE_SYSTEM, FL_ENDBLOCK, 3}},
|
||||
|
||||
// Unused instructions on GC
|
||||
{310, &JitArm::Default}, //"eciwx", OPTYPE_INTEGER, FL_RC_BIT}},
|
||||
{438, &JitArm::Default}, //"ecowx", OPTYPE_INTEGER, FL_RC_BIT}},
|
||||
{854, &JitArm::Default}, //"eieio", OPTYPE_INTEGER, FL_RC_BIT}},
|
||||
{306, &JitArm::Default}, //"tlbie", OPTYPE_SYSTEM, 0}},
|
||||
{370, &JitArm::Default}, //"tlbia", OPTYPE_SYSTEM, 0}},
|
||||
{566, &JitArm::Default}, //"tlbsync", OPTYPE_SYSTEM, 0}},
|
||||
};
|
||||
|
||||
static GekkoOPTemplate table31_2[] =
|
||||
{
|
||||
{266, &JitArm::addx}, //"addx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
|
||||
{778, &JitArm::addx}, //"addx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
|
||||
{10, &JitArm::Default}, //"addcx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT}},
|
||||
{138, &JitArm::Default}, //"addex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
|
||||
{234, &JitArm::Default}, //"addmex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
|
||||
{202, &JitArm::Default}, //"addzex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
|
||||
{491, &JitArm::Default}, //"divwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39}},
|
||||
{1003, &JitArm::Default}, //"divwox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39}},
|
||||
{459, &JitArm::Default}, //"divwux", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39}},
|
||||
{971, &JitArm::Default}, //"divwuox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39}},
|
||||
{75, &JitArm::Default}, //"mulhwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}},
|
||||
{11, &JitArm::Default}, //"mulhwux", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}},
|
||||
{235, &JitArm::Default}, //"mullwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}},
|
||||
{747, &JitArm::Default}, //"mullwox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}},
|
||||
{104, &JitArm::negx}, //"negx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
|
||||
{40, &JitArm::Default}, //"subfx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
|
||||
{552, &JitArm::Default}, //"subox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
|
||||
{8, &JitArm::Default}, //"subfcx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT}},
|
||||
{136, &JitArm::Default}, //"subfex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
|
||||
{232, &JitArm::Default}, //"subfmex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
|
||||
{200, &JitArm::Default}, //"subfzex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
|
||||
};
|
||||
|
||||
static GekkoOPTemplate table59[] =
|
||||
{
|
||||
{18, &JitArm::Default}, //{"fdivsx", OPTYPE_FPU, FL_RC_BIT_F, 16}},
|
||||
{20, &JitArm::Default}, //"fsubsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{21, &JitArm::Default}, //"faddsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
// {22, &JitArm::Default}, //"fsqrtsx", OPTYPE_FPU, FL_RC_BIT_F}}, // Not implemented on gekko
|
||||
{24, &JitArm::Default}, //"fresx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{25, &JitArm::Default}, //"fmulsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{28, &JitArm::Default}, //"fmsubsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{29, &JitArm::Default}, //"fmaddsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{30, &JitArm::Default}, //"fnmsubsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{31, &JitArm::Default}, //"fnmaddsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
};
|
||||
|
||||
static GekkoOPTemplate table63[] =
|
||||
{
|
||||
{264, &JitArm::fabsx}, //"fabsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{32, &JitArm::Default}, //"fcmpo", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{0, &JitArm::Default}, //"fcmpu", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{14, &JitArm::Default}, //"fctiwx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{15, &JitArm::Default}, //"fctiwzx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{72, &JitArm::fmrx}, //"fmrx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{136, &JitArm::Default}, //"fnabsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{40, &JitArm::Default}, //"fnegx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{12, &JitArm::Default}, //"frspx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
|
||||
{64, &JitArm::Default}, //"mcrfs", OPTYPE_SYSTEMFP, 0}},
|
||||
{583, &JitArm::Default}, //"mffsx", OPTYPE_SYSTEMFP, 0}},
|
||||
{70, &JitArm::Default}, //"mtfsb0x", OPTYPE_SYSTEMFP, 0, 2}},
|
||||
{38, &JitArm::Default}, //"mtfsb1x", OPTYPE_SYSTEMFP, 0, 2}},
|
||||
{134, &JitArm::Default}, //"mtfsfix", OPTYPE_SYSTEMFP, 0, 2}},
|
||||
{711, &JitArm::Default}, //"mtfsfx", OPTYPE_SYSTEMFP, 0, 2}},
|
||||
};
|
||||
|
||||
static GekkoOPTemplate table63_2[] =
|
||||
{
|
||||
{18, &JitArm::Default}, //"fdivx", OPTYPE_FPU, FL_RC_BIT_F, 30}},
|
||||
{20, &JitArm::Default}, //"fsubx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{21, &JitArm::faddx}, //"faddx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{22, &JitArm::Default}, //"fsqrtx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{23, &JitArm::Default}, //"fselx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{25, &JitArm::Default}, //"fmulx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{26, &JitArm::Default}, //"frsqrtex", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{28, &JitArm::Default}, //"fmsubx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{29, &JitArm::Default}, //"fmaddx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{30, &JitArm::Default}, //"fnmsubx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{31, &JitArm::Default}, //"fnmaddx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
};
|
||||
|
||||
|
||||
namespace JitArmTables
|
||||
{
|
||||
|
||||
void CompileInstruction(PPCAnalyst::CodeOp & op)
|
||||
{
|
||||
JitArm *jitarm = (JitArm *)jit;
|
||||
(jitarm->*dynaOpTable[op.inst.OPCD])(op.inst);
|
||||
GekkoOPInfo *info = op.opinfo;
|
||||
if (info) {
|
||||
#ifdef OPLOG
|
||||
if (!strcmp(info->opname, OP_TO_LOG)){ ///"mcrfs"
|
||||
rsplocations.push_back(jit.js.compilerPC);
|
||||
}
|
||||
#endif
|
||||
info->compileCount++;
|
||||
info->lastUse = jit->js.compilerPC;
|
||||
}
|
||||
}
|
||||
|
||||
void InitTables()
|
||||
{
|
||||
// once initialized, tables are read-only
|
||||
static bool initialized = false;
|
||||
if (initialized)
|
||||
return;
|
||||
|
||||
//clear
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
dynaOpTable59[i] = &JitArm::unknown_instruction;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 1024; i++)
|
||||
{
|
||||
dynaOpTable4 [i] = &JitArm::unknown_instruction;
|
||||
dynaOpTable19[i] = &JitArm::unknown_instruction;
|
||||
dynaOpTable31[i] = &JitArm::unknown_instruction;
|
||||
dynaOpTable63[i] = &JitArm::unknown_instruction;
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)(sizeof(primarytable) / sizeof(GekkoOPTemplate)); i++)
|
||||
{
|
||||
dynaOpTable[primarytable[i].opcode] = primarytable[i].Inst;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
int fill = i << 5;
|
||||
for (int j = 0; j < (int)(sizeof(table4_2) / sizeof(GekkoOPTemplate)); j++)
|
||||
{
|
||||
int op = fill+table4_2[j].opcode;
|
||||
dynaOpTable4[op] = table4_2[j].Inst;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
int fill = i << 6;
|
||||
for (int j = 0; j < (int)(sizeof(table4_3) / sizeof(GekkoOPTemplate)); j++)
|
||||
{
|
||||
int op = fill+table4_3[j].opcode;
|
||||
dynaOpTable4[op] = table4_3[j].Inst;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)(sizeof(table4) / sizeof(GekkoOPTemplate)); i++)
|
||||
{
|
||||
int op = table4[i].opcode;
|
||||
dynaOpTable4[op] = table4[i].Inst;
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)(sizeof(table31) / sizeof(GekkoOPTemplate)); i++)
|
||||
{
|
||||
int op = table31[i].opcode;
|
||||
dynaOpTable31[op] = table31[i].Inst;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 1; i++)
|
||||
{
|
||||
int fill = i << 9;
|
||||
for (int j = 0; j < (int)(sizeof(table31_2) / sizeof(GekkoOPTemplate)); j++)
|
||||
{
|
||||
int op = fill + table31_2[j].opcode;
|
||||
dynaOpTable31[op] = table31_2[j].Inst;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)(sizeof(table19) / sizeof(GekkoOPTemplate)); i++)
|
||||
{
|
||||
int op = table19[i].opcode;
|
||||
dynaOpTable19[op] = table19[i].Inst;
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)(sizeof(table59) / sizeof(GekkoOPTemplate)); i++)
|
||||
{
|
||||
int op = table59[i].opcode;
|
||||
dynaOpTable59[op] = table59[i].Inst;
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)(sizeof(table63) / sizeof(GekkoOPTemplate)); i++)
|
||||
{
|
||||
int op = table63[i].opcode;
|
||||
dynaOpTable63[op] = table63[i].Inst;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
int fill = i << 5;
|
||||
for (int j = 0; j < (int)(sizeof(table63_2) / sizeof(GekkoOPTemplate)); j++)
|
||||
{
|
||||
int op = fill + table63_2[j].opcode;
|
||||
dynaOpTable63[op] = table63_2[j].Inst;
|
||||
}
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
|
||||
}
|
||||
|
||||
} // namespace
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef JITARM_TABLES_H
|
||||
#define JITARM_TABLES_H
|
||||
|
||||
#include "../Gekko.h"
|
||||
#include "../PPCTables.h"
|
||||
|
||||
namespace JitArmTables
|
||||
{
|
||||
void CompileInstruction(PPCAnalyst::CodeOp & op);
|
||||
void InitTables();
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,174 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
|
||||
#include "../../HW/Memmap.h"
|
||||
|
||||
#include "../PowerPC.h"
|
||||
#include "../../CoreTiming.h"
|
||||
#include "MemoryUtil.h"
|
||||
|
||||
#include "Jit.h"
|
||||
#include "../JitCommon/JitCache.h"
|
||||
|
||||
#include "../../HW/GPFifo.h"
|
||||
#include "../../Core.h"
|
||||
#include "JitAsm.h"
|
||||
#include "ArmEmitter.h"
|
||||
|
||||
using namespace ArmGen;
|
||||
|
||||
//TODO - make an option
|
||||
//#if _DEBUG
|
||||
// bool enableDebug = false;
|
||||
//#else
|
||||
// bool enableDebug = false;
|
||||
//#endif
|
||||
|
||||
JitArmAsmRoutineManager asm_routines;
|
||||
|
||||
void JitArmAsmRoutineManager::Generate()
|
||||
{
|
||||
enterCode = GetCodePtr();
|
||||
PUSH(2, R11, _LR); // R11 is frame pointer in Debug.
|
||||
|
||||
MOVI2R(R0, (u32)&CoreTiming::downcount);
|
||||
MOVI2R(R9, (u32)&PowerPC::ppcState);
|
||||
|
||||
FixupBranch skipToRealDispatcher = B();
|
||||
dispatcher = GetCodePtr();
|
||||
printf("Dispatcher is %p\n", dispatcher);
|
||||
|
||||
// Downcount Check
|
||||
// The result of slice decrementation should be in flags if somebody jumped here
|
||||
// IMPORTANT - We jump on negative, not carry!!!
|
||||
FixupBranch bail = B_CC(CC_MI);
|
||||
|
||||
SetJumpTarget(skipToRealDispatcher);
|
||||
dispatcherNoCheck = GetCodePtr();
|
||||
|
||||
// This block of code gets the address of the compiled block of code
|
||||
// It runs though to the compiling portion if it isn't found
|
||||
LDR(R12, R9, STRUCT_OFF(PowerPC::ppcState, pc));// Load the current PC into R12
|
||||
|
||||
MOVI2R(R14, JIT_ICACHE_MASK); // Potential for optimization
|
||||
AND(R12, R12, R14); // R12 contains PC & JIT_ICACHE_MASK here.
|
||||
// Confirmed good to this point 08-03-12
|
||||
|
||||
MOVI2R(R14, (u32)jit->GetBlockCache()->GetICache());
|
||||
// Confirmed That this loads the base iCache Location correctly 08-04-12
|
||||
|
||||
LDR(R12, R14, R12, true, true); // R12 contains iCache[PC & JIT_ICACHE_MASK] here
|
||||
// R12 Confirmed this is the correct iCache Location loaded.
|
||||
TST(R12, 0xFC); // Test to see if it is a JIT block.
|
||||
|
||||
SetCC(CC_EQ); // Only run next part if R12 is zero
|
||||
// Success, it is our Jitblock.
|
||||
MOVI2R(R14, (u32)jit->GetBlockCache()->GetCodePointers());
|
||||
// LDR R14 right here to get CodePointers()[0] pointer.
|
||||
REV(R12, R12); // Reversing this gives us our JITblock.
|
||||
LSL(R12, R12, 2); // Multiply by four because address locations are u32 in size
|
||||
LDR(R14, R14, R12, true, true); // Load the block address in to R14
|
||||
|
||||
B(R14);
|
||||
|
||||
FixupBranch NextBlock = B(); // Jump to end so we can start a new block
|
||||
SetCC(); // Return to always executing codes
|
||||
|
||||
// If we get to this point, that means that we don't have the block cached to execute
|
||||
// So call ArmJit to compile the block and then execute it.
|
||||
MOVI2R(R14, (u32)&Jit);
|
||||
LDR(R0, R9, STRUCT_OFF(PowerPC::ppcState, pc));
|
||||
BL(R14);
|
||||
|
||||
B(dispatcherNoCheck);
|
||||
|
||||
// fpException()
|
||||
// Floating Point Exception Check, Jumped to if false
|
||||
fpException = GetCodePtr();
|
||||
LDR(R0, R9, STRUCT_OFF(PowerPC::ppcState, Exceptions));
|
||||
ORR(R0, R0, EXCEPTION_FPU_UNAVAILABLE);
|
||||
STR(R9, R0, STRUCT_OFF(PowerPC::ppcState, Exceptions));
|
||||
QuickCallFunction(R14, (void*)&PowerPC::CheckExceptions);
|
||||
LDR(R0, R9, STRUCT_OFF(PowerPC::ppcState, npc));
|
||||
STR(R9, R0, STRUCT_OFF(PowerPC::ppcState, pc));
|
||||
B(dispatcher);
|
||||
|
||||
SetJumpTarget(bail);
|
||||
doTiming = GetCodePtr();
|
||||
// XXX: In JIT64, Advance() gets called /after/ the exception checking
|
||||
// once it jumps back to the start of outerLoop
|
||||
QuickCallFunction(R14, (void*)&CoreTiming::Advance);
|
||||
|
||||
// Does exception checking
|
||||
testExceptions = GetCodePtr();
|
||||
LDR(R0, R9, STRUCT_OFF(PowerPC::ppcState, pc));
|
||||
STR(R9, R0, STRUCT_OFF(PowerPC::ppcState, npc));
|
||||
QuickCallFunction(R14, (void*)&PowerPC::CheckExceptions);
|
||||
LDR(R0, R9, STRUCT_OFF(PowerPC::ppcState, npc));
|
||||
STR(R9, R0, STRUCT_OFF(PowerPC::ppcState, pc));
|
||||
// Check the state pointer to see if we are exiting
|
||||
// Gets checked on every exception check
|
||||
MOVI2R(R0, (u32)PowerPC::GetStatePtr());
|
||||
MVN(R1, 0);
|
||||
LDR(R0, R0);
|
||||
TST(R0, R1);
|
||||
FixupBranch Exit = B_CC(CC_NEQ);
|
||||
|
||||
SetJumpTarget(NextBlock);
|
||||
B(dispatcher);
|
||||
|
||||
SetJumpTarget(Exit);
|
||||
|
||||
POP(2, R11, _PC);
|
||||
FlushIcache();
|
||||
}
|
||||
|
||||
void JitArmAsmRoutineManager::GenerateCommon()
|
||||
{
|
||||
/* fifoDirectWrite8 = AlignCode4();
|
||||
GenFifoWrite(8);
|
||||
fifoDirectWrite16 = AlignCode4();
|
||||
GenFifoWrite(16);
|
||||
fifoDirectWrite32 = AlignCode4();
|
||||
GenFifoWrite(32);
|
||||
fifoDirectWriteFloat = AlignCode4();
|
||||
GenFifoFloatWrite();
|
||||
fifoDirectWriteXmm64 = AlignCode4();
|
||||
GenFifoXmm64Write();
|
||||
|
||||
GenQuantizedLoads();
|
||||
GenQuantizedStores();
|
||||
GenQuantizedSingleStores();
|
||||
*/
|
||||
//CMPSD(R(XMM0), M(&zero),
|
||||
// TODO
|
||||
|
||||
// Fast write routines - special case the most common hardware write
|
||||
// TODO: use this.
|
||||
// Even in x86, the param values will be in the right registers.
|
||||
/*
|
||||
const u8 *fastMemWrite8 = AlignCode16();
|
||||
CMP(32, R(ABI_PARAM2), Imm32(0xCC008000));
|
||||
FixupBranch skip_fast_write = J_CC(CC_NE, false);
|
||||
MOV(32, EAX, M(&m_gatherPipeCount));
|
||||
MOV(8, MDisp(EAX, (u32)&m_gatherPipe), ABI_PARAM1);
|
||||
ADD(32, 1, M(&m_gatherPipeCount));
|
||||
RET();
|
||||
SetJumpTarget(skip_fast_write);
|
||||
CALL((void *)&Memory::Write_U8);*/
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _JITARMASM_H
|
||||
#define _JITARMASM_H
|
||||
#include "ArmEmitter.h"
|
||||
#include "../JitCommon/JitAsmCommon.h"
|
||||
using namespace ArmGen;
|
||||
class JitArmAsmRoutineManager : public CommonAsmRoutinesBase, public ARMXCodeBlock
|
||||
{
|
||||
private:
|
||||
void Generate();
|
||||
void GenerateCommon();
|
||||
|
||||
public:
|
||||
void Init() {
|
||||
AllocCodeSpace(8192);
|
||||
Generate();
|
||||
WriteProtect();
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
FreeCodeSpace();
|
||||
}
|
||||
};
|
||||
|
||||
extern JitArmAsmRoutineManager asm_routines;
|
||||
|
||||
#endif // _JIT64ASM_H
|
|
@ -0,0 +1,167 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "JitRegCache.h"
|
||||
|
||||
ArmRegCache::ArmRegCache()
|
||||
{
|
||||
emit = 0;
|
||||
}
|
||||
|
||||
void ArmRegCache::Init(ARMXEmitter *emitter)
|
||||
{
|
||||
emit = emitter;
|
||||
ARMReg *PPCRegs = GetPPCAllocationOrder(NUMPPCREG);
|
||||
ARMReg *Regs = GetAllocationOrder(NUMARMREG);
|
||||
for(u8 a = 0; a < 32; ++a)
|
||||
{
|
||||
// This gives us the memory locations of the gpr registers so we can
|
||||
// load them.
|
||||
regs[a].location = (u8*)&PowerPC::ppcState.gpr[a];
|
||||
regs[a].UsesLeft = 0;
|
||||
}
|
||||
for(u8 a = 0; a < NUMPPCREG; ++a)
|
||||
{
|
||||
ArmCRegs[a].PPCReg = 33;
|
||||
ArmCRegs[a].Reg = PPCRegs[a];
|
||||
ArmCRegs[a].LastLoad = 0;
|
||||
}
|
||||
for(u8 a = 0; a < NUMARMREG; ++a)
|
||||
{
|
||||
ArmRegs[a].Reg = Regs[a];
|
||||
ArmRegs[a].free = true;
|
||||
}
|
||||
}
|
||||
void ArmRegCache::Start(PPCAnalyst::BlockRegStats &stats)
|
||||
{
|
||||
for(u8 a = 0; a < NUMPPCREG; ++a)
|
||||
{
|
||||
ArmCRegs[a].PPCReg = 33;
|
||||
ArmCRegs[a].LastLoad = 0;
|
||||
}
|
||||
for(u8 a = 0; a < 32; ++a)
|
||||
regs[a].UsesLeft = stats.GetTotalNumAccesses(a);
|
||||
}
|
||||
ARMReg *ArmRegCache::GetPPCAllocationOrder(int &count)
|
||||
{
|
||||
// This will return us the allocation order of the registers we can use on
|
||||
// the ppc side.
|
||||
static ARMReg allocationOrder[] =
|
||||
{
|
||||
R0, R1, R2, R3, R4, R5, R6, R7, R8
|
||||
};
|
||||
count = sizeof(allocationOrder) / sizeof(const int);
|
||||
return allocationOrder;
|
||||
}
|
||||
ARMReg *ArmRegCache::GetAllocationOrder(int &count)
|
||||
{
|
||||
// This will return us the allocation order of the registers we can use on
|
||||
// the host side.
|
||||
static ARMReg allocationOrder[] =
|
||||
{
|
||||
R14, R12, R11, R10
|
||||
};
|
||||
count = sizeof(allocationOrder) / sizeof(const int);
|
||||
return allocationOrder;
|
||||
}
|
||||
|
||||
ARMReg ArmRegCache::GetReg(bool AutoLock)
|
||||
{
|
||||
for(u8 a = 0; a < NUMARMREG; ++a)
|
||||
if(ArmRegs[a].free)
|
||||
{
|
||||
// Alright, this one is free
|
||||
if (AutoLock)
|
||||
ArmRegs[a].free = false;
|
||||
return ArmRegs[a].Reg;
|
||||
}
|
||||
// Uh Oh, we have all them locked....
|
||||
_assert_msg_(_DYNA_REC_, false, "All available registers are locked dumb dumb");
|
||||
return R0;
|
||||
}
|
||||
void ArmRegCache::Lock(ARMReg Reg)
|
||||
{
|
||||
for(u8 RegNum = 0; RegNum < NUMARMREG; ++RegNum)
|
||||
if(ArmRegs[RegNum].Reg == Reg)
|
||||
{
|
||||
_assert_msg_(_DYNA_REC, ArmRegs[RegNum].free, "This register is already locked");
|
||||
ArmRegs[RegNum].free = false;
|
||||
}
|
||||
_assert_msg_(_DYNA_REC, false, "Register %d can't be used with lock", Reg);
|
||||
}
|
||||
void ArmRegCache::Unlock(ARMReg R0, ARMReg R1, ARMReg R2, ARMReg R3)
|
||||
{
|
||||
for(u8 RegNum = 0; RegNum < NUMARMREG; ++RegNum)
|
||||
{
|
||||
if(ArmRegs[RegNum].Reg == R0)
|
||||
{
|
||||
_assert_msg_(_DYNA_REC, !ArmRegs[RegNum].free, "This register is already unlocked");
|
||||
ArmRegs[RegNum].free = true;
|
||||
}
|
||||
if( R1 != INVALID_REG && ArmRegs[RegNum].Reg == R1) ArmRegs[RegNum].free = true;
|
||||
if( R2 != INVALID_REG && ArmRegs[RegNum].Reg == R2) ArmRegs[RegNum].free = true;
|
||||
if( R3 != INVALID_REG && ArmRegs[RegNum].Reg == R3) ArmRegs[RegNum].free = true;
|
||||
}
|
||||
}
|
||||
|
||||
ARMReg ArmRegCache::R(u32 preg)
|
||||
{
|
||||
u32 HighestUsed = 0;
|
||||
u8 Num = 0;
|
||||
for(u8 a = 0; a < NUMPPCREG; ++a){
|
||||
++ArmCRegs[a].LastLoad;
|
||||
if (ArmCRegs[a].LastLoad > HighestUsed)
|
||||
{
|
||||
HighestUsed = ArmCRegs[a].LastLoad;
|
||||
Num = a;
|
||||
}
|
||||
}
|
||||
// Check if already Loaded
|
||||
for(u8 a = 0; a < NUMPPCREG; ++a)
|
||||
if (ArmCRegs[a].PPCReg == preg)
|
||||
{
|
||||
ArmCRegs[a].LastLoad = 0;
|
||||
return ArmCRegs[a].Reg;
|
||||
}
|
||||
// Check if we have a free register
|
||||
for (u8 a = 0; a < NUMPPCREG; ++a)
|
||||
if (ArmCRegs[a].PPCReg == 33)
|
||||
{
|
||||
emit->LDR(ArmCRegs[a].Reg, R9, STRUCT_OFF(PowerPC::ppcState, gpr) + preg * 4);
|
||||
ArmCRegs[a].PPCReg = preg;
|
||||
ArmCRegs[a].LastLoad = 0;
|
||||
return ArmCRegs[a].Reg;
|
||||
}
|
||||
// Alright, we couldn't get a free space, dump that least used register
|
||||
emit->STR(R9, ArmCRegs[Num].Reg, STRUCT_OFF(PowerPC::ppcState, gpr) + ArmCRegs[Num].PPCReg * 4);
|
||||
emit->LDR(ArmCRegs[Num].Reg, R9, STRUCT_OFF(PowerPC::ppcState, gpr) + preg * 4);
|
||||
ArmCRegs[Num].PPCReg = preg;
|
||||
ArmCRegs[Num].LastLoad = 0;
|
||||
return ArmCRegs[Num].Reg;
|
||||
}
|
||||
|
||||
void ArmRegCache::Flush()
|
||||
{
|
||||
for(u8 a = 0; a < NUMPPCREG; ++a)
|
||||
if (ArmCRegs[a].PPCReg != 33)
|
||||
{
|
||||
emit->STR(R9, ArmCRegs[a].Reg, STRUCT_OFF(PowerPC::ppcState, gpr) + ArmCRegs[a].PPCReg * 4);
|
||||
ArmCRegs[a].PPCReg = 33;
|
||||
ArmCRegs[a].LastLoad = 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _JITARMREGCACHE_H
|
||||
#define _JITARMREGCACHE_H
|
||||
|
||||
#include "ArmEmitter.h"
|
||||
#include "../Gekko.h"
|
||||
#include "../PPCAnalyst.h"
|
||||
|
||||
using namespace ArmGen;
|
||||
// This ARM Register cache actually pre loads the most used registers before
|
||||
// the block to increase speed since every memory load requires two
|
||||
// instructions to load it. We are going to use R0-RMAX as registers for the
|
||||
// use of PPC Registers.
|
||||
// Allocation order as follows
|
||||
#define ARMREGS 16
|
||||
// Allocate R0 to R9 for PPC first.
|
||||
// For General registers on the host side, start with R14 and go down as we go
|
||||
// R13 is reserved for our stack pointer, don't ever use that. Unless you save
|
||||
// it
|
||||
// So we have R14, R12, R11, R10 to work with instructions
|
||||
|
||||
struct PPCCachedReg
|
||||
{
|
||||
const u8 *location;
|
||||
u32 UsesLeft;
|
||||
};
|
||||
struct JRCPPC
|
||||
{
|
||||
u32 PPCReg; // Tied to which PPC Register
|
||||
bool PS1;
|
||||
ARMReg Reg; // Tied to which ARM Register
|
||||
u32 LastLoad;
|
||||
};
|
||||
struct JRCReg
|
||||
{
|
||||
ARMReg Reg; // Which reg this is.
|
||||
bool free;
|
||||
};
|
||||
class ArmRegCache
|
||||
{
|
||||
private:
|
||||
PPCCachedReg regs[32];
|
||||
JRCPPC ArmCRegs[ARMREGS];
|
||||
JRCReg ArmRegs[ARMREGS]; // Four registers remaining
|
||||
|
||||
int NUMPPCREG;
|
||||
int NUMARMREG;
|
||||
|
||||
ARMReg *GetAllocationOrder(int &count);
|
||||
ARMReg *GetPPCAllocationOrder(int &count);
|
||||
|
||||
protected:
|
||||
ARMXEmitter *emit;
|
||||
|
||||
public:
|
||||
ArmRegCache();
|
||||
~ArmRegCache() {}
|
||||
|
||||
void Init(ARMXEmitter *emitter);
|
||||
void Start(PPCAnalyst::BlockRegStats &stats);
|
||||
|
||||
void SetEmitter(ARMXEmitter *emitter) {emit = emitter;}
|
||||
|
||||
ARMReg GetReg(bool AutoLock = true); // Return a ARM register we can use.
|
||||
void Lock(ARMReg reg);
|
||||
void Unlock(ARMReg R0, ARMReg R1 = INVALID_REG, ARMReg R2 = INVALID_REG, ARMReg R3 =
|
||||
INVALID_REG);
|
||||
void Flush();
|
||||
ARMReg R(u32 preg); // Returns a cached register
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
|
@ -15,7 +15,7 @@
|
|||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
#include "Thunk.h"
|
||||
#include "CPUDetect.h"
|
||||
#include "x64Emitter.h"
|
||||
|
@ -26,7 +26,7 @@
|
|||
#include "../../CoreTiming.h"
|
||||
#include "MemoryUtil.h"
|
||||
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
#include "../JitCommon/JitCache.h"
|
||||
|
||||
#include "../../HW/GPFifo.h"
|
||||
|
|
|
@ -21,16 +21,8 @@
|
|||
#include "../JitCommon/Jit_Util.h"
|
||||
#include "Thunk.h"
|
||||
|
||||
class CommonAsmRoutines : public EmuCodeBlock {
|
||||
protected:
|
||||
void GenQuantizedLoads();
|
||||
void GenQuantizedStores();
|
||||
void GenQuantizedSingleStores();
|
||||
|
||||
class CommonAsmRoutinesBase {
|
||||
public:
|
||||
void GenFifoWrite(int size);
|
||||
void GenFifoXmm64Write();
|
||||
void GenFifoFloatWrite();
|
||||
|
||||
const u8 *fifoDirectWrite8;
|
||||
const u8 *fifoDirectWrite16;
|
||||
|
@ -72,8 +64,23 @@ public:
|
|||
// In: XMM0: Bottom 32-bit slot holds the float to be written.
|
||||
const u8 **singleStoreQuantized;
|
||||
|
||||
};
|
||||
|
||||
class CommonAsmRoutines : public CommonAsmRoutinesBase, public EmuCodeBlock
|
||||
{
|
||||
protected:
|
||||
void GenQuantizedLoads();
|
||||
void GenQuantizedStores();
|
||||
void GenQuantizedSingleStores();
|
||||
|
||||
public:
|
||||
void GenFifoWrite(int size);
|
||||
void GenFifoXmm64Write();
|
||||
void GenFifoFloatWrite();
|
||||
|
||||
private:
|
||||
ThunkManager thunks;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#include "../../HW/Memmap.h"
|
||||
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
#include "Thunk.h"
|
||||
#include "x64Analyzer.h"
|
||||
|
||||
|
@ -160,7 +160,7 @@ const u8 *TrampolineCache::GetWriteTrampoline(const InstructionInfo &info)
|
|||
// 1) It's really necessary. We don't know anything about the context.
|
||||
// 2) It doesn't really hurt. Only instructions that access I/O will get these, and there won't be
|
||||
// that many of them in a typical program/game.
|
||||
const u8 *JitBase::BackPatch(u8 *codePtr, int accessType, u32 emAddress, void *ctx_void)
|
||||
const u8 *Jitx86Base::BackPatch(u8 *codePtr, int accessType, u32 emAddress, void *ctx_void)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
CONTEXT *ctx = (CONTEXT *)ctx_void;
|
||||
|
|
|
@ -34,6 +34,9 @@
|
|||
// from the real context.
|
||||
struct CONTEXT
|
||||
{
|
||||
#ifdef _M_ARM
|
||||
u32 reg_pc;
|
||||
#else
|
||||
#ifdef _M_X64
|
||||
u64 Rip;
|
||||
u64 Rax;
|
||||
|
@ -41,6 +44,7 @@
|
|||
u32 Eip;
|
||||
u32 Eax;
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -32,12 +32,9 @@
|
|||
|
||||
#define JIT_OPCODE 0
|
||||
|
||||
class JitBase : public CPUCoreBase, public EmuCodeBlock
|
||||
class JitBase : public CPUCoreBase
|
||||
{
|
||||
protected:
|
||||
JitBlockCache blocks;
|
||||
TrampolineCache trampolines;
|
||||
|
||||
struct JitOptions
|
||||
{
|
||||
bool optimizeStack;
|
||||
|
@ -85,14 +82,29 @@ public:
|
|||
// This should probably be removed from public:
|
||||
JitOptions jo;
|
||||
JitState js;
|
||||
|
||||
JitBlockCache *GetBlockCache() { return &blocks; }
|
||||
|
||||
virtual JitBaseBlockCache *GetBlockCache() = 0;
|
||||
|
||||
virtual void Jit(u32 em_address) = 0;
|
||||
|
||||
virtual const u8 *BackPatch(u8 *codePtr, int accessType, u32 em_address, void *ctx) = 0;
|
||||
|
||||
virtual const CommonAsmRoutinesBase *GetAsmRoutines() = 0;
|
||||
|
||||
virtual bool IsInCodeSpace(u8 *ptr) = 0;
|
||||
};
|
||||
|
||||
class Jitx86Base : public JitBase, public EmuCodeBlock
|
||||
{
|
||||
protected:
|
||||
JitBlockCache blocks;
|
||||
TrampolineCache trampolines;
|
||||
public:
|
||||
JitBlockCache *GetBlockCache() { return &blocks; }
|
||||
|
||||
const u8 *BackPatch(u8 *codePtr, int accessType, u32 em_address, void *ctx);
|
||||
|
||||
virtual const CommonAsmRoutines *GetAsmRoutines() = 0;
|
||||
bool IsInCodeSpace(u8 *ptr) { return IsInSpace(ptr); }
|
||||
};
|
||||
|
||||
extern JitBase *jit;
|
||||
|
|
|
@ -32,15 +32,13 @@
|
|||
#include "MemoryUtil.h"
|
||||
|
||||
#include "../../HW/Memmap.h"
|
||||
#include "../JitInterface.h"
|
||||
#include "../../CoreTiming.h"
|
||||
|
||||
#include "../PowerPC.h"
|
||||
#include "../PPCTables.h"
|
||||
#include "../PPCAnalyst.h"
|
||||
|
||||
#include "x64Emitter.h"
|
||||
#include "x64Analyzer.h"
|
||||
|
||||
#include "JitCache.h"
|
||||
#include "JitBase.h"
|
||||
|
||||
|
@ -68,12 +66,12 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
return (em_address >= originalAddress && em_address < originalAddress + 4 * originalSize);
|
||||
}
|
||||
|
||||
bool JitBlockCache::IsFull() const
|
||||
bool JitBaseBlockCache::IsFull() const
|
||||
{
|
||||
return GetNumBlocks() >= MAX_NUM_BLOCKS - 1;
|
||||
}
|
||||
|
||||
void JitBlockCache::Init()
|
||||
void JitBaseBlockCache::Init()
|
||||
{
|
||||
MAX_NUM_BLOCKS = 65536*2;
|
||||
|
||||
|
@ -91,11 +89,11 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
}
|
||||
else
|
||||
{
|
||||
PanicAlert("JitBlockCache::Init() - iCache is already initialized");
|
||||
PanicAlert("JitBaseBlockCache::Init() - iCache is already initialized");
|
||||
}
|
||||
if (iCache == 0 || iCacheEx == 0 || iCacheVMEM == 0)
|
||||
{
|
||||
PanicAlert("JitBlockCache::Init() - unable to allocate iCache");
|
||||
PanicAlert("JitBaseBlockCache::Init() - unable to allocate iCache");
|
||||
}
|
||||
memset(iCache, JIT_ICACHE_INVALID_BYTE, JIT_ICACHE_SIZE);
|
||||
memset(iCacheEx, JIT_ICACHE_INVALID_BYTE, JIT_ICACHEEX_SIZE);
|
||||
|
@ -104,7 +102,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
Clear();
|
||||
}
|
||||
|
||||
void JitBlockCache::Shutdown()
|
||||
void JitBaseBlockCache::Shutdown()
|
||||
{
|
||||
delete[] blocks;
|
||||
delete[] blockCodePointers;
|
||||
|
@ -133,7 +131,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
|
||||
// This clears the JIT cache. It's called from JitCache.cpp when the JIT cache
|
||||
// is full and when saving and loading states.
|
||||
void JitBlockCache::Clear()
|
||||
void JitBaseBlockCache::Clear()
|
||||
{
|
||||
if (IsFull())
|
||||
Core::DisplayMessage("Clearing block cache.", 3000);
|
||||
|
@ -151,7 +149,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
memset(blockCodePointers, 0, sizeof(u8*)*MAX_NUM_BLOCKS);
|
||||
}
|
||||
|
||||
void JitBlockCache::ClearSafe()
|
||||
void JitBaseBlockCache::ClearSafe()
|
||||
{
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
memset(iCache, JIT_ICACHE_INVALID_BYTE, JIT_ICACHE_SIZE);
|
||||
|
@ -160,7 +158,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
#endif
|
||||
}
|
||||
|
||||
/*void JitBlockCache::DestroyBlocksWithFlag(BlockFlag death_flag)
|
||||
/*void JitBaseBlockCache::DestroyBlocksWithFlag(BlockFlag death_flag)
|
||||
{
|
||||
for (int i = 0; i < num_blocks; i++)
|
||||
{
|
||||
|
@ -171,23 +169,23 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
}
|
||||
}*/
|
||||
|
||||
void JitBlockCache::Reset()
|
||||
void JitBaseBlockCache::Reset()
|
||||
{
|
||||
Shutdown();
|
||||
Init();
|
||||
}
|
||||
|
||||
JitBlock *JitBlockCache::GetBlock(int no)
|
||||
JitBlock *JitBaseBlockCache::GetBlock(int no)
|
||||
{
|
||||
return &blocks[no];
|
||||
}
|
||||
|
||||
int JitBlockCache::GetNumBlocks() const
|
||||
int JitBaseBlockCache::GetNumBlocks() const
|
||||
{
|
||||
return num_blocks;
|
||||
}
|
||||
|
||||
bool JitBlockCache::RangeIntersect(int s1, int e1, int s2, int e2) const
|
||||
bool JitBaseBlockCache::RangeIntersect(int s1, int e1, int s2, int e2) const
|
||||
{
|
||||
// check if any endpoint is inside the other range
|
||||
if ((s1 >= s2 && s1 <= e2) ||
|
||||
|
@ -199,7 +197,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
return false;
|
||||
}
|
||||
|
||||
int JitBlockCache::AllocateBlock(u32 em_address)
|
||||
int JitBaseBlockCache::AllocateBlock(u32 em_address)
|
||||
{
|
||||
JitBlock &b = blocks[num_blocks];
|
||||
b.invalid = false;
|
||||
|
@ -215,12 +213,12 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
return num_blocks - 1;
|
||||
}
|
||||
|
||||
void JitBlockCache::FinalizeBlock(int block_num, bool block_link, const u8 *code_ptr)
|
||||
void JitBaseBlockCache::FinalizeBlock(int block_num, bool block_link, const u8 *code_ptr)
|
||||
{
|
||||
blockCodePointers[block_num] = code_ptr;
|
||||
JitBlock &b = blocks[block_num];
|
||||
b.originalFirstOpcode = Memory::Read_Opcode_JIT(b.originalAddress);
|
||||
Memory::Write_Opcode_JIT(b.originalAddress, (JIT_OPCODE << 26) | block_num);
|
||||
b.originalFirstOpcode = JitInterface::Read_Opcode_JIT(b.originalAddress);
|
||||
JitInterface::Write_Opcode_JIT(b.originalAddress, (JIT_OPCODE << 26) | block_num);
|
||||
|
||||
// Convert the logical address to a physical address for the block map
|
||||
u32 pAddr = b.originalAddress & 0x1FFFFFFF;
|
||||
|
@ -264,29 +262,29 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
#endif
|
||||
}
|
||||
|
||||
const u8 **JitBlockCache::GetCodePointers()
|
||||
const u8 **JitBaseBlockCache::GetCodePointers()
|
||||
{
|
||||
return blockCodePointers;
|
||||
}
|
||||
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
u8* JitBlockCache::GetICache()
|
||||
u8* JitBaseBlockCache::GetICache()
|
||||
{
|
||||
return iCache;
|
||||
}
|
||||
|
||||
u8* JitBlockCache::GetICacheEx()
|
||||
u8* JitBaseBlockCache::GetICacheEx()
|
||||
{
|
||||
return iCacheEx;
|
||||
}
|
||||
|
||||
u8* JitBlockCache::GetICacheVMEM()
|
||||
u8* JitBaseBlockCache::GetICacheVMEM()
|
||||
{
|
||||
return iCacheVMEM;
|
||||
}
|
||||
#endif
|
||||
|
||||
int JitBlockCache::GetBlockNumberFromStartAddress(u32 addr)
|
||||
int JitBaseBlockCache::GetBlockNumberFromStartAddress(u32 addr)
|
||||
{
|
||||
if (!blocks)
|
||||
return -1;
|
||||
|
@ -317,24 +315,24 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
return inst;
|
||||
}
|
||||
|
||||
void JitBlockCache::GetBlockNumbersFromAddress(u32 em_address, std::vector<int> *block_numbers)
|
||||
void JitBaseBlockCache::GetBlockNumbersFromAddress(u32 em_address, std::vector<int> *block_numbers)
|
||||
{
|
||||
for (int i = 0; i < num_blocks; i++)
|
||||
if (blocks[i].ContainsAddress(em_address))
|
||||
block_numbers->push_back(i);
|
||||
}
|
||||
|
||||
u32 JitBlockCache::GetOriginalFirstOp(int block_num)
|
||||
u32 JitBaseBlockCache::GetOriginalFirstOp(int block_num)
|
||||
{
|
||||
if (block_num >= num_blocks)
|
||||
{
|
||||
//PanicAlert("JitBlockCache::GetOriginalFirstOp - block_num = %u is out of range", block_num);
|
||||
//PanicAlert("JitBaseBlockCache::GetOriginalFirstOp - block_num = %u is out of range", block_num);
|
||||
return block_num;
|
||||
}
|
||||
return blocks[block_num].originalFirstOpcode;
|
||||
}
|
||||
|
||||
CompiledCode JitBlockCache::GetCompiledCodeFromBlock(int block_num)
|
||||
CompiledCode JitBaseBlockCache::GetCompiledCodeFromBlock(int block_num)
|
||||
{
|
||||
return (CompiledCode)blockCodePointers[block_num];
|
||||
}
|
||||
|
@ -345,7 +343,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
//Can be faster by doing a queue for blocks to link up, and only process those
|
||||
//Should probably be done
|
||||
|
||||
void JitBlockCache::LinkBlockExits(int i)
|
||||
void JitBaseBlockCache::LinkBlockExits(int i)
|
||||
{
|
||||
JitBlock &b = blocks[i];
|
||||
if (b.invalid)
|
||||
|
@ -360,8 +358,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
int destinationBlock = GetBlockNumberFromStartAddress(b.exitAddress[e]);
|
||||
if (destinationBlock != -1)
|
||||
{
|
||||
XEmitter emit(b.exitPtrs[e]);
|
||||
emit.JMP(blocks[destinationBlock].checkedEntry, true);
|
||||
WriteLinkBlock(b.exitPtrs[e], blocks[destinationBlock].checkedEntry);
|
||||
b.linkStatus[e] = true;
|
||||
}
|
||||
}
|
||||
|
@ -370,7 +367,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
|
||||
using namespace std;
|
||||
|
||||
void JitBlockCache::LinkBlock(int i)
|
||||
void JitBaseBlockCache::LinkBlock(int i)
|
||||
{
|
||||
LinkBlockExits(i);
|
||||
JitBlock &b = blocks[i];
|
||||
|
@ -386,7 +383,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
}
|
||||
}
|
||||
|
||||
void JitBlockCache::UnlinkBlock(int i)
|
||||
void JitBaseBlockCache::UnlinkBlock(int i)
|
||||
{
|
||||
JitBlock &b = blocks[i];
|
||||
pair<multimap<u32, int>::iterator, multimap<u32, int>::iterator> ppp;
|
||||
|
@ -403,7 +400,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
}
|
||||
}
|
||||
|
||||
void JitBlockCache::DestroyBlock(int block_num, bool invalidate)
|
||||
void JitBaseBlockCache::DestroyBlock(int block_num, bool invalidate)
|
||||
{
|
||||
if (block_num < 0 || block_num >= num_blocks)
|
||||
{
|
||||
|
@ -419,7 +416,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
}
|
||||
b.invalid = true;
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
Memory::Write_Opcode_JIT(b.originalAddress, b.originalFirstOpcode?b.originalFirstOpcode:JIT_ICACHE_INVALID_WORD);
|
||||
JitInterface::Write_Opcode_JIT(b.originalAddress, b.originalFirstOpcode?b.originalFirstOpcode:JIT_ICACHE_INVALID_WORD);
|
||||
#else
|
||||
if (Memory::ReadFast32(b.originalAddress) == block_num)
|
||||
Memory::WriteUnchecked_U32(b.originalFirstOpcode, b.originalAddress);
|
||||
|
@ -430,18 +427,10 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
// Send anyone who tries to run this block back to the dispatcher.
|
||||
// Not entirely ideal, but .. pretty good.
|
||||
// Spurious entrances from previously linked blocks can only come through checkedEntry
|
||||
XEmitter emit((u8 *)b.checkedEntry);
|
||||
emit.MOV(32, M(&PC), Imm32(b.originalAddress));
|
||||
emit.JMP(jit->GetAsmRoutines()->dispatcher, true);
|
||||
// this is not needed really
|
||||
/*
|
||||
emit.SetCodePtr((u8 *)blockCodePointers[blocknum]);
|
||||
emit.MOV(32, M(&PC), Imm32(b.originalAddress));
|
||||
emit.JMP(asm_routines.dispatcher, true);
|
||||
*/
|
||||
WriteDestroyBlock(b.checkedEntry, b.originalAddress);
|
||||
}
|
||||
|
||||
void JitBlockCache::InvalidateICache(u32 address, const u32 length)
|
||||
void JitBaseBlockCache::InvalidateICache(u32 address, const u32 length)
|
||||
{
|
||||
// Convert the logical address to a physical address for the block map
|
||||
u32 pAddr = address & 0x1FFFFFFF;
|
||||
|
@ -449,7 +438,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
// Optimize the common case of length == 32 which is used by Interpreter::dcb*
|
||||
bool destroy_block = true;
|
||||
if (length == 32)
|
||||
{
|
||||
{
|
||||
if (!valid_block[pAddr / 32])
|
||||
destroy_block = false;
|
||||
else
|
||||
|
@ -462,7 +451,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
{
|
||||
std::map<pair<u32,u32>, u32>::iterator it1 = block_map.lower_bound(std::make_pair(pAddr, 0)), it2 = it1;
|
||||
while (it2 != block_map.end() && it2->first.second < pAddr + length)
|
||||
{
|
||||
{
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
JitBlock &b = blocks[it2->second];
|
||||
if (b.originalAddress & JIT_ICACHE_VMEM_BIT)
|
||||
|
@ -516,3 +505,14 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
}
|
||||
#endif
|
||||
}
|
||||
void JitBlockCache::WriteLinkBlock(u8* location, const u8* address)
|
||||
{
|
||||
XEmitter emit(location);
|
||||
emit.JMP(address, true);
|
||||
}
|
||||
void JitBlockCache::WriteDestroyBlock(const u8* location, u32 address)
|
||||
{
|
||||
XEmitter emit((u8 *)location);
|
||||
emit.MOV(32, M(&PC), Imm32(address));
|
||||
emit.JMP(jit->GetAsmRoutines()->dispatcher, true);
|
||||
}
|
||||
|
|
|
@ -78,7 +78,8 @@ struct JitBlock
|
|||
|
||||
typedef void (*CompiledCode)();
|
||||
|
||||
class JitBlockCache
|
||||
|
||||
class JitBaseBlockCache
|
||||
{
|
||||
const u8 **blockCodePointers;
|
||||
JitBlock *blocks;
|
||||
|
@ -97,9 +98,13 @@ class JitBlockCache
|
|||
void LinkBlockExits(int i);
|
||||
void LinkBlock(int i);
|
||||
void UnlinkBlock(int i);
|
||||
|
||||
// Virtual for overloaded
|
||||
virtual void WriteLinkBlock(u8* location, const u8* address) = 0;
|
||||
virtual void WriteDestroyBlock(const u8* location, u32 address) = 0;
|
||||
|
||||
public:
|
||||
JitBlockCache() :
|
||||
JitBaseBlockCache() :
|
||||
blockCodePointers(0), blocks(0), num_blocks(0),
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
iCache(0), iCacheEx(0), iCacheVMEM(0),
|
||||
|
@ -146,4 +151,11 @@ public:
|
|||
//void DestroyBlocksWithFlag(BlockFlag death_flag);
|
||||
};
|
||||
|
||||
// x86 BlockCache
|
||||
class JitBlockCache : public JitBaseBlockCache
|
||||
{
|
||||
private:
|
||||
void WriteLinkBlock(u8* location, const u8* address);
|
||||
void WriteDestroyBlock(const u8* location, u32 address);
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#include "../../HW/Memmap.h"
|
||||
#include "../PPCTables.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
#include "JitBase.h"
|
||||
#include "Jit_Util.h"
|
||||
|
||||
|
|
|
@ -0,0 +1,350 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "JitInterface.h"
|
||||
#include "JitCommon/JitBase.h"
|
||||
|
||||
#ifndef _M_GENERIC
|
||||
#include "Jit64IL/JitIL.h"
|
||||
#include "Jit64/Jit.h"
|
||||
#include "Jit64/Jit64_Tables.h"
|
||||
#include "Jit64IL/JitIL_Tables.h"
|
||||
#endif
|
||||
|
||||
#ifdef _M_ARM
|
||||
#include "JitArm32/Jit.h"
|
||||
#include "JitArm32/JitArm_Tables.h"
|
||||
#endif
|
||||
|
||||
#include "Profiler.h"
|
||||
#include "PPCSymbolDB.h"
|
||||
#include "HW/Memmap.h"
|
||||
#include "ConfigManager.h"
|
||||
|
||||
bool bFakeVMEM = false;
|
||||
bool bMMU = false;
|
||||
|
||||
namespace JitInterface
|
||||
{
|
||||
void DoState(PointerWrap &p)
|
||||
{
|
||||
if (jit && p.GetMode() == PointerWrap::MODE_READ)
|
||||
jit->GetBlockCache()->ClearSafe();
|
||||
}
|
||||
CPUCoreBase *InitJitCore(int core)
|
||||
{
|
||||
bFakeVMEM = SConfig::GetInstance().m_LocalCoreStartupParameter.iTLBHack == 1;
|
||||
bMMU = SConfig::GetInstance().m_LocalCoreStartupParameter.bMMU;
|
||||
|
||||
CPUCoreBase *ptr = NULL;
|
||||
switch(core)
|
||||
{
|
||||
#ifndef _M_GENERIC
|
||||
case 1:
|
||||
{
|
||||
ptr = new Jit64();
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
ptr = new JitIL();
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef _M_ARM
|
||||
case 3:
|
||||
{
|
||||
ptr = new JitArm();
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
{
|
||||
PanicAlert("Unrecognizable cpu_core: %d", core);
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
jit = static_cast<JitBase*>(ptr);
|
||||
jit->Init();
|
||||
return ptr;
|
||||
}
|
||||
void InitTables(int core)
|
||||
{
|
||||
switch(core)
|
||||
{
|
||||
#ifndef _M_GENERIC
|
||||
case 1:
|
||||
{
|
||||
Jit64Tables::InitTables();
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
JitILTables::InitTables();
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef _M_ARM
|
||||
case 3:
|
||||
{
|
||||
JitArmTables::InitTables();
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
{
|
||||
PanicAlert("Unrecognizable cpu_core: %d", core);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
CPUCoreBase *GetCore()
|
||||
{
|
||||
return jit;
|
||||
}
|
||||
|
||||
void WriteProfileResults(const char *filename)
|
||||
{
|
||||
// Can't really do this with no jit core available
|
||||
#ifndef _M_GENERIC
|
||||
|
||||
std::vector<BlockStat> stats;
|
||||
stats.reserve(jit->GetBlockCache()->GetNumBlocks());
|
||||
u64 cost_sum = 0;
|
||||
#ifdef _WIN32
|
||||
u64 timecost_sum = 0;
|
||||
u64 countsPerSec;
|
||||
QueryPerformanceFrequency((LARGE_INTEGER *)&countsPerSec);
|
||||
#endif
|
||||
for (int i = 0; i < jit->GetBlockCache()->GetNumBlocks(); i++)
|
||||
{
|
||||
const JitBlock *block = jit->GetBlockCache()->GetBlock(i);
|
||||
// Rough heuristic. Mem instructions should cost more.
|
||||
u64 cost = block->originalSize * (block->runCount / 4);
|
||||
#ifdef _WIN32
|
||||
u64 timecost = block->ticCounter;
|
||||
#endif
|
||||
// Todo: tweak.
|
||||
if (block->runCount >= 1)
|
||||
stats.push_back(BlockStat(i, cost));
|
||||
cost_sum += cost;
|
||||
#ifdef _WIN32
|
||||
timecost_sum += timecost;
|
||||
#endif
|
||||
}
|
||||
|
||||
sort(stats.begin(), stats.end());
|
||||
File::IOFile f(filename, "w");
|
||||
if (!f)
|
||||
{
|
||||
PanicAlert("failed to open %s", filename);
|
||||
return;
|
||||
}
|
||||
fprintf(f.GetHandle(), "origAddr\tblkName\tcost\ttimeCost\tpercent\ttimePercent\tOvAllinBlkTime(ms)\tblkCodeSize\n");
|
||||
for (unsigned int i = 0; i < stats.size(); i++)
|
||||
{
|
||||
const JitBlock *block = jit->GetBlockCache()->GetBlock(stats[i].blockNum);
|
||||
if (block)
|
||||
{
|
||||
std::string name = g_symbolDB.GetDescription(block->originalAddress);
|
||||
double percent = 100.0 * (double)stats[i].cost / (double)cost_sum;
|
||||
#ifdef _WIN32
|
||||
double timePercent = 100.0 * (double)block->ticCounter / (double)timecost_sum;
|
||||
fprintf(f.GetHandle(), "%08x\t%s\t%llu\t%llu\t%.2lf\t%llf\t%lf\t%i\n",
|
||||
block->originalAddress, name.c_str(), stats[i].cost,
|
||||
block->ticCounter, percent, timePercent,
|
||||
(double)block->ticCounter*1000.0/(double)countsPerSec, block->codeSize);
|
||||
#else
|
||||
fprintf(f.GetHandle(), "%08x\t%s\t%llu\t???\t%.2lf\t???\t???\t%i\n",
|
||||
block->originalAddress, name.c_str(), stats[i].cost, percent, block->codeSize);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
bool IsInCodeSpace(u8 *ptr)
|
||||
{
|
||||
return jit->IsInCodeSpace(ptr);
|
||||
}
|
||||
const u8 *BackPatch(u8 *codePtr, int accessType, u32 em_address, void *ctx)
|
||||
{
|
||||
return jit->BackPatch(codePtr, accessType, em_address, ctx);
|
||||
}
|
||||
|
||||
void ClearCache()
|
||||
{
|
||||
if (jit)
|
||||
jit->ClearCache();
|
||||
}
|
||||
void ClearSafe()
|
||||
{
|
||||
if (jit)
|
||||
jit->GetBlockCache()->ClearSafe();
|
||||
}
|
||||
|
||||
void InvalidateICache(u32 address, u32 size)
|
||||
{
|
||||
if (jit)
|
||||
jit->GetBlockCache()->InvalidateICache(address, size);
|
||||
}
|
||||
|
||||
|
||||
// Memory functions
|
||||
u32 Read_Opcode_JIT_Uncached(const u32 _Address)
|
||||
{
|
||||
u8* iCache;
|
||||
u32 addr;
|
||||
if (_Address & JIT_ICACHE_VMEM_BIT)
|
||||
{
|
||||
iCache = jit->GetBlockCache()->GetICacheVMEM();
|
||||
addr = _Address & JIT_ICACHE_MASK;
|
||||
}
|
||||
else if (_Address & JIT_ICACHE_EXRAM_BIT)
|
||||
{
|
||||
iCache = jit->GetBlockCache()->GetICacheEx();
|
||||
addr = _Address & JIT_ICACHEEX_MASK;
|
||||
}
|
||||
else
|
||||
{
|
||||
iCache = jit->GetBlockCache()->GetICache();
|
||||
addr = _Address & JIT_ICACHE_MASK;
|
||||
}
|
||||
u32 inst = *(u32*)(iCache + addr);
|
||||
if (inst == JIT_ICACHE_INVALID_WORD)
|
||||
{
|
||||
u32 cache_block_start = addr & ~0x1f;
|
||||
u32 mem_block_start = _Address & ~0x1f;
|
||||
u8 *pMem = Memory::GetPointer(mem_block_start);
|
||||
memcpy(iCache + cache_block_start, pMem, 32);
|
||||
inst = *(u32*)(iCache + addr);
|
||||
}
|
||||
inst = Common::swap32(inst);
|
||||
|
||||
if ((inst & 0xfc000000) == 0)
|
||||
{
|
||||
inst = jit->GetBlockCache()->GetOriginalFirstOp(inst);
|
||||
}
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
u32 Read_Opcode_JIT(u32 _Address)
|
||||
{
|
||||
#ifdef FAST_ICACHE
|
||||
if (bMMU && !bFakeVMEM && (_Address & Memory::ADDR_MASK_MEM1))
|
||||
{
|
||||
_Address = Memory::TranslateAddress(_Address, Memory::FLAG_OPCODE);
|
||||
if (_Address == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
u32 inst = 0;
|
||||
|
||||
// Bypass the icache for the external interrupt exception handler
|
||||
if ( (_Address & 0x0FFFFF00) == 0x00000500 )
|
||||
inst = Read_Opcode_JIT_Uncached(_Address);
|
||||
else
|
||||
inst = PowerPC::ppcState.iCache.ReadInstruction(_Address);
|
||||
#else
|
||||
u32 inst = Memory::ReadUnchecked_U32(_Address);
|
||||
#endif
|
||||
return inst;
|
||||
}
|
||||
|
||||
// The following function is deprecated in favour of FAST_ICACHE
|
||||
u32 Read_Opcode_JIT_LC(const u32 _Address)
|
||||
{
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
if ((_Address & ~JIT_ICACHE_MASK) != 0x80000000 && (_Address & ~JIT_ICACHE_MASK) != 0x00000000 &&
|
||||
(_Address & ~JIT_ICACHE_MASK) != 0x7e000000 && // TLB area
|
||||
(_Address & ~JIT_ICACHEEX_MASK) != 0x90000000 && (_Address & ~JIT_ICACHEEX_MASK) != 0x10000000)
|
||||
{
|
||||
PanicAlertT("iCacheJIT: Reading Opcode from %x. Please report.", _Address);
|
||||
ERROR_LOG(MEMMAP, "iCacheJIT: Reading Opcode from %x. Please report.", _Address);
|
||||
return 0;
|
||||
}
|
||||
u8* iCache;
|
||||
u32 addr;
|
||||
if (_Address & JIT_ICACHE_VMEM_BIT)
|
||||
{
|
||||
iCache = jit->GetBlockCache()->GetICacheVMEM();
|
||||
addr = _Address & JIT_ICACHE_MASK;
|
||||
}
|
||||
else if (_Address & JIT_ICACHE_EXRAM_BIT)
|
||||
{
|
||||
iCache = jit->GetBlockCache()->GetICacheEx();
|
||||
addr = _Address & JIT_ICACHEEX_MASK;
|
||||
}
|
||||
else
|
||||
{
|
||||
iCache = jit->GetBlockCache()->GetICache();
|
||||
addr = _Address & JIT_ICACHE_MASK;
|
||||
}
|
||||
u32 inst = *(u32*)(iCache + addr);
|
||||
if (inst == JIT_ICACHE_INVALID_WORD)
|
||||
inst = Memory::ReadUnchecked_U32(_Address);
|
||||
else
|
||||
inst = Common::swap32(inst);
|
||||
#else
|
||||
u32 inst = Memory::ReadUnchecked_U32(_Address);
|
||||
#endif
|
||||
if ((inst & 0xfc000000) == 0)
|
||||
{
|
||||
inst = jit->GetBlockCache()->GetOriginalFirstOp(inst);
|
||||
}
|
||||
return inst;
|
||||
}
|
||||
|
||||
// WARNING! No checks!
|
||||
// We assume that _Address is cached
|
||||
void Write_Opcode_JIT(const u32 _Address, const u32 _Value)
|
||||
{
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
if (_Address & JIT_ICACHE_VMEM_BIT)
|
||||
{
|
||||
*(u32*)(jit->GetBlockCache()->GetICacheVMEM() + (_Address & JIT_ICACHE_MASK)) = Common::swap32(_Value);
|
||||
}
|
||||
else if (_Address & JIT_ICACHE_EXRAM_BIT)
|
||||
{
|
||||
*(u32*)(jit->GetBlockCache()->GetICacheEx() + (_Address & JIT_ICACHEEX_MASK)) = Common::swap32(_Value);
|
||||
}
|
||||
else
|
||||
*(u32*)(jit->GetBlockCache()->GetICache() + (_Address & JIT_ICACHE_MASK)) = Common::swap32(_Value);
|
||||
#else
|
||||
Memory::WriteUnchecked_U32(_Value, _Address);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void Shutdown()
|
||||
{
|
||||
if (jit)
|
||||
{
|
||||
jit->Shutdown();
|
||||
delete jit;
|
||||
jit = NULL;
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue