ARM Support without GLSL

This commit is contained in:
Ryan Houdek 2013-02-26 13:49:00 -06:00
parent 46adbfa9ed
commit 717b976875
133 changed files with 9048 additions and 948 deletions

View File

@ -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)

View File

@ -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})

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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>

View File

@ -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" />

View File

@ -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;
}

View File

@ -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));
}
}
}
}

View File

@ -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_

View File

@ -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();

View File

@ -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.

View File

@ -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_

View File

@ -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"

View File

@ -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

View File

@ -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;

View File

@ -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()
{
}
}

View File

@ -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);
}

View File

@ -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)

View File

@ -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))

View File

@ -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);

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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>

View File

@ -17,7 +17,7 @@
#include "Common.h"
#include "x64Emitter.h"
#include "ABI.h"
#include "x64ABI.h"
using namespace Gen;

View File

@ -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__ (

View File

@ -17,7 +17,7 @@
#include "Common.h"
#include "x64Emitter.h"
#include "ABI.h"
#include "x64ABI.h"
#include "CPUDetect.h"
namespace Gen

View File

@ -757,7 +757,7 @@ public:
region_size = 0;
}
bool IsInCodeSpace(u8 *ptr)
bool IsInSpace(u8 *ptr)
{
return ptr >= region && ptr < region + region_size;
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -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)

View File

@ -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" />

View File

@ -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" />

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -25,7 +25,7 @@
#include "DSPJitUtil.h"
#endif
#include "x64Emitter.h"
#include "ABI.h"
#include "x64ABI.h"
using namespace Gen;
// CLR $acR

View File

@ -21,7 +21,7 @@
#include "../DSPAnalyzer.h"
#include "DSPJitUtil.h"
#include "x64Emitter.h"
#include "ABI.h"
#include "x64ABI.h"
using namespace Gen;

View File

@ -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

View File

@ -18,7 +18,7 @@
#include "../DSPEmitter.h"
#include "DSPJitUtil.h"
#include "x64Emitter.h"
#include "ABI.h"
#include "x64ABI.h"
using namespace Gen;

View File

@ -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)

View File

@ -20,7 +20,7 @@
#include "../DSPEmitter.h"
#include "DSPJitUtil.h"
#include "x64Emitter.h"
#include "ABI.h"
#include "x64ABI.h"
using namespace Gen;
//clobbers:

View File

@ -27,7 +27,7 @@
#include "DSPJitUtil.h"
#endif
#include "x64Emitter.h"
#include "ABI.h"
#include "x64ABI.h"
using namespace Gen;
// Returns s64 in RAX

View File

@ -20,7 +20,7 @@
#include "../DSPEmitter.h"
#include "DSPJitUtil.h"
#include "x64Emitter.h"
#include "ABI.h"
#include "x64ABI.h"
using namespace Gen;

View File

@ -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);

View File

@ -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);

View File

@ -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));
}
}

View File

@ -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();
}

View File

@ -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)

View File

@ -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}},

View File

@ -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);
}

View File

@ -62,7 +62,7 @@
if (js.memcheck) \
SetJumpTarget(memException);
class Jit64 : public JitBase
class Jit64 : public Jitx86Base
{
private:
GPRRegCache gpr;

View File

@ -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);

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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);
}

View File

@ -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"

View File

@ -57,7 +57,7 @@
#define DISABLE64
#endif
class JitIL : public JitBase
class JitIL : public Jitx86Base
{
private:

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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;
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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);*/
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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);
}

View File

@ -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

View File

@ -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"

View File

@ -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