Merge branch 'master' into FIFO-BP
# By Jordan Woyak (46) and others # Via Jordan Woyak (2) and others * master: (70 commits) Fixes two memory leaks, one is pretty bad for OSX. Yell at pauldachz if this doesn't work. Or... say thanks. Added a BluetoothEnumerateInstalledServices call so that the wiimote remembers the pairing. Make ARMJit core default CPU core on ARM architecture Fix a StringUtil regression from the arm-noglsl merge Small improvement to cmpli/cmpi in ARMJit. Merge latest ArmEmitter changes from ppsspp while we're at it. Ah. I blame vim on this typo entirely. Add disabled code for authenticating wiimotes on Windows. Add the missing FPR cache Buildfix. Yell at the user if they change window size while dumping frames, and some other avi dumping stuff. Not sure if this is the right way to handle this, but it makes the save states perfectly stable. That's all that really matters, right? Abort loading states from incompatible graphics backends. ARM Support without GLSL Improve VideoSoftware save states. They are fairly stable, but not perfect. OpcodeDecoder::DoState() needs to be fixed. Begin implementing save states to video software. Kind of works, sometimes. Make error message for loading save state with wrong dsp engine shorter. Abort load state if it uses a different dsp engine, instead of crashing. Update the gameini of F-zero. Efb to Ram is no longer the default choice. fix last commit by neobrain ... Conflicts: Source/Core/VideoCommon/Src/Fifo.cpp
This commit is contained in:
@@ -6,6 +6,11 @@ set(SRCS Src/AudioCommon.cpp
|
||||
|
||||
set(LIBS "")
|
||||
|
||||
if(ANDROID)
|
||||
set(SRCS ${SRCS} Src/OpenSLESStream.cpp)
|
||||
set(LIBS ${LIBS} OpenSLES)
|
||||
endif(ANDROID)
|
||||
|
||||
if(ALSA_FOUND)
|
||||
set(SRCS ${SRCS} Src/AlsaSoundStream.cpp)
|
||||
set(LIBS ${LIBS} ${ALSA_LIBRARIES})
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "CoreAudioSoundStream.h"
|
||||
#include "OpenALStream.h"
|
||||
#include "PulseAudioStream.h"
|
||||
#include "OpenSLESStream.h"
|
||||
#include "../../Core/Src/Movie.h"
|
||||
#include "../../Core/Src/ConfigManager.h"
|
||||
|
||||
@@ -55,7 +56,8 @@ namespace AudioCommon
|
||||
soundStream = new CoreAudioSound(mixer);
|
||||
else if (backend == BACKEND_PULSEAUDIO && PulseAudio::isValid())
|
||||
soundStream = new PulseAudio(mixer);
|
||||
|
||||
else if (backend == BACKEND_OPENSLES && OpenSLESStream::isValid())
|
||||
soundStream = new OpenSLESStream(mixer);
|
||||
if (soundStream != NULL)
|
||||
{
|
||||
UpdateSoundStream();
|
||||
@@ -116,7 +118,8 @@ namespace AudioCommon
|
||||
backends.push_back(BACKEND_PULSEAUDIO);
|
||||
if (OpenALStream::isValid())
|
||||
backends.push_back(BACKEND_OPENAL);
|
||||
|
||||
if (OpenSLESStream::isValid())
|
||||
backends.push_back(BACKEND_OPENSLES);
|
||||
return backends;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifdef ANDROID
|
||||
#include "Common.h"
|
||||
#include <assert.h>
|
||||
#include "OpenSLESStream.h"
|
||||
|
||||
#include <SLES/OpenSLES.h>
|
||||
#include <SLES/OpenSLES_Android.h>
|
||||
|
||||
// engine interfaces
|
||||
static SLObjectItf engineObject;
|
||||
static SLEngineItf engineEngine;
|
||||
static SLObjectItf outputMixObject;
|
||||
|
||||
// buffer queue player interfaces
|
||||
static SLObjectItf bqPlayerObject = NULL;
|
||||
static SLPlayItf bqPlayerPlay;
|
||||
static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;
|
||||
static SLMuteSoloItf bqPlayerMuteSolo;
|
||||
static SLVolumeItf bqPlayerVolume;
|
||||
static CMixer *g_mixer;
|
||||
#define BUFFER_SIZE 512
|
||||
#define BUFFER_SIZE_IN_SAMPLES (BUFFER_SIZE / 2)
|
||||
|
||||
// Double buffering.
|
||||
static short buffer[2][BUFFER_SIZE];
|
||||
static int curBuffer = 0;
|
||||
|
||||
static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) {
|
||||
assert(bq == bqPlayerBufferQueue);
|
||||
assert(NULL == context);
|
||||
|
||||
short *nextBuffer = buffer[curBuffer];
|
||||
int nextSize = sizeof(buffer[0]);
|
||||
|
||||
SLresult result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, nextBuffer, nextSize);
|
||||
|
||||
// Comment from sample code:
|
||||
// the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT,
|
||||
// which for this code example would indicate a programming error
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
|
||||
curBuffer ^= 1; // Switch buffer
|
||||
// Render to the fresh buffer
|
||||
g_mixer->Mix(reinterpret_cast<short *>(buffer[curBuffer]), BUFFER_SIZE_IN_SAMPLES);
|
||||
}
|
||||
bool OpenSLESStream::Start()
|
||||
{
|
||||
SLresult result;
|
||||
// create engine
|
||||
result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, 0, 0);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
|
||||
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
|
||||
SLDataFormat_PCM format_pcm = {
|
||||
SL_DATAFORMAT_PCM,
|
||||
2,
|
||||
SL_SAMPLINGRATE_44_1,
|
||||
SL_PCMSAMPLEFORMAT_FIXED_16,
|
||||
SL_PCMSAMPLEFORMAT_FIXED_16,
|
||||
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
|
||||
SL_BYTEORDER_LITTLEENDIAN
|
||||
};
|
||||
|
||||
SLDataSource audioSrc = {&loc_bufq, &format_pcm};
|
||||
|
||||
// configure audio sink
|
||||
SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
|
||||
SLDataSink audioSnk = {&loc_outmix, NULL};
|
||||
|
||||
// create audio player
|
||||
const SLInterfaceID ids[2] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME};
|
||||
const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
|
||||
result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk, 2, ids, req);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
|
||||
result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE,
|
||||
&bqPlayerBufferQueue);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, NULL);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
|
||||
// Render and enqueue a first buffer. (or should we just play the buffer empty?)
|
||||
curBuffer = 0;
|
||||
|
||||
result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer[curBuffer], sizeof(buffer[curBuffer]));
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
return false;
|
||||
}
|
||||
curBuffer ^= 1;
|
||||
g_mixer = m_mixer;
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenSLESStream::Stop()
|
||||
{
|
||||
if (bqPlayerObject != NULL) {
|
||||
(*bqPlayerObject)->Destroy(bqPlayerObject);
|
||||
bqPlayerObject = NULL;
|
||||
bqPlayerPlay = NULL;
|
||||
bqPlayerBufferQueue = NULL;
|
||||
bqPlayerMuteSolo = NULL;
|
||||
bqPlayerVolume = NULL;
|
||||
}
|
||||
if (outputMixObject != NULL) {
|
||||
(*outputMixObject)->Destroy(outputMixObject);
|
||||
outputMixObject = NULL;
|
||||
}
|
||||
if (engineObject != NULL) {
|
||||
(*engineObject)->Destroy(engineObject);
|
||||
engineObject = NULL;
|
||||
engineEngine = NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _OPENSLSTREAM_H_
|
||||
#define _OPENSLSTREAM_H_
|
||||
|
||||
#include "Thread.h"
|
||||
#include "SoundStream.h"
|
||||
|
||||
class OpenSLESStream : public SoundStream
|
||||
{
|
||||
#ifdef ANDROID
|
||||
public:
|
||||
OpenSLESStream(CMixer *mixer, void *hWnd = NULL)
|
||||
: SoundStream(mixer)
|
||||
{};
|
||||
|
||||
virtual ~OpenSLESStream() {};
|
||||
|
||||
virtual bool Start();
|
||||
virtual void Stop();
|
||||
static bool isValid() { return true; }
|
||||
virtual bool usesMixer() const { return true; }
|
||||
|
||||
private:
|
||||
std::thread thread;
|
||||
Common::Event soundSyncEvent;
|
||||
#else
|
||||
public:
|
||||
OpenSLESStream(CMixer *mixer, void *hWnd = NULL): SoundStream(mixer) {}
|
||||
#endif // HAVE_OPENSL
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,9 +1,7 @@
|
||||
set(SRCS Src/ABI.cpp
|
||||
Src/BreakPoints.cpp
|
||||
set(SRCS Src/BreakPoints.cpp
|
||||
Src/CDUtils.cpp
|
||||
Src/ColorUtil.cpp
|
||||
Src/ConsoleListener.cpp
|
||||
Src/CPUDetect.cpp
|
||||
Src/FileSearch.cpp
|
||||
Src/FileUtil.cpp
|
||||
Src/Hash.cpp
|
||||
@@ -20,10 +18,10 @@ set(SRCS Src/ABI.cpp
|
||||
Src/SymbolDB.cpp
|
||||
Src/SysConf.cpp
|
||||
Src/Thread.cpp
|
||||
Src/Thunk.cpp
|
||||
Src/Timer.cpp
|
||||
Src/Version.cpp
|
||||
Src/VideoBackendBase.cpp
|
||||
Src/x64ABI.cpp
|
||||
Src/x64Analyzer.cpp
|
||||
Src/x64Emitter.cpp
|
||||
Src/Crypto/aes_cbc.cpp
|
||||
@@ -33,6 +31,23 @@ set(SRCS Src/ABI.cpp
|
||||
Src/Crypto/md5.cpp
|
||||
Src/Crypto/sha1.cpp)
|
||||
|
||||
if(_M_ARM) #ARM
|
||||
set(SRCS ${SRCS}
|
||||
Src/ArmCPUDetect.cpp
|
||||
Src/ArmEmitter.cpp)
|
||||
else()
|
||||
if(NOT _M_GENERIC) #X86
|
||||
set(SRCS ${SRCS}
|
||||
Src/x64FPURoundMode.cpp
|
||||
Src/x64Thunk.cpp
|
||||
)
|
||||
endif()
|
||||
set(SRCS ${SRCS} Src/x64CPUDetect.cpp)
|
||||
endif()
|
||||
if(_M_GENERIC) #Generic
|
||||
set(SRCS ${SRCS}
|
||||
Src/GenericFPURoundMode.cpp)
|
||||
endif()
|
||||
if(WIN32)
|
||||
set(SRCS ${SRCS} Src/ExtendedTrace.cpp)
|
||||
endif(WIN32)
|
||||
|
||||
@@ -158,12 +158,10 @@
|
||||
<Lib />
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Src\ABI.cpp" />
|
||||
<ClCompile Include="Src\BreakPoints.cpp" />
|
||||
<ClCompile Include="Src\CDUtils.cpp" />
|
||||
<ClCompile Include="Src\ColorUtil.cpp" />
|
||||
<ClCompile Include="Src\ConsoleListener.cpp" />
|
||||
<ClCompile Include="Src\CPUDetect.cpp" />
|
||||
<ClCompile Include="Src\Crypto\aes_cbc.cpp" />
|
||||
<ClCompile Include="Src\Crypto\aes_core.cpp" />
|
||||
<ClCompile Include="Src\Crypto\bn.cpp" />
|
||||
@@ -195,15 +193,17 @@
|
||||
<ClCompile Include="Src\SymbolDB.cpp" />
|
||||
<ClCompile Include="Src\SysConf.cpp" />
|
||||
<ClCompile Include="Src\Thread.cpp" />
|
||||
<ClCompile Include="Src\Thunk.cpp" />
|
||||
<ClCompile Include="Src\Timer.cpp" />
|
||||
<ClCompile Include="Src\Version.cpp" />
|
||||
<ClCompile Include="Src\VideoBackendBase.cpp" />
|
||||
<ClCompile Include="Src\x64ABI.cpp" />
|
||||
<ClCompile Include="Src\x64Analyzer.cpp" />
|
||||
<ClCompile Include="Src\x64CPUDetect.cpp" />
|
||||
<ClCompile Include="Src\x64Emitter.cpp" />
|
||||
<ClCompile Include="Src\x64FPURoundMode.cpp" />
|
||||
<ClCompile Include="Src\x64Thunk.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Src\ABI.h" />
|
||||
<ClInclude Include="Src\Atomic.h" />
|
||||
<ClInclude Include="Src\Atomic_GCC.h" />
|
||||
<ClInclude Include="Src\Atomic_Win32.h" />
|
||||
@@ -251,6 +251,7 @@
|
||||
<ClInclude Include="Src\Thunk.h" />
|
||||
<ClInclude Include="Src\Timer.h" />
|
||||
<ClInclude Include="Src\VideoBackendBase.h" />
|
||||
<ClInclude Include="Src\x64ABI.h" />
|
||||
<ClInclude Include="Src\x64Analyzer.h" />
|
||||
<ClInclude Include="Src\x64Emitter.h" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Src\ABI.cpp" />
|
||||
<ClCompile Include="Src\BreakPoints.cpp" />
|
||||
<ClCompile Include="Src\CDUtils.cpp" />
|
||||
<ClCompile Include="Src\ColorUtil.cpp" />
|
||||
<ClCompile Include="Src\CPUDetect.cpp" />
|
||||
<ClCompile Include="Src\ExtendedTrace.cpp" />
|
||||
<ClCompile Include="Src\FileSearch.cpp" />
|
||||
<ClCompile Include="Src\FileUtil.cpp" />
|
||||
@@ -23,7 +21,6 @@
|
||||
<ClCompile Include="Src\SymbolDB.cpp" />
|
||||
<ClCompile Include="Src\SysConf.cpp" />
|
||||
<ClCompile Include="Src\Thread.cpp" />
|
||||
<ClCompile Include="Src\Thunk.cpp" />
|
||||
<ClCompile Include="Src\Timer.cpp" />
|
||||
<ClCompile Include="Src\Version.cpp" />
|
||||
<ClCompile Include="Src\VideoBackendBase.cpp" />
|
||||
@@ -53,9 +50,12 @@
|
||||
<ClCompile Include="Src\Crypto\sha1.cpp">
|
||||
<Filter>Crypto</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Src\x64ABI.cpp" />
|
||||
<ClCompile Include="Src\x64CPUDetect.cpp" />
|
||||
<ClCompile Include="Src\x64FPURoundMode.cpp" />
|
||||
<ClCompile Include="Src\x64Thunk.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Src\ABI.h" />
|
||||
<ClInclude Include="Src\Atomic.h" />
|
||||
<ClInclude Include="Src\Atomic_GCC.h" />
|
||||
<ClInclude Include="Src\Atomic_Win32.h" />
|
||||
@@ -121,6 +121,7 @@
|
||||
</ClInclude>
|
||||
<ClInclude Include="Src\StdMutex.h" />
|
||||
<ClInclude Include="Src\StdConditionVariable.h" />
|
||||
<ClInclude Include="Src\x64ABI.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="CMakeLists.txt" />
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "Common.h"
|
||||
#include "CPUDetect.h"
|
||||
#include "StringUtil.h"
|
||||
|
||||
const char procfile[] = "/proc/cpuinfo";
|
||||
|
||||
char *GetCPUString()
|
||||
{
|
||||
const char marker[] = "Hardware\t: ";
|
||||
char *cpu_string = 0;
|
||||
// Count the number of processor lines in /proc/cpuinfo
|
||||
char buf[1024];
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(procfile, "r");
|
||||
if (!fp)
|
||||
return 0;
|
||||
|
||||
while (fgets(buf, sizeof(buf), fp))
|
||||
{
|
||||
if (strncmp(buf, marker, sizeof(marker) - 1))
|
||||
continue;
|
||||
cpu_string = buf + sizeof(marker) - 1;
|
||||
cpu_string = strndup(cpu_string, strlen(cpu_string) - 1); // Strip the newline
|
||||
break;
|
||||
}
|
||||
return cpu_string;
|
||||
}
|
||||
bool CheckCPUFeature(const char *feature)
|
||||
{
|
||||
const char marker[] = "Features\t: ";
|
||||
char buf[1024];
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(procfile, "r");
|
||||
if (!fp)
|
||||
return 0;
|
||||
|
||||
while (fgets(buf, sizeof(buf), fp))
|
||||
{
|
||||
if (strncmp(buf, marker, sizeof(marker) - 1))
|
||||
continue;
|
||||
char *featurestring = buf + sizeof(marker) - 1;
|
||||
char *token = strtok(featurestring, " ");
|
||||
while (token != NULL)
|
||||
{
|
||||
if (strstr(token, feature))
|
||||
return true;
|
||||
token = strtok(NULL, " ");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
int GetCoreCount()
|
||||
{
|
||||
const char marker[] = "processor\t: ";
|
||||
int cores = 0;
|
||||
char buf[1024];
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(procfile, "r");
|
||||
if (!fp)
|
||||
return 0;
|
||||
|
||||
while (fgets(buf, sizeof(buf), fp))
|
||||
{
|
||||
if (strncmp(buf, marker, sizeof(marker) - 1))
|
||||
continue;
|
||||
++cores;
|
||||
}
|
||||
return cores;
|
||||
}
|
||||
|
||||
CPUInfo cpu_info;
|
||||
|
||||
CPUInfo::CPUInfo() {
|
||||
Detect();
|
||||
}
|
||||
|
||||
// Detects the various cpu features
|
||||
void CPUInfo::Detect()
|
||||
{
|
||||
// Set some defaults here
|
||||
// When ARMv8 cpus come out, these need to be updated.
|
||||
HTT = false;
|
||||
OS64bit = false;
|
||||
CPU64bit = false;
|
||||
Mode64bit = false;
|
||||
vendor = VENDOR_ARM;
|
||||
|
||||
// Get the information about the CPU
|
||||
strncpy(cpu_string, GetCPUString(), sizeof(cpu_string));
|
||||
num_cores = GetCoreCount();
|
||||
bSwp = CheckCPUFeature("swp");
|
||||
bHalf = CheckCPUFeature("half");
|
||||
bThumb = CheckCPUFeature("thumb");
|
||||
bFastMult = CheckCPUFeature("fastmult");
|
||||
bVFP = CheckCPUFeature("vfp");
|
||||
bEDSP = CheckCPUFeature("edsp");
|
||||
bThumbEE = CheckCPUFeature("thumbee");
|
||||
bNEON = CheckCPUFeature("neon");
|
||||
bVFPv3 = CheckCPUFeature("vfpv3");
|
||||
bTLS = CheckCPUFeature("tls");
|
||||
bVFPv4 = CheckCPUFeature("vfpv4");
|
||||
bIDIVa = CheckCPUFeature("idiva");
|
||||
bIDIVt = CheckCPUFeature("idivt");
|
||||
// These two are ARMv8 specific.
|
||||
bFP = CheckCPUFeature("fp");
|
||||
bASIMD = CheckCPUFeature("asimd");
|
||||
|
||||
|
||||
#if defined(__ARM_ARCH_7A__)
|
||||
bArmV7 = true;
|
||||
#else
|
||||
bArmV7 = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Turn the cpu info into a string we can show
|
||||
std::string CPUInfo::Summarize()
|
||||
{
|
||||
std::string sum;
|
||||
if (num_cores == 1)
|
||||
sum = StringFromFormat("%s, %i core", cpu_string, num_cores);
|
||||
else
|
||||
sum = StringFromFormat("%s, %i cores", cpu_string, num_cores);
|
||||
|
||||
if (bSwp) sum += ", SWP";
|
||||
if (bHalf) sum += ", Half";
|
||||
if (bThumb) sum += ", Thumb";
|
||||
if (bFastMult) sum += ", FastMult";
|
||||
if (bVFP) sum += ", VFP";
|
||||
if (bEDSP) sum += ", EDSP";
|
||||
if (bThumbEE) sum += ", ThumbEE";
|
||||
if (bNEON) sum += ", NEON";
|
||||
if (bVFPv3) sum += ", VFPv3";
|
||||
if (bTLS) sum += ", TLS";
|
||||
if (bVFPv4) sum += ", VFPv4";
|
||||
if (bIDIVa) sum += ", IDIVa";
|
||||
if (bIDIVt) sum += ", IDIVt";
|
||||
|
||||
return sum;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,589 @@
|
||||
// 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 offset);
|
||||
void VSTR(ARMReg Src, ARMReg Base, u16 offset);
|
||||
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 VADD(ARMReg Vd, ARMReg Vn, ARMReg Vm);
|
||||
void VSUB(ARMReg Vd, ARMReg Vn, ARMReg Vm);
|
||||
void VABS(ARMReg Vd, ARMReg Vm);
|
||||
void VNEG(ARMReg Vd, ARMReg Vm);
|
||||
void VMUL(ARMReg Vd, ARMReg Vn, ARMReg Vm);
|
||||
void VMOV(ARMReg Dest, ARMReg Src, bool high);
|
||||
void VMOV(ARMReg Dest, ARMReg Src);
|
||||
|
||||
void QuickCallFunction(ARMReg scratchreg, void *func);
|
||||
// Utility functions
|
||||
void MOVI2R(ARMReg reg, u32 val, bool optimize = true);
|
||||
void ARMABI_MOVI2M(Operand2 op, Operand2 val);
|
||||
}; // class ARMXEmitter
|
||||
|
||||
|
||||
// Everything that needs to generate X86 code should inherit from this.
|
||||
// You get memory management for free, plus, you can use all the MOV etc functions without
|
||||
// having to prefix them with gen-> or something similar.
|
||||
class ARMXCodeBlock : public ARMXEmitter
|
||||
{
|
||||
protected:
|
||||
u8 *region;
|
||||
size_t region_size;
|
||||
|
||||
public:
|
||||
ARMXCodeBlock() : region(NULL), region_size(0) {}
|
||||
virtual ~ARMXCodeBlock() { if (region) FreeCodeSpace(); }
|
||||
|
||||
// Call this before you generate any code.
|
||||
void AllocCodeSpace(int size)
|
||||
{
|
||||
region_size = size;
|
||||
region = (u8*)AllocateExecutableMemory(region_size);
|
||||
SetCodePtr(region);
|
||||
}
|
||||
|
||||
// Always clear code space with breakpoints, so that if someone accidentally executes
|
||||
// uninitialized, it just breaks into the debugger.
|
||||
void ClearCodeSpace()
|
||||
{
|
||||
// x86/64: 0xCC = breakpoint
|
||||
memset(region, 0xCC, region_size);
|
||||
ResetCodePtr();
|
||||
}
|
||||
|
||||
// Call this when shutting down. Don't rely on the destructor, even though it'll do the job.
|
||||
void FreeCodeSpace()
|
||||
{
|
||||
FreeMemoryPages(region, region_size);
|
||||
region = NULL;
|
||||
region_size = 0;
|
||||
}
|
||||
|
||||
bool IsInSpace(u8 *ptr)
|
||||
{
|
||||
return ptr >= region && ptr < region + region_size;
|
||||
}
|
||||
|
||||
// Cannot currently be undone. Will write protect the entire code region.
|
||||
// Start over if you need to change the code (call FreeCodeSpace(), AllocCodeSpace()).
|
||||
void WriteProtect()
|
||||
{
|
||||
WriteProtectMemory(region, region_size, true);
|
||||
}
|
||||
|
||||
void ResetCodePtr()
|
||||
{
|
||||
SetCodePtr(region);
|
||||
}
|
||||
|
||||
size_t GetSpaceLeft() const
|
||||
{
|
||||
return region_size - (GetCodePtr() - region);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif // _DOLPHIN_INTEL_CODEGEN_
|
||||
@@ -25,7 +25,8 @@ enum CPUVendor
|
||||
{
|
||||
VENDOR_INTEL = 0,
|
||||
VENDOR_AMD = 1,
|
||||
VENDOR_OTHER = 2,
|
||||
VENDOR_ARM = 2,
|
||||
VENDOR_OTHER = 3,
|
||||
};
|
||||
|
||||
struct CPUInfo
|
||||
@@ -55,6 +56,26 @@ struct CPUInfo
|
||||
bool bAES;
|
||||
bool bLAHFSAHF64;
|
||||
bool bLongMode;
|
||||
|
||||
// ARM specific CPUInfo
|
||||
bool bSwp;
|
||||
bool bHalf;
|
||||
bool bThumb;
|
||||
bool bFastMult;
|
||||
bool bVFP;
|
||||
bool bEDSP;
|
||||
bool bThumbEE;
|
||||
bool bNEON;
|
||||
bool bVFPv3;
|
||||
bool bTLS;
|
||||
bool bVFPv4;
|
||||
bool bIDIVa;
|
||||
bool bIDIVt;
|
||||
bool bArmV7; // enable MOVT, MOVW etc
|
||||
|
||||
// ARMv8 specific
|
||||
bool bFP;
|
||||
bool bASIMD;
|
||||
|
||||
// Call Detect()
|
||||
explicit CPUInfo();
|
||||
|
||||
@@ -133,7 +133,9 @@ private:
|
||||
// wxWidgets does not have a true dummy macro for this.
|
||||
#define _trans(a) a
|
||||
|
||||
#if defined __GNUC__
|
||||
#if defined _M_GENERIC
|
||||
# define _M_SSE 0x0
|
||||
#elif defined __GNUC__
|
||||
# if defined __SSE4_2__
|
||||
# define _M_SSE 0x402
|
||||
# elif defined __SSE4_1__
|
||||
@@ -144,7 +146,7 @@ private:
|
||||
# define _M_SSE 0x300
|
||||
# endif
|
||||
#elif (_MSC_VER >= 1500) || __INTEL_COMPILER // Visual Studio 2008
|
||||
# define _M_SSE 0x402
|
||||
# define _M_SSE 0x402
|
||||
#endif
|
||||
|
||||
// Host communication.
|
||||
@@ -155,7 +157,6 @@ enum HOST_COMM
|
||||
WM_USER_CREATE,
|
||||
WM_USER_SETCURSOR,
|
||||
WM_USER_KEYDOWN,
|
||||
WIIMOTE_DISCONNECT // Disconnect Wiimote
|
||||
};
|
||||
|
||||
// Used for notification on emulation state
|
||||
|
||||
@@ -35,7 +35,7 @@ template<> struct CompileTimeAssert<true> {};
|
||||
#define b32(x) (b16(x) | (b16(x) >>16) )
|
||||
#define ROUND_UP_POW2(x) (b32(x - 1) + 1)
|
||||
|
||||
#if defined __GNUC__ && !defined __SSSE3__
|
||||
#if defined __GNUC__ && !defined __SSSE3__ && !defined _M_GENERIC
|
||||
#include <emmintrin.h>
|
||||
static __inline __m128i __attribute__((__always_inline__))
|
||||
_mm_shuffle_epi8(__m128i a, __m128i mask)
|
||||
@@ -60,6 +60,8 @@ _mm_shuffle_epi8(__m128i a, __m128i mask)
|
||||
// go to debugger mode
|
||||
#ifdef GEKKO
|
||||
#define Crash()
|
||||
#elif defined _M_GENERIC
|
||||
#define Crash() { exit(1); }
|
||||
#else
|
||||
#define Crash() {asm ("int $3");}
|
||||
#endif
|
||||
@@ -136,6 +138,15 @@ inline u8 swap8(u8 _data) {return _data;}
|
||||
inline u16 swap16(u16 _data) {return _byteswap_ushort(_data);}
|
||||
inline u32 swap32(u32 _data) {return _byteswap_ulong (_data);}
|
||||
inline u64 swap64(u64 _data) {return _byteswap_uint64(_data);}
|
||||
#elif _M_ARM
|
||||
#ifdef ANDROID
|
||||
#undef swap16
|
||||
#undef swap32
|
||||
#undef swap64
|
||||
#endif
|
||||
inline u16 swap16 (u16 _data) { u32 data = _data; __asm__ ("rev16 %0, %1\n" : "=l" (data) : "l" (data)); return (u16)data;}
|
||||
inline u32 swap32 (u32 _data) {__asm__ ("rev %0, %1\n" : "=l" (_data) : "l" (_data)); return _data;}
|
||||
inline u64 swap64(u64 _data) {return ((u64)swap32(_data) << 32) | swap32(_data >> 32);}
|
||||
#elif __linux__
|
||||
inline u16 swap16(u16 _data) {return bswap_16(_data);}
|
||||
inline u32 swap32(u32 _data) {return bswap_32(_data);}
|
||||
@@ -161,7 +172,6 @@ inline u64 swap64(u64 data) {return ((u64)swap32(data) << 32) | swap32(data >> 3
|
||||
inline u16 swap16(const u8* _pData) {return swap16(*(const u16*)_pData);}
|
||||
inline u32 swap32(const u8* _pData) {return swap32(*(const u32*)_pData);}
|
||||
inline u64 swap64(const u8* _pData) {return swap64(*(const u64*)_pData);}
|
||||
|
||||
} // Namespace Common
|
||||
|
||||
#endif // _COMMONFUNCS_H_
|
||||
|
||||
@@ -36,6 +36,9 @@
|
||||
// You can use the File::GetUserPath() util for this
|
||||
#define USERDATA_DIR "Contents/Resources/User"
|
||||
#define DOLPHIN_DATA_DIR "Library/Application Support/Dolphin"
|
||||
#elif defined ANDROID
|
||||
#define USERDATA_DIR "user"
|
||||
#define DOLPHIN_DATA_DIR "/sdcard/dolphin-emu"
|
||||
#else
|
||||
#define USERDATA_DIR "user"
|
||||
#ifdef USER_DIR
|
||||
@@ -52,6 +55,8 @@
|
||||
#define SYSDATA_DIR "Contents/Resources/Sys"
|
||||
#define SHARED_USER_DIR File::GetBundleDirectory() + \
|
||||
DIR_SEP USERDATA_DIR DIR_SEP
|
||||
#elif defined ANDROID
|
||||
#define SYSDATA_DIR "/sdcard/dolphin-emu"
|
||||
#else
|
||||
#ifdef DATA_DIR
|
||||
#define SYSDATA_DIR DATA_DIR "sys"
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
#ifndef FPU_ROUND_MODE_H_
|
||||
#define FPU_ROUND_MODE_H_
|
||||
#include "Common.h"
|
||||
|
||||
namespace FPURoundMode
|
||||
{
|
||||
enum RoundModes
|
||||
{
|
||||
ROUND_NEAR = 0,
|
||||
ROUND_CHOP,
|
||||
ROUND_UP,
|
||||
ROUND_DOWN
|
||||
};
|
||||
enum PrecisionModes {
|
||||
PREC_24 = 0,
|
||||
PREC_53,
|
||||
PREC_64
|
||||
};
|
||||
void SetRoundMode(u32 mode);
|
||||
|
||||
void SetPrecisionMode(u32 mode);
|
||||
|
||||
void SetSIMDMode(u32 mode);
|
||||
|
||||
/*
|
||||
There are two different flavors of float to int conversion:
|
||||
_mm_cvtps_epi32() and _mm_cvttps_epi32(). The first rounds
|
||||
according to the MXCSR rounding bits. The second one always
|
||||
uses round towards zero.
|
||||
*/
|
||||
void SaveSIMDState();
|
||||
void LoadSIMDState();
|
||||
void LoadDefaultSIMDState();
|
||||
}
|
||||
#endif
|
||||
@@ -668,9 +668,10 @@ std::string &GetUserPath(const unsigned int DirIDX, const std::string &newPath)
|
||||
if (File::Exists(ROOT_DIR DIR_SEP USERDATA_DIR))
|
||||
paths[D_USER_IDX] = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP;
|
||||
else
|
||||
paths[D_USER_IDX] = std::string(getenv("HOME") ? getenv("HOME") : getenv("PWD")) + DIR_SEP DOLPHIN_DATA_DIR DIR_SEP;
|
||||
paths[D_USER_IDX] = std::string(getenv("HOME") ?
|
||||
getenv("HOME") : getenv("PWD") ?
|
||||
getenv("PWD") : "") + DIR_SEP DOLPHIN_DATA_DIR DIR_SEP;
|
||||
#endif
|
||||
INFO_LOG(COMMON, "GetUserPath: Setting user directory to %s:", paths[D_USER_IDX].c_str());
|
||||
|
||||
paths[D_GCUSER_IDX] = paths[D_USER_IDX] + GC_USER_DIR DIR_SEP;
|
||||
paths[D_WIIROOT_IDX] = paths[D_USER_IDX] + WII_USER_DIR;
|
||||
@@ -781,9 +782,9 @@ IOFile::IOFile(IOFile&& other)
|
||||
Swap(other);
|
||||
}
|
||||
|
||||
IOFile& IOFile::operator=(IOFile other)
|
||||
IOFile& IOFile::operator=(IOFile&& other)
|
||||
{
|
||||
Swap(other);
|
||||
IOFile((IOFile&&)other).Swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
@@ -160,7 +160,7 @@ public:
|
||||
~IOFile();
|
||||
|
||||
IOFile(IOFile&& other);
|
||||
IOFile& operator=(IOFile other);
|
||||
IOFile& operator=(IOFile&& other);
|
||||
|
||||
void Swap(IOFile& other);
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "FPURoundMode.h"
|
||||
|
||||
// Generic, do nothing
|
||||
namespace FPURoundMode
|
||||
{
|
||||
void SetRoundMode(u32 mode)
|
||||
{
|
||||
}
|
||||
void SetPrecisionMode(u32 mode)
|
||||
{
|
||||
}
|
||||
void SetSIMDMode(u32 mode)
|
||||
{
|
||||
}
|
||||
void SaveSIMDState()
|
||||
{
|
||||
}
|
||||
void LoadSIMDState()
|
||||
{
|
||||
}
|
||||
void LoadDefaultSIMDState()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,9 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef ANDROID
|
||||
#include "Host.h"
|
||||
#endif
|
||||
#include "LogManager.h"
|
||||
#include "ConsoleListener.h"
|
||||
#include "Timer.h"
|
||||
@@ -132,7 +135,9 @@ void LogManager::Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type,
|
||||
Common::Timer::GetTimeFormatted().c_str(),
|
||||
file, line, level_to_char[(int)level],
|
||||
log->GetShortName(), temp);
|
||||
|
||||
#ifdef ANDROID
|
||||
Host_SysMessage(msg);
|
||||
#endif
|
||||
log->Trigger(level, msg);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,13 +21,6 @@
|
||||
#include <cmath>
|
||||
#include <numeric>
|
||||
|
||||
namespace {
|
||||
|
||||
static u32 saved_sse_state = _mm_getcsr();
|
||||
static const u32 default_sse_state = _mm_getcsr();
|
||||
|
||||
}
|
||||
|
||||
namespace MathUtil
|
||||
{
|
||||
|
||||
@@ -114,23 +107,6 @@ u32 ClassifyFloat(float fvalue)
|
||||
|
||||
} // namespace
|
||||
|
||||
void LoadDefaultSSEState()
|
||||
{
|
||||
_mm_setcsr(default_sse_state);
|
||||
}
|
||||
|
||||
|
||||
void LoadSSEState()
|
||||
{
|
||||
_mm_setcsr(saved_sse_state);
|
||||
}
|
||||
|
||||
|
||||
void SaveSSEState()
|
||||
{
|
||||
saved_sse_state = _mm_getcsr();
|
||||
}
|
||||
|
||||
inline void MatrixMul(int n, const float *a, const float *b, float *result)
|
||||
{
|
||||
for (int i = 0; i < n; ++i)
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
#include <xmmintrin.h>
|
||||
#include <vector>
|
||||
#include "FPURoundMode.h"
|
||||
|
||||
namespace MathUtil
|
||||
{
|
||||
@@ -147,17 +147,6 @@ struct Rectangle
|
||||
inline float pow2f(float x) {return x * x;}
|
||||
inline double pow2(double x) {return x * x;}
|
||||
|
||||
|
||||
/*
|
||||
There are two different flavors of float to int conversion:
|
||||
_mm_cvtps_epi32() and _mm_cvttps_epi32(). The first rounds
|
||||
according to the MXCSR rounding bits. The second one always
|
||||
uses round towards zero.
|
||||
*/
|
||||
|
||||
void SaveSSEState();
|
||||
void LoadSSEState();
|
||||
void LoadDefaultSSEState();
|
||||
float MathFloatVectorSum(const std::vector<float>&);
|
||||
|
||||
#define ROUND_UP(x, a) (((x) + (a) - 1) & ~((a) - 1))
|
||||
|
||||
@@ -27,6 +27,10 @@
|
||||
#include <unistd.h>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#ifdef ANDROID
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/ashmem.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
@@ -34,11 +38,41 @@ static const char* ram_temp_file = "/tmp/gc_mem.tmp";
|
||||
#elif !defined(_WIN32) // non OSX unixes
|
||||
static const char* ram_temp_file = "/dev/shm/gc_mem.tmp";
|
||||
#endif
|
||||
#ifdef ANDROID
|
||||
#define ASHMEM_DEVICE "/dev/ashmem"
|
||||
|
||||
int AshmemCreateFileMapping(const char *name, size_t size)
|
||||
{
|
||||
int fd, ret;
|
||||
fd = open(ASHMEM_DEVICE, O_RDWR);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
// We don't really care if we can't set the name, it is optional
|
||||
ret = ioctl(fd, ASHMEM_SET_NAME, name);
|
||||
|
||||
ret = ioctl(fd, ASHMEM_SET_SIZE, size);
|
||||
if (ret < 0)
|
||||
{
|
||||
close(fd);
|
||||
NOTICE_LOG(MEMMAP, "Ashmem returned error: 0x%08x", ret);
|
||||
return ret;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
#endif
|
||||
|
||||
void MemArena::GrabLowMemSpace(size_t size)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
hMemoryMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, (DWORD)(size), NULL);
|
||||
#elif defined(ANDROID)
|
||||
fd = AshmemCreateFileMapping("Dolphin-emu", size);
|
||||
if (fd < 0)
|
||||
{
|
||||
NOTICE_LOG(MEMMAP, "Ashmem allocation failed");
|
||||
return;
|
||||
}
|
||||
#else
|
||||
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
|
||||
fd = open(ram_temp_file, O_RDWR | O_CREAT, mode);
|
||||
|
||||
@@ -117,9 +117,12 @@ void* AllocateAlignedMemory(size_t size,size_t alignment)
|
||||
void* ptr = _aligned_malloc(size,alignment);
|
||||
#else
|
||||
void* ptr = NULL;
|
||||
#ifdef ANDROID
|
||||
ptr = memalign(alignment, size);
|
||||
#else
|
||||
if (posix_memalign(&ptr, alignment, size) != 0)
|
||||
ERROR_LOG(MEMMAP, "Failed to allocate aligned memory");
|
||||
;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// printf("Mapped memory at %p (size %ld)\n", ptr,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#define GCC_VER(x,y,z) ((x) * 10000 + (y) * 100 + (z))
|
||||
#define GCC_VERSION GCC_VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
|
||||
|
||||
#if GCC_VERSION >= GCC_VER(4,4,0) && __GXX_EXPERIMENTAL_CXX0X__
|
||||
#if GCC_VERSION >= GCC_VER(4,4,0) && __GXX_EXPERIMENTAL_CXX0X__ && !ANDROID
|
||||
// GCC 4.4 provides <condition_variable>
|
||||
#include <condition_variable>
|
||||
#else
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#define GCC_VER(x,y,z) ((x) * 10000 + (y) * 100 + (z))
|
||||
#define GCC_VERSION GCC_VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
|
||||
|
||||
#if GCC_VERSION >= GCC_VER(4,4,0) && __GXX_EXPERIMENTAL_CXX0X__
|
||||
#if GCC_VERSION >= GCC_VER(4,4,0) && __GXX_EXPERIMENTAL_CXX0X__ && !ANDROID
|
||||
// GCC 4.4 provides <mutex>
|
||||
#include <mutex>
|
||||
#else
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#define GCC_VER(x,y,z) ((x) * 10000 + (y) * 100 + (z))
|
||||
#define GCC_VERSION GCC_VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
|
||||
|
||||
#if GCC_VERSION >= GCC_VER(4,4,0) && __GXX_EXPERIMENTAL_CXX0X__
|
||||
#if GCC_VERSION >= GCC_VER(4,4,0) && __GXX_EXPERIMENTAL_CXX0X__ && !ANDROID
|
||||
// GCC 4.4 provides <thread>
|
||||
#ifndef _GLIBCXX_USE_SCHED_YIELD
|
||||
#define _GLIBCXX_USE_SCHED_YIELD
|
||||
|
||||
@@ -263,25 +263,25 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st
|
||||
const char HEX2DEC[256] =
|
||||
{
|
||||
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
|
||||
/* 0 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
/* 1 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
/* 2 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
/* 3 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1, -1,-1,-1,-1,
|
||||
/* 0 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
|
||||
/* 1 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
|
||||
/* 2 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
|
||||
/* 3 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,16,16, 16,16,16,16,
|
||||
|
||||
/* 4 */ -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
/* 5 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
/* 6 */ -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
/* 7 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
/* 4 */ 16,10,11,12, 13,14,15,16, 16,16,16,16, 16,16,16,16,
|
||||
/* 5 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
|
||||
/* 6 */ 16,10,11,12, 13,14,15,16, 16,16,16,16, 16,16,16,16,
|
||||
/* 7 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
|
||||
|
||||
/* 8 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
/* 9 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
/* A */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
/* B */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
/* 8 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
|
||||
/* 9 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
|
||||
/* A */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
|
||||
/* B */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
|
||||
|
||||
/* C */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
/* D */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
/* E */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
/* F */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1
|
||||
/* C */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
|
||||
/* D */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
|
||||
/* E */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
|
||||
/* F */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16
|
||||
};
|
||||
|
||||
std::string UriDecode(const std::string & sSrc)
|
||||
@@ -303,8 +303,8 @@ std::string UriDecode(const std::string & sSrc)
|
||||
if (*pSrc == '%')
|
||||
{
|
||||
char dec1, dec2;
|
||||
if (-1 != (dec1 = HEX2DEC[*(pSrc + 1)])
|
||||
&& -1 != (dec2 = HEX2DEC[*(pSrc + 2)]))
|
||||
if (16 != (dec1 = HEX2DEC[*(pSrc + 1)])
|
||||
&& 16 != (dec2 = HEX2DEC[*(pSrc + 2)]))
|
||||
{
|
||||
*pEnd++ = (dec1 << 4) + dec2;
|
||||
pSrc += 3;
|
||||
|
||||
@@ -105,7 +105,7 @@ void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask)
|
||||
#ifdef __APPLE__
|
||||
thread_policy_set(pthread_mach_thread_np(thread),
|
||||
THREAD_AFFINITY_POLICY, (integer_t *)&mask, 1);
|
||||
#elif defined __linux__ || defined BSD4_4
|
||||
#elif (defined __linux__ || defined BSD4_4) && !(defined ANDROID)
|
||||
cpu_set_t cpu_set;
|
||||
CPU_ZERO(&cpu_set);
|
||||
|
||||
|
||||
@@ -33,8 +33,6 @@
|
||||
#define INFINITE 0xffffffff
|
||||
#endif
|
||||
|
||||
#include <xmmintrin.h>
|
||||
|
||||
//for gettimeofday and struct time(spec|val)
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
#include "Common.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
|
||||
using namespace Gen;
|
||||
|
||||
@@ -30,7 +30,9 @@
|
||||
#else
|
||||
|
||||
//#include <config/i386/cpuid.h>
|
||||
#ifndef _M_GENERIC
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
|
||||
#if defined __FreeBSD__
|
||||
#include <sys/types.h>
|
||||
@@ -39,7 +41,9 @@
|
||||
static inline void do_cpuid(unsigned int *eax, unsigned int *ebx,
|
||||
unsigned int *ecx, unsigned int *edx)
|
||||
{
|
||||
#ifdef _LP64
|
||||
#if defined _M_GENERIC
|
||||
(*eax) = (*ebx) = (*ecx) = (*edx) = 0;
|
||||
#elif defined _LP64
|
||||
// Note: EBX is reserved on Mac OS X and in PIC on Linux, so it has to
|
||||
// restored at the end of the asm block.
|
||||
__asm__ (
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
#include "Common.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
#include "CPUDetect.h"
|
||||
|
||||
namespace Gen
|
||||
|
||||
@@ -757,7 +757,7 @@ public:
|
||||
region_size = 0;
|
||||
}
|
||||
|
||||
bool IsInCodeSpace(u8 *ptr)
|
||||
bool IsInSpace(u8 *ptr)
|
||||
{
|
||||
return ptr >= region && ptr < region + region_size;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "Common.h"
|
||||
#include "FPURoundMode.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
static const unsigned short FPU_ROUND_NEAR = 0 << 10;
|
||||
static const unsigned short FPU_ROUND_DOWN = 1 << 10;
|
||||
static const unsigned short FPU_ROUND_UP = 2 << 10;
|
||||
static const unsigned short FPU_ROUND_CHOP = 3 << 10;
|
||||
static const unsigned short FPU_ROUND_MASK = 3 << 10;
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
|
||||
const u32 MASKS = 0x1F80; // mask away the interrupts.
|
||||
const u32 DAZ = 0x40;
|
||||
const u32 FTZ = 0x8000;
|
||||
|
||||
namespace FPURoundMode
|
||||
{
|
||||
// Get the default SSE states here.
|
||||
static u32 saved_sse_state = _mm_getcsr();
|
||||
static const u32 default_sse_state = _mm_getcsr();
|
||||
|
||||
void SetRoundMode(u32 mode)
|
||||
{
|
||||
// Set FPU rounding mode to mimic the PowerPC's
|
||||
#ifdef _M_IX86
|
||||
// This shouldn't really be needed anymore since we use SSE
|
||||
#ifdef _WIN32
|
||||
const int table[4] =
|
||||
{
|
||||
_RC_NEAR,
|
||||
_RC_CHOP,
|
||||
_RC_UP,
|
||||
_RC_DOWN
|
||||
};
|
||||
_set_controlfp(_MCW_RC, table[mode]);
|
||||
#else
|
||||
const unsigned short table[4] =
|
||||
{
|
||||
FPU_ROUND_NEAR,
|
||||
FPU_ROUND_CHOP,
|
||||
FPU_ROUND_UP,
|
||||
FPU_ROUND_DOWN
|
||||
};
|
||||
unsigned short _mode;
|
||||
asm ("fstcw %0" : "=m" (_mode) : );
|
||||
_mode = (_mode & ~FPU_ROUND_MASK) | table[mode];
|
||||
asm ("fldcw %0" : : "m" (_mode));
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void SetPrecisionMode(u32 mode)
|
||||
{
|
||||
#ifdef _M_IX86
|
||||
// sets the floating-point lib to 53-bit
|
||||
// PowerPC has a 53bit floating pipeline only
|
||||
// eg: sscanf is very sensitive
|
||||
#ifdef _WIN32
|
||||
_control87(_PC_53, MCW_PC);
|
||||
#else
|
||||
const unsigned short table[4] = {
|
||||
0 << 8, // FPU_PREC_24
|
||||
2 << 8, // FPU_PREC_53
|
||||
3 << 8, // FPU_PREC_64
|
||||
3 << 8, // FPU_PREC_MASK
|
||||
};
|
||||
unsigned short _mode;
|
||||
asm ("fstcw %0" : : "m" (_mode));
|
||||
_mode = (_mode & ~table[4]) | table[mode];
|
||||
asm ("fldcw %0" : : "m" (_mode));
|
||||
#endif
|
||||
#else
|
||||
//x64 doesn't need this - fpu is done with SSE
|
||||
//but still - set any useful sse options here
|
||||
#endif
|
||||
}
|
||||
void SetSIMDMode(u32 mode)
|
||||
{
|
||||
static const u32 ssetable[4] =
|
||||
{
|
||||
(0 << 13) | MASKS,
|
||||
(3 << 13) | MASKS,
|
||||
(2 << 13) | MASKS,
|
||||
(1 << 13) | MASKS,
|
||||
};
|
||||
u32 csr = ssetable[mode];
|
||||
_mm_setcsr(csr);
|
||||
}
|
||||
|
||||
void SaveSIMDState()
|
||||
{
|
||||
saved_sse_state = _mm_getcsr();
|
||||
}
|
||||
void LoadSIMDState()
|
||||
{
|
||||
_mm_setcsr(saved_sse_state);
|
||||
}
|
||||
void LoadDefaultSIMDState()
|
||||
{
|
||||
_mm_setcsr(default_sse_state);
|
||||
}
|
||||
}
|
||||
@@ -18,9 +18,8 @@
|
||||
#include <map>
|
||||
|
||||
#include "Common.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "MemoryUtil.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
#include "Thunk.h"
|
||||
|
||||
#define THUNK_ARENA_SIZE 1024*1024*1
|
||||
@@ -9,7 +9,6 @@ set(SRCS Src/ActionReplay.cpp
|
||||
Src/DSPEmulator.cpp
|
||||
Src/GeckoCodeConfig.cpp
|
||||
Src/GeckoCode.cpp
|
||||
Src/MemTools.cpp
|
||||
Src/Movie.cpp
|
||||
Src/NetPlay.cpp
|
||||
Src/NetPlayClient.cpp
|
||||
@@ -153,6 +152,7 @@ set(SRCS Src/ActionReplay.cpp
|
||||
Src/PowerPC/PPCTables.cpp
|
||||
Src/PowerPC/Profiler.cpp
|
||||
Src/PowerPC/SignatureDB.cpp
|
||||
Src/PowerPC/JitInterface.cpp
|
||||
Src/PowerPC/Interpreter/Interpreter_Branch.cpp
|
||||
Src/PowerPC/Interpreter/Interpreter.cpp
|
||||
Src/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp
|
||||
@@ -162,6 +162,15 @@ set(SRCS Src/ActionReplay.cpp
|
||||
Src/PowerPC/Interpreter/Interpreter_Paired.cpp
|
||||
Src/PowerPC/Interpreter/Interpreter_SystemRegisters.cpp
|
||||
Src/PowerPC/Interpreter/Interpreter_Tables.cpp
|
||||
Src/PowerPC/JitCommon/JitAsmCommon.cpp
|
||||
Src/PowerPC/JitCommon/JitBackpatch.cpp
|
||||
Src/PowerPC/JitCommon/JitBase.cpp
|
||||
Src/PowerPC/JitCommon/JitCache.cpp
|
||||
Src/PowerPC/JitCommon/Jit_Util.cpp)
|
||||
|
||||
if(NOT _M_GENERIC)
|
||||
set(SRCS ${SRCS}
|
||||
Src/x64MemTools.cpp
|
||||
Src/PowerPC/Jit64IL/IR.cpp
|
||||
Src/PowerPC/Jit64IL/IR_X86.cpp
|
||||
Src/PowerPC/Jit64IL/JitILAsm.cpp
|
||||
@@ -186,12 +195,25 @@ set(SRCS Src/ActionReplay.cpp
|
||||
Src/PowerPC/Jit64/Jit_LoadStorePaired.cpp
|
||||
Src/PowerPC/Jit64/Jit_Paired.cpp
|
||||
Src/PowerPC/Jit64/JitRegCache.cpp
|
||||
Src/PowerPC/Jit64/Jit_SystemRegisters.cpp
|
||||
Src/PowerPC/JitCommon/JitAsmCommon.cpp
|
||||
Src/PowerPC/JitCommon/JitBackpatch.cpp
|
||||
Src/PowerPC/JitCommon/JitBase.cpp
|
||||
Src/PowerPC/JitCommon/JitCache.cpp
|
||||
Src/PowerPC/JitCommon/Jit_Util.cpp)
|
||||
Src/PowerPC/Jit64/Jit_SystemRegisters.cpp)
|
||||
endif()
|
||||
if(_M_ARM)
|
||||
set(SRCS ${SRCS}
|
||||
Src/ArmMemTools.cpp
|
||||
Src/PowerPC/JitArm32/Jit.cpp
|
||||
Src/PowerPC/JitArm32/JitAsm.cpp
|
||||
Src/PowerPC/JitArm32/JitArm_BackPatch.cpp
|
||||
Src/PowerPC/JitArm32/JitArm_Tables.cpp
|
||||
Src/PowerPC/JitArm32/JitArmCache.cpp
|
||||
Src/PowerPC/JitArm32/JitRegCache.cpp
|
||||
Src/PowerPC/JitArm32/JitFPRCache.cpp
|
||||
Src/PowerPC/JitArm32/JitArm_Branch.cpp
|
||||
Src/PowerPC/JitArm32/JitArm_Integer.cpp
|
||||
Src/PowerPC/JitArm32/JitArm_LoadStore.cpp
|
||||
Src/PowerPC/JitArm32/JitArm_FloatingPoint.cpp
|
||||
Src/PowerPC/JitArm32/JitArm_SystemRegisters.cpp
|
||||
Src/PowerPC/JitArm32/JitArm_LoadStoreFloating.cpp)
|
||||
endif()
|
||||
|
||||
set(LIBS bdisasm inputcommon videosoftware sfml-network)
|
||||
|
||||
|
||||
@@ -332,7 +332,7 @@
|
||||
<ClCompile Include="Src\IPC_HLE\WII_IPC_HLE_Device_usb.cpp" />
|
||||
<ClCompile Include="Src\IPC_HLE\WII_IPC_HLE_Device_usb_kbd.cpp" />
|
||||
<ClCompile Include="Src\IPC_HLE\WII_IPC_HLE_WiiMote.cpp" />
|
||||
<ClCompile Include="Src\MemTools.cpp" />
|
||||
<ClCompile Include="Src\x64MemTools.cpp" />
|
||||
<ClCompile Include="Src\Movie.cpp" />
|
||||
<ClCompile Include="Src\NetPlay.cpp" />
|
||||
<ClCompile Include="Src\NetPlayClient.cpp" />
|
||||
@@ -378,6 +378,7 @@
|
||||
<ClCompile Include="Src\PowerPC\JitCommon\JitBase.cpp" />
|
||||
<ClCompile Include="Src\PowerPC\JitCommon\JitCache.cpp" />
|
||||
<ClCompile Include="Src\PowerPC\JitCommon\Jit_Util.cpp" />
|
||||
<ClCompile Include="Src\PowerPC\JitInterface.cpp" />
|
||||
<ClCompile Include="Src\PowerPC\LUT_frsqrtex.cpp" />
|
||||
<ClCompile Include="Src\PowerPC\PowerPC.cpp" />
|
||||
<ClCompile Include="Src\PowerPC\PPCAnalyst.cpp" />
|
||||
@@ -387,10 +388,10 @@
|
||||
<ClCompile Include="Src\PowerPC\Profiler.cpp" />
|
||||
<ClCompile Include="Src\PowerPC\SignatureDB.cpp" />
|
||||
<ClCompile Include="Src\State.cpp">
|
||||
<OpenMPSupport Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'">false</OpenMPSupport>
|
||||
<OpenMPSupport Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</OpenMPSupport>
|
||||
<OpenMPSupport Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</OpenMPSupport>
|
||||
</ClCompile>
|
||||
<OpenMPSupport Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'">false</OpenMPSupport>
|
||||
<OpenMPSupport Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</OpenMPSupport>
|
||||
<OpenMPSupport Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</OpenMPSupport>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Src\stdafx.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
@@ -563,6 +564,7 @@
|
||||
<ClInclude Include="Src\PowerPC\JitCommon\JitBase.h" />
|
||||
<ClInclude Include="Src\PowerPC\JitCommon\JitCache.h" />
|
||||
<ClInclude Include="Src\PowerPC\JitCommon\Jit_Util.h" />
|
||||
<ClInclude Include="Src\PowerPC\JitInterface.h" />
|
||||
<ClInclude Include="Src\PowerPC\LUT_frsqrtex.h" />
|
||||
<ClInclude Include="Src\PowerPC\PowerPC.h" />
|
||||
<ClInclude Include="Src\PowerPC\PPCAnalyst.h" />
|
||||
|
||||
@@ -5,9 +5,8 @@
|
||||
<ClCompile Include="Src\Console.cpp" />
|
||||
<ClCompile Include="Src\Core.cpp" />
|
||||
<ClCompile Include="Src\CoreParameter.cpp" />
|
||||
<ClCompile Include="Src\CoreRerecording.cpp" />
|
||||
<ClCompile Include="Src\CoreTiming.cpp" />
|
||||
<ClCompile Include="Src\MemTools.cpp" />
|
||||
<ClCompile Include="Src\x64MemTools.cpp" />
|
||||
<ClCompile Include="Src\PatchEngine.cpp" />
|
||||
<ClCompile Include="Src\DSPEmulator.cpp" />
|
||||
<ClCompile Include="Src\State.cpp" />
|
||||
@@ -562,6 +561,9 @@
|
||||
<ClCompile Include="Src\HW\GCMemcard.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\GCMemcard</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Src\PowerPC\JitInterface.cpp">
|
||||
<Filter>PowerPC</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Src\ConfigManager.h" />
|
||||
@@ -1048,6 +1050,9 @@
|
||||
<ClInclude Include="Src\HW\GCMemcard.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\GCMemcard</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Src\PowerPC\JitInterface.h">
|
||||
<Filter>PowerPC</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="CMakeLists.txt" />
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#ifdef ANDROID
|
||||
#include <asm/sigcontext.h>
|
||||
#else
|
||||
#include <sys/ucontext.h> // Look in here for the context definition.
|
||||
#include <execinfo.h>
|
||||
#endif
|
||||
|
||||
#include "Common.h"
|
||||
#include "MemTools.h"
|
||||
#include "HW/Memmap.h"
|
||||
#include "PowerPC/PowerPC.h"
|
||||
#include "PowerPC/JitInterface.h"
|
||||
#include "PowerPC/JitCommon/JitBase.h"
|
||||
|
||||
namespace EMM
|
||||
{
|
||||
#ifdef ANDROID
|
||||
typedef struct sigcontext mcontext_t;
|
||||
typedef struct ucontext {
|
||||
uint32_t uc_flags;
|
||||
struct ucontext* uc_link;
|
||||
stack_t uc_stack;
|
||||
mcontext_t uc_mcontext;
|
||||
// Other fields are not used by Google Breakpad. Don't define them.
|
||||
} ucontext_t;
|
||||
#endif
|
||||
|
||||
void sigsegv_handler(int signal, siginfo_t *info, void *raw_context)
|
||||
{
|
||||
if (signal != SIGSEGV)
|
||||
{
|
||||
// We are not interested in other signals - handle it as usual.
|
||||
return;
|
||||
}
|
||||
ucontext_t *context = (ucontext_t *)raw_context;
|
||||
int sicode = info->si_code;
|
||||
if (sicode != SEGV_MAPERR && sicode != SEGV_ACCERR)
|
||||
{
|
||||
// Huh? Return.
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Get all the information we can out of the context.
|
||||
mcontext_t *ctx = &context->uc_mcontext;
|
||||
|
||||
void *fault_memory_ptr = (void*)ctx->arm_r10;
|
||||
u8 *fault_instruction_ptr = (u8 *)ctx->arm_pc;
|
||||
|
||||
if (!JitInterface::IsInCodeSpace(fault_instruction_ptr)) {
|
||||
// Let's not prevent debugging.
|
||||
return;
|
||||
}
|
||||
|
||||
u64 bad_address = (u64)fault_memory_ptr;
|
||||
u64 memspace_bottom = (u64)Memory::base;
|
||||
if (bad_address < memspace_bottom) {
|
||||
PanicAlertT("Exception handler - access below memory space. %08llx%08llx",
|
||||
bad_address >> 32, bad_address);
|
||||
}
|
||||
|
||||
u32 em_address = (u32)(bad_address - memspace_bottom);
|
||||
|
||||
int access_type = 0;
|
||||
|
||||
CONTEXT fake_ctx;
|
||||
fake_ctx.reg_pc = ctx->arm_pc;
|
||||
const u8 *new_rip = jit->BackPatch(fault_instruction_ptr, access_type, em_address, &fake_ctx);
|
||||
if (new_rip) {
|
||||
ctx->arm_pc = fake_ctx.reg_pc;
|
||||
}
|
||||
}
|
||||
|
||||
void InstallExceptionHandler()
|
||||
{
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = 0;
|
||||
sa.sa_sigaction = &sigsegv_handler;
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sigaction(SIGSEGV, &sa, NULL);
|
||||
}
|
||||
} // namespace
|
||||
@@ -100,9 +100,6 @@ SConfig::SConfig()
|
||||
{
|
||||
// Make sure we have log manager
|
||||
LoadSettings();
|
||||
//Make sure we load any extra settings
|
||||
LoadSettingsWii();
|
||||
|
||||
}
|
||||
|
||||
void SConfig::Init()
|
||||
@@ -244,6 +241,8 @@ void SConfig::SaveSettings()
|
||||
ini.Set("Core", "WiiSDCard", m_WiiSDCard);
|
||||
ini.Set("Core", "WiiKeyboard", m_WiiKeyboard);
|
||||
ini.Set("Core", "WiimoteReconnectOnLoad", m_WiimoteReconnectOnLoad);
|
||||
ini.Set("Core", "WiimoteContinuousScanning", m_WiimoteContinuousScanning);
|
||||
ini.Set("Core", "WiimoteEnableSpeaker", m_WiimoteEnableSpeaker);
|
||||
ini.Set("Core", "RunCompareServer", m_LocalCoreStartupParameter.bRunCompareServer);
|
||||
ini.Set("Core", "RunCompareClient", m_LocalCoreStartupParameter.bRunCompareClient);
|
||||
ini.Set("Core", "FrameLimit", m_Framelimit);
|
||||
@@ -363,7 +362,11 @@ void SConfig::LoadSettings()
|
||||
|
||||
// Core
|
||||
ini.Get("Core", "HLE_BS2", &m_LocalCoreStartupParameter.bHLE_BS2, false);
|
||||
#ifdef _M_ARM
|
||||
ini.Get("Core", "CPUCore", &m_LocalCoreStartupParameter.iCPUCore, 3);
|
||||
#else
|
||||
ini.Get("Core", "CPUCore", &m_LocalCoreStartupParameter.iCPUCore, 1);
|
||||
#endif
|
||||
ini.Get("Core", "DSPThread", &m_LocalCoreStartupParameter.bDSPThread, false);
|
||||
ini.Get("Core", "DSPHLE", &m_LocalCoreStartupParameter.bDSPHLE, true);
|
||||
ini.Get("Core", "CPUThread", &m_LocalCoreStartupParameter.bCPUThread, true);
|
||||
@@ -393,7 +396,9 @@ void SConfig::LoadSettings()
|
||||
|
||||
ini.Get("Core", "WiiSDCard", &m_WiiSDCard, false);
|
||||
ini.Get("Core", "WiiKeyboard", &m_WiiKeyboard, false);
|
||||
ini.Get("Core", "WiimoteReconnectOnLoad", &m_WiimoteReconnectOnLoad, true);
|
||||
ini.Get("Core", "WiimoteReconnectOnLoad", &m_WiimoteReconnectOnLoad, true);
|
||||
ini.Get("Core", "WiimoteContinuousScanning", &m_WiimoteContinuousScanning, false);
|
||||
ini.Get("Core", "WiimoteEnableSpeaker", &m_WiimoteEnableSpeaker, true);
|
||||
ini.Get("Core", "RunCompareServer", &m_LocalCoreStartupParameter.bRunCompareServer, false);
|
||||
ini.Get("Core", "RunCompareClient", &m_LocalCoreStartupParameter.bRunCompareClient, false);
|
||||
ini.Get("Core", "MMU", &m_LocalCoreStartupParameter.bMMU, false);
|
||||
@@ -429,18 +434,3 @@ void SConfig::LoadSettings()
|
||||
|
||||
m_SYSCONF = new SysConf();
|
||||
}
|
||||
void SConfig::LoadSettingsWii()
|
||||
{
|
||||
IniFile ini;
|
||||
//Wiimote configs
|
||||
ini.Load((File::GetUserPath(D_CONFIG_IDX) + "Dolphin.ini"));
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
char SectionName[32];
|
||||
sprintf(SectionName, "Wiimote%i", i + 1);
|
||||
ini.Get(SectionName, "AutoReconnectRealWiimote", &m_WiiAutoReconnect[i], false);
|
||||
}
|
||||
ini.Load((File::GetUserPath(D_CONFIG_IDX) + "wiimote.ini"));
|
||||
ini.Get("Real", "Unpair", &m_WiiAutoUnpair, false);
|
||||
|
||||
}
|
||||
|
||||
@@ -35,15 +35,15 @@
|
||||
#define BACKEND_OPENAL "OpenAL"
|
||||
#define BACKEND_PULSEAUDIO "Pulse"
|
||||
#define BACKEND_XAUDIO2 "XAudio2"
|
||||
|
||||
#define BACKEND_OPENSLES "OpenSLES"
|
||||
struct SConfig : NonCopyable
|
||||
{
|
||||
// Wii Devices
|
||||
bool m_WiiSDCard;
|
||||
bool m_WiiKeyboard;
|
||||
bool m_WiiAutoReconnect[4];
|
||||
bool m_WiiAutoUnpair;
|
||||
bool m_WiimoteReconnectOnLoad;
|
||||
bool m_WiimoteContinuousScanning;
|
||||
bool m_WiimoteEnableSpeaker;
|
||||
|
||||
// name of the last used filename
|
||||
std::string m_LastFilename;
|
||||
@@ -107,9 +107,6 @@ struct SConfig : NonCopyable
|
||||
// load settings
|
||||
void LoadSettings();
|
||||
|
||||
//Special load settings
|
||||
void LoadSettingsWii();
|
||||
|
||||
// Return the permanent and somewhat globally used instance of this struct
|
||||
static SConfig& GetInstance() {return(*m_Instance);}
|
||||
|
||||
|
||||
@@ -54,7 +54,6 @@
|
||||
#include "IPC_HLE/WII_IPC_HLE_Device_usb.h"
|
||||
|
||||
#include "PowerPC/PowerPC.h"
|
||||
#include "PowerPC/JitCommon/JitBase.h"
|
||||
|
||||
#include "DSPEmulator.h"
|
||||
#include "ConfigManager.h"
|
||||
@@ -140,7 +139,7 @@ void DisplayMessage(const char *message, int time_in_ms)
|
||||
if (_CoreParameter.bRenderToMain &&
|
||||
SConfig::GetInstance().m_InterfaceStatusbar)
|
||||
{
|
||||
Host_UpdateStatusBar(message);
|
||||
Host_UpdateStatusBar(message);
|
||||
}
|
||||
else
|
||||
Host_UpdateTitle(message);
|
||||
@@ -189,7 +188,7 @@ bool IsGPUThread()
|
||||
return IsCPUThread();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// This is called from the GUI thread. See the booting call schedule in
|
||||
// BootManager.cpp
|
||||
bool Init()
|
||||
@@ -310,7 +309,7 @@ void CpuThread()
|
||||
g_video_backend->Video_Prepare();
|
||||
}
|
||||
|
||||
#if defined(_M_X64)
|
||||
#if defined(_M_X64) || _M_ARM
|
||||
EMM::InstallExceptionHandler(); // Let's run under memory watch
|
||||
#endif
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#include "DSPAnalyzer.h"
|
||||
#include "Jit/DSPJitUtil.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
|
||||
#define MAX_BLOCK_SIZE 250
|
||||
#define DSP_IDLE_SKIP_CYCLES 0x1000
|
||||
|
||||
@@ -252,7 +252,9 @@ static void gdsp_idma_out(u16 dsp_addr, u32 addr, u32 size)
|
||||
ERROR_LOG(DSPLLE, "*** idma_out IRAM_DSP (0x%04x) -> RAM (0x%08x) : size (0x%08x)", dsp_addr / 2, addr, size);
|
||||
}
|
||||
|
||||
#if _M_SSE >= 0x301
|
||||
static const __m128i s_mask = _mm_set_epi32(0x0E0F0C0DL, 0x0A0B0809L, 0x06070405L, 0x02030001L);
|
||||
#endif
|
||||
|
||||
// TODO: These should eat clock cycles.
|
||||
static void gdsp_ddma_in(u16 dsp_addr, u32 addr, u32 size)
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#include "DSPJitUtil.h"
|
||||
#endif
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
using namespace Gen;
|
||||
|
||||
// CLR $acR
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#include "../DSPAnalyzer.h"
|
||||
#include "DSPJitUtil.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
|
||||
using namespace Gen;
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#include "../DSPEmitter.h"
|
||||
#include "DSPJitUtil.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
using namespace Gen;
|
||||
|
||||
// In: RAX: s64 _Value
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#include "../DSPEmitter.h"
|
||||
#include "DSPJitUtil.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
|
||||
using namespace Gen;
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#include "../DSPEmitter.h"
|
||||
#include "DSPJitUtil.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
using namespace Gen;
|
||||
|
||||
// SRS @M, $(0x18+S)
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include "../DSPEmitter.h"
|
||||
#include "DSPJitUtil.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
using namespace Gen;
|
||||
|
||||
//clobbers:
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "DSPJitUtil.h"
|
||||
#endif
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
using namespace Gen;
|
||||
|
||||
// Returns s64 in RAX
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include "../DSPEmitter.h"
|
||||
#include "DSPJitUtil.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
|
||||
using namespace Gen;
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "UCodes/UCodes.h"
|
||||
#include "../AudioInterface.h"
|
||||
#include "ConfigManager.h"
|
||||
#include "Core.h"
|
||||
|
||||
DSPHLE::DSPHLE() {
|
||||
m_InitMixer = false;
|
||||
@@ -130,6 +131,14 @@ void DSPHLE::SwapUCode(u32 _crc)
|
||||
|
||||
void DSPHLE::DoState(PointerWrap &p)
|
||||
{
|
||||
bool isHLE = true;
|
||||
p.Do(isHLE);
|
||||
if (isHLE != true && p.GetMode() == PointerWrap::MODE_READ)
|
||||
{
|
||||
Core::DisplayMessage("State is incompatible with current DSP engine. Aborting load state.", 3000);
|
||||
p.SetMode(PointerWrap::MODE_VERIFY);
|
||||
return;
|
||||
}
|
||||
bool prevInitMixer = m_InitMixer;
|
||||
p.Do(m_InitMixer);
|
||||
if (prevInitMixer != m_InitMixer && p.GetMode() == PointerWrap::MODE_READ)
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "IniFile.h"
|
||||
#include "ConfigManager.h"
|
||||
#include "CPUDetect.h"
|
||||
#include "Core.h"
|
||||
|
||||
#include "DSPLLEGlobals.h" // Local
|
||||
#include "DSP/DSPInterpreter.h"
|
||||
@@ -56,6 +57,14 @@ Common::Event ppcEvent;
|
||||
|
||||
void DSPLLE::DoState(PointerWrap &p)
|
||||
{
|
||||
bool isHLE = false;
|
||||
p.Do(isHLE);
|
||||
if (isHLE != false && p.GetMode() == PointerWrap::MODE_READ)
|
||||
{
|
||||
Core::DisplayMessage("State is incompatible with current DSP engine. Aborting load state.", 3000);
|
||||
p.SetMode(PointerWrap::MODE_VERIFY);
|
||||
return;
|
||||
}
|
||||
p.Do(g_dsp.r);
|
||||
p.Do(g_dsp.pc);
|
||||
#if PROFILE
|
||||
|
||||
@@ -419,132 +419,6 @@ u32 Read_Instruction(const u32 em_address)
|
||||
return inst.hex;
|
||||
}
|
||||
|
||||
u32 Read_Opcode_JIT_Uncached(const u32 _Address)
|
||||
{
|
||||
u8* iCache;
|
||||
u32 addr;
|
||||
if (_Address & JIT_ICACHE_VMEM_BIT)
|
||||
{
|
||||
iCache = jit->GetBlockCache()->GetICacheVMEM();
|
||||
addr = _Address & JIT_ICACHE_MASK;
|
||||
}
|
||||
else if (_Address & JIT_ICACHE_EXRAM_BIT)
|
||||
{
|
||||
iCache = jit->GetBlockCache()->GetICacheEx();
|
||||
addr = _Address & JIT_ICACHEEX_MASK;
|
||||
}
|
||||
else
|
||||
{
|
||||
iCache = jit->GetBlockCache()->GetICache();
|
||||
addr = _Address & JIT_ICACHE_MASK;
|
||||
}
|
||||
u32 inst = *(u32*)(iCache + addr);
|
||||
if (inst == JIT_ICACHE_INVALID_WORD)
|
||||
{
|
||||
u32 cache_block_start = addr & ~0x1f;
|
||||
u32 mem_block_start = _Address & ~0x1f;
|
||||
u8 *pMem = Memory::GetPointer(mem_block_start);
|
||||
memcpy(iCache + cache_block_start, pMem, 32);
|
||||
inst = *(u32*)(iCache + addr);
|
||||
}
|
||||
inst = Common::swap32(inst);
|
||||
|
||||
if ((inst & 0xfc000000) == 0)
|
||||
{
|
||||
inst = jit->GetBlockCache()->GetOriginalFirstOp(inst);
|
||||
}
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
u32 Read_Opcode_JIT(u32 _Address)
|
||||
{
|
||||
#ifdef FAST_ICACHE
|
||||
if (bMMU && !bFakeVMEM && (_Address & ADDR_MASK_MEM1))
|
||||
{
|
||||
_Address = Memory::TranslateAddress(_Address, FLAG_OPCODE);
|
||||
if (_Address == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
u32 inst = 0;
|
||||
|
||||
// Bypass the icache for the external interrupt exception handler
|
||||
if ( (_Address & 0x0FFFFF00) == 0x00000500 )
|
||||
inst = Read_Opcode_JIT_Uncached(_Address);
|
||||
else
|
||||
inst = PowerPC::ppcState.iCache.ReadInstruction(_Address);
|
||||
#else
|
||||
u32 inst = Memory::ReadUnchecked_U32(_Address);
|
||||
#endif
|
||||
return inst;
|
||||
}
|
||||
|
||||
// The following function is deprecated in favour of FAST_ICACHE
|
||||
u32 Read_Opcode_JIT_LC(const u32 _Address)
|
||||
{
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
if ((_Address & ~JIT_ICACHE_MASK) != 0x80000000 && (_Address & ~JIT_ICACHE_MASK) != 0x00000000 &&
|
||||
(_Address & ~JIT_ICACHE_MASK) != 0x7e000000 && // TLB area
|
||||
(_Address & ~JIT_ICACHEEX_MASK) != 0x90000000 && (_Address & ~JIT_ICACHEEX_MASK) != 0x10000000)
|
||||
{
|
||||
PanicAlertT("iCacheJIT: Reading Opcode from %x. Please report.", _Address);
|
||||
ERROR_LOG(MEMMAP, "iCacheJIT: Reading Opcode from %x. Please report.", _Address);
|
||||
return 0;
|
||||
}
|
||||
u8* iCache;
|
||||
u32 addr;
|
||||
if (_Address & JIT_ICACHE_VMEM_BIT)
|
||||
{
|
||||
iCache = jit->GetBlockCache()->GetICacheVMEM();
|
||||
addr = _Address & JIT_ICACHE_MASK;
|
||||
}
|
||||
else if (_Address & JIT_ICACHE_EXRAM_BIT)
|
||||
{
|
||||
iCache = jit->GetBlockCache()->GetICacheEx();
|
||||
addr = _Address & JIT_ICACHEEX_MASK;
|
||||
}
|
||||
else
|
||||
{
|
||||
iCache = jit->GetBlockCache()->GetICache();
|
||||
addr = _Address & JIT_ICACHE_MASK;
|
||||
}
|
||||
u32 inst = *(u32*)(iCache + addr);
|
||||
if (inst == JIT_ICACHE_INVALID_WORD)
|
||||
inst = Memory::ReadUnchecked_U32(_Address);
|
||||
else
|
||||
inst = Common::swap32(inst);
|
||||
#else
|
||||
u32 inst = Memory::ReadUnchecked_U32(_Address);
|
||||
#endif
|
||||
if ((inst & 0xfc000000) == 0)
|
||||
{
|
||||
inst = jit->GetBlockCache()->GetOriginalFirstOp(inst);
|
||||
}
|
||||
return inst;
|
||||
}
|
||||
|
||||
// WARNING! No checks!
|
||||
// We assume that _Address is cached
|
||||
void Write_Opcode_JIT(const u32 _Address, const u32 _Value)
|
||||
{
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
if (_Address & JIT_ICACHE_VMEM_BIT)
|
||||
{
|
||||
*(u32*)(jit->GetBlockCache()->GetICacheVMEM() + (_Address & JIT_ICACHE_MASK)) = Common::swap32(_Value);
|
||||
}
|
||||
else if (_Address & JIT_ICACHE_EXRAM_BIT)
|
||||
{
|
||||
*(u32*)(jit->GetBlockCache()->GetICacheEx() + (_Address & JIT_ICACHEEX_MASK)) = Common::swap32(_Value);
|
||||
}
|
||||
else
|
||||
*(u32*)(jit->GetBlockCache()->GetICache() + (_Address & JIT_ICACHE_MASK)) = Common::swap32(_Value);
|
||||
#else
|
||||
Memory::WriteUnchecked_U32(_Value, _Address);
|
||||
#endif
|
||||
}
|
||||
|
||||
void WriteBigEData(const u8 *_pData, const u32 _Address, const u32 _iSize)
|
||||
{
|
||||
memcpy(GetPointer(_Address), _pData, _iSize);
|
||||
|
||||
@@ -119,11 +119,6 @@ inline u32 ReadFast32(const u32 _Address)
|
||||
|
||||
// used by interpreter to read instructions, uses iCache
|
||||
u32 Read_Opcode(const u32 _Address);
|
||||
// used by JIT to read instructions
|
||||
u32 Read_Opcode_JIT(const u32 _Address);
|
||||
// used by JIT. uses iCacheJIT. Reads in the "Locked cache" mode
|
||||
u32 Read_Opcode_JIT_LC(const u32 _Address);
|
||||
void Write_Opcode_JIT(const u32 _Address, const u32 _Value);
|
||||
// this is used by Debugger a lot.
|
||||
// For now, just reads from memory!
|
||||
u32 Read_Instruction(const u32 _Address);
|
||||
|
||||
@@ -38,17 +38,12 @@ void Update(int _number);
|
||||
namespace WiimoteReal
|
||||
{
|
||||
|
||||
unsigned int Initialize();
|
||||
void Initialize();
|
||||
void Shutdown();
|
||||
void Refresh();
|
||||
|
||||
void LoadSettings();
|
||||
|
||||
#ifdef _WIN32
|
||||
int PairUp(bool unpair = false);
|
||||
int UnPair();
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -248,13 +248,12 @@ void Wiimote::RequestStatus(const wm_request_status* const rs)
|
||||
{
|
||||
using namespace WiimoteReal;
|
||||
|
||||
std::lock_guard<std::mutex> lk(g_refresh_lock);
|
||||
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
||||
|
||||
if (g_wiimotes[m_index])
|
||||
{
|
||||
wm_request_status rpt;
|
||||
rpt.rumble = 0;
|
||||
g_wiimotes[m_index]->SendPacket(WM_REQUEST_STATUS, &rpt, sizeof(rpt));
|
||||
wm_request_status rpt = {};
|
||||
g_wiimotes[m_index]->QueueReport(WM_REQUEST_STATUS, &rpt, sizeof(rpt));
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
@@ -704,7 +704,7 @@ void Wiimote::Update()
|
||||
{
|
||||
using namespace WiimoteReal;
|
||||
|
||||
std::lock_guard<std::mutex> lk(g_refresh_lock);
|
||||
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
||||
if (g_wiimotes[m_index])
|
||||
{
|
||||
Report rpt = g_wiimotes[m_index]->ProcessReadQueue();
|
||||
|
||||
@@ -201,6 +201,7 @@ struct wm_report
|
||||
#define WM_LEDS 0x11
|
||||
struct wm_leds {
|
||||
u8 rumble : 1;
|
||||
// real wii also sets bit 0x2 (unknown purpose)
|
||||
u8 : 3;
|
||||
u8 leds : 4;
|
||||
};
|
||||
@@ -208,8 +209,9 @@ struct wm_leds {
|
||||
#define WM_REPORT_MODE 0x12
|
||||
struct wm_report_mode {
|
||||
u8 rumble : 1;
|
||||
u8 continuous : 1; // these 2 seem to be named wrong
|
||||
// unsure what "all_the_time" actually is, the real wii does set it (bit 0x2)
|
||||
u8 all_the_time : 1;
|
||||
u8 continuous : 1;
|
||||
u8 : 5;
|
||||
u8 mode;
|
||||
};
|
||||
|
||||
@@ -21,9 +21,25 @@
|
||||
namespace WiimoteReal
|
||||
{
|
||||
|
||||
int FindWiimotes(Wiimote **wm, int max_wiimotes)
|
||||
WiimoteScanner::WiimoteScanner()
|
||||
{
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
WiimoteScanner::~WiimoteScanner()
|
||||
{}
|
||||
|
||||
void WiimoteScanner::Update()
|
||||
{}
|
||||
|
||||
std::vector<Wiimote*> WiimoteScanner::FindWiimotes()
|
||||
{
|
||||
return std::vector<Wiimote*>();
|
||||
}
|
||||
|
||||
bool WiimoteScanner::IsReady() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Wiimote::Connect()
|
||||
@@ -31,22 +47,22 @@ bool Wiimote::Connect()
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Wiimote::RealDisconnect()
|
||||
void Wiimote::Disconnect()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool Wiimote::IsOpen() const
|
||||
bool Wiimote::IsConnected() const
|
||||
{
|
||||
return IsConnected();
|
||||
return false;
|
||||
}
|
||||
|
||||
int Wiimote::IORead(unsigned char* buf)
|
||||
int Wiimote::IORead(u8* buf)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Wiimote::IOWrite(unsigned char* buf, int len)
|
||||
int Wiimote::IOWrite(const u8* buf, int len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -15,16 +15,6 @@
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <bluetooth/bluetooth.h>
|
||||
#include <bluetooth/hci.h>
|
||||
#include <bluetooth/hci_lib.h>
|
||||
@@ -32,114 +22,116 @@
|
||||
|
||||
#include "Common.h"
|
||||
#include "WiimoteReal.h"
|
||||
#include "Host.h"
|
||||
|
||||
namespace WiimoteReal
|
||||
{
|
||||
|
||||
// Find wiimotes.
|
||||
// Does not replace already found wiimotes even if they are disconnected.
|
||||
// wm is an array of max_wiimotes wiimotes
|
||||
// Returns the total number of found wiimotes.
|
||||
int FindWiimotes(Wiimote** wm, int max_wiimotes)
|
||||
WiimoteScanner::WiimoteScanner()
|
||||
: m_run_thread()
|
||||
, m_want_wiimotes()
|
||||
, device_id(-1)
|
||||
, device_sock(-1)
|
||||
{
|
||||
int device_id;
|
||||
int device_sock;
|
||||
int found_devices;
|
||||
int found_wiimotes = 0;
|
||||
int i;
|
||||
|
||||
// Count the number of already found wiimotes
|
||||
for (i = 0; i < MAX_WIIMOTES; ++i)
|
||||
{
|
||||
if (wm[i])
|
||||
found_wiimotes++;
|
||||
}
|
||||
|
||||
// Get the id of the first bluetooth device.
|
||||
if ((device_id = hci_get_route(NULL)) < 0)
|
||||
device_id = hci_get_route(NULL);
|
||||
if (device_id < 0)
|
||||
{
|
||||
NOTICE_LOG(WIIMOTE, "Bluetooth not found.");
|
||||
return found_wiimotes;
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a socket to the device
|
||||
if ((device_sock = hci_open_dev(device_id)) < 0)
|
||||
device_sock = hci_open_dev(device_id);
|
||||
if (device_sock < 0)
|
||||
{
|
||||
ERROR_LOG(WIIMOTE, "Unable to open bluetooth.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool WiimoteScanner::IsReady() const
|
||||
{
|
||||
return device_sock > 0;
|
||||
}
|
||||
|
||||
WiimoteScanner::~WiimoteScanner()
|
||||
{
|
||||
if (IsReady())
|
||||
close(device_sock);
|
||||
}
|
||||
|
||||
void WiimoteScanner::Update()
|
||||
{}
|
||||
|
||||
std::vector<Wiimote*> WiimoteScanner::FindWiimotes()
|
||||
{
|
||||
std::vector<Wiimote*> found_wiimotes;
|
||||
|
||||
// supposedly 1.28 seconds
|
||||
int const wait_len = 1;
|
||||
|
||||
int const max_infos = 255;
|
||||
inquiry_info scan_infos[max_infos] = {};
|
||||
auto* scan_infos_ptr = scan_infos;
|
||||
|
||||
// Scan for bluetooth devices
|
||||
int const found_devices = hci_inquiry(device_id, wait_len, max_infos, NULL, &scan_infos_ptr, IREQ_CACHE_FLUSH);
|
||||
if (found_devices < 0)
|
||||
{
|
||||
ERROR_LOG(WIIMOTE, "Error searching for bluetooth devices.");
|
||||
return found_wiimotes;
|
||||
}
|
||||
|
||||
int try_num = 0;
|
||||
while ((try_num < 5) && (found_wiimotes < max_wiimotes))
|
||||
DEBUG_LOG(WIIMOTE, "Found %i bluetooth device(s).", found_devices);
|
||||
|
||||
// Display discovered devices
|
||||
for (int i = 0; i < found_devices; ++i)
|
||||
{
|
||||
inquiry_info scan_info_arr[128];
|
||||
inquiry_info* scan_info = scan_info_arr;
|
||||
memset(&scan_info_arr, 0, sizeof(scan_info_arr));
|
||||
|
||||
// Scan for bluetooth devices for approximately one second
|
||||
found_devices = hci_inquiry(device_id, 1, 128, NULL, &scan_info, IREQ_CACHE_FLUSH);
|
||||
if (found_devices < 0)
|
||||
ERROR_LOG(WIIMOTE, "found a device...");
|
||||
|
||||
// BT names are a maximum of 248 bytes apparently
|
||||
char name[255] = {};
|
||||
if (hci_read_remote_name(device_sock, &scan_infos[i].bdaddr, sizeof(name), name, 0) < 0)
|
||||
{
|
||||
ERROR_LOG(WIIMOTE, "Error searching for bluetooth devices.");
|
||||
return found_wiimotes;
|
||||
ERROR_LOG(WIIMOTE, "name request failed");
|
||||
continue;
|
||||
}
|
||||
|
||||
DEBUG_LOG(WIIMOTE, "Found %i bluetooth device(s).", found_devices);
|
||||
|
||||
// Display discovered devices
|
||||
for (i = 0; (i < found_devices) && (found_wiimotes < max_wiimotes); ++i)
|
||||
ERROR_LOG(WIIMOTE, "device name %s", name);
|
||||
if (IsValidBluetoothName(name))
|
||||
{
|
||||
char name[1000];
|
||||
memset(name, 0, sizeof(name));
|
||||
ERROR_LOG(WIIMOTE, "found a device...");
|
||||
if (hci_read_remote_name(device_sock, &scan_info[i].bdaddr, sizeof(name), name, 0) < 0) {
|
||||
ERROR_LOG(WIIMOTE, "name request failed");
|
||||
continue;
|
||||
}
|
||||
ERROR_LOG(WIIMOTE, "device name %s", name);
|
||||
if (IsValidBluetoothName(name))
|
||||
bool new_wiimote = true;
|
||||
|
||||
// TODO: do this
|
||||
|
||||
// Determine if this wiimote has already been found.
|
||||
//for (int j = 0; j < MAX_WIIMOTES && new_wiimote; ++j)
|
||||
//{
|
||||
// if (wm[j] && bacmp(&scan_infos[i].bdaddr,&wm[j]->bdaddr) == 0)
|
||||
// new_wiimote = false;
|
||||
//}
|
||||
|
||||
if (new_wiimote)
|
||||
{
|
||||
bool new_wiimote = true;
|
||||
// Determine if this wiimote has already been found.
|
||||
for (int j = 0; j < MAX_WIIMOTES && new_wiimote; ++j)
|
||||
{
|
||||
if (wm[j] && bacmp(&scan_info[i].bdaddr,&wm[j]->bdaddr) == 0)
|
||||
new_wiimote = false;
|
||||
}
|
||||
// Found a new device
|
||||
char bdaddr_str[18] = {};
|
||||
ba2str(&scan_infos[i].bdaddr, bdaddr_str);
|
||||
|
||||
if (new_wiimote)
|
||||
{
|
||||
// Find an unused slot
|
||||
unsigned int k = 0;
|
||||
for (; k < MAX_WIIMOTES && !(WIIMOTE_SRC_REAL & g_wiimote_sources[k] && !wm[k]); ++k);
|
||||
wm[k] = new Wiimote(k);
|
||||
|
||||
// Found a new device
|
||||
char bdaddr_str[18];
|
||||
ba2str(&scan_info[i].bdaddr, bdaddr_str);
|
||||
|
||||
NOTICE_LOG(WIIMOTE, "Found wiimote %i, (%s).",
|
||||
wm[k]->index + 1, bdaddr_str);
|
||||
|
||||
wm[k]->bdaddr = scan_info[i].bdaddr;
|
||||
++found_wiimotes;
|
||||
}
|
||||
auto* const wm = new Wiimote;
|
||||
wm->bdaddr = scan_infos[i].bdaddr;
|
||||
found_wiimotes.push_back(wm);
|
||||
|
||||
NOTICE_LOG(WIIMOTE, "Found wiimote (%s).", bdaddr_str);
|
||||
}
|
||||
}
|
||||
try_num++;
|
||||
}
|
||||
|
||||
close(device_sock);
|
||||
return found_wiimotes;
|
||||
}
|
||||
|
||||
// Connect to a wiimote with a known address.
|
||||
bool Wiimote::Connect()
|
||||
{
|
||||
if (IsConnected())
|
||||
return false;
|
||||
|
||||
sockaddr_l2 addr;
|
||||
addr.l2_family = AF_BLUETOOTH;
|
||||
addr.l2_bdaddr = bdaddr;
|
||||
@@ -148,7 +140,7 @@ bool Wiimote::Connect()
|
||||
// Output channel
|
||||
addr.l2_psm = htobs(WM_OUTPUT_CHANNEL);
|
||||
if ((cmd_sock = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) == -1 ||
|
||||
connect(cmd_sock, (struct sockaddr*)&addr, sizeof(addr)) < 0)
|
||||
connect(cmd_sock, (sockaddr*)&addr, sizeof(addr)) < 0)
|
||||
{
|
||||
DEBUG_LOG(WIIMOTE, "Unable to open output socket to wiimote.");
|
||||
close(cmd_sock);
|
||||
@@ -159,7 +151,7 @@ bool Wiimote::Connect()
|
||||
// Input channel
|
||||
addr.l2_psm = htobs(WM_INPUT_CHANNEL);
|
||||
if ((int_sock = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) == -1 ||
|
||||
connect(int_sock, (struct sockaddr*)&addr, sizeof(addr)) < 0)
|
||||
connect(int_sock, (sockaddr*)&addr, sizeof(addr)) < 0)
|
||||
{
|
||||
DEBUG_LOG(WIIMOTE, "Unable to open input socket from wiimote.");
|
||||
close(int_sock);
|
||||
@@ -168,57 +160,27 @@ bool Wiimote::Connect()
|
||||
return false;
|
||||
}
|
||||
|
||||
NOTICE_LOG(WIIMOTE, "Connected to wiimote %i.", index + 1);
|
||||
|
||||
m_connected = true;
|
||||
|
||||
// Do the handshake
|
||||
Handshake();
|
||||
|
||||
// Set LEDs
|
||||
SetLEDs(WIIMOTE_LED_1 << index);
|
||||
|
||||
m_wiimote_thread = std::thread(std::mem_fun(&Wiimote::ThreadFunc), this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Disconnect a wiimote.
|
||||
void Wiimote::RealDisconnect()
|
||||
void Wiimote::Disconnect()
|
||||
{
|
||||
if (!IsConnected())
|
||||
return;
|
||||
close(cmd_sock);
|
||||
close(int_sock);
|
||||
|
||||
NOTICE_LOG(WIIMOTE, "Disconnecting wiimote %i.", index + 1);
|
||||
|
||||
m_connected = false;
|
||||
|
||||
if (m_wiimote_thread.joinable())
|
||||
m_wiimote_thread.join();
|
||||
|
||||
Close();
|
||||
cmd_sock = -1;
|
||||
int_sock = -1;
|
||||
}
|
||||
|
||||
void Wiimote::Close()
|
||||
bool Wiimote::IsConnected() const
|
||||
{
|
||||
if (IsOpen())
|
||||
{
|
||||
Host_ConnectWiimote(index, false);
|
||||
|
||||
close(cmd_sock);
|
||||
close(int_sock);
|
||||
|
||||
cmd_sock = -1;
|
||||
int_sock = -1;
|
||||
}
|
||||
return cmd_sock != -1;// && int_sock != -1;
|
||||
}
|
||||
|
||||
bool Wiimote::IsOpen() const
|
||||
{
|
||||
return IsConnected() && cmd_sock != -1 && int_sock != -1;
|
||||
}
|
||||
|
||||
int Wiimote::IORead(unsigned char *buf)
|
||||
// positive = read packet
|
||||
// negative = didn't read packet
|
||||
// zero = error
|
||||
int Wiimote::IORead(u8* buf)
|
||||
{
|
||||
// Block select for 1/2000th of a second
|
||||
timeval tv;
|
||||
@@ -232,11 +194,11 @@ int Wiimote::IORead(unsigned char *buf)
|
||||
if (select(int_sock + 1, &fds, NULL, NULL, &tv) == -1)
|
||||
{
|
||||
ERROR_LOG(WIIMOTE, "Unable to select wiimote %i input socket.", index + 1);
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!FD_ISSET(int_sock, &fds))
|
||||
return 0;
|
||||
return -1;
|
||||
|
||||
// Read the pending message into the buffer
|
||||
int r = read(int_sock, buf, MAX_PAYLOAD);
|
||||
@@ -250,21 +212,15 @@ int Wiimote::IORead(unsigned char *buf)
|
||||
// This can happen if the bluetooth dongle is disconnected
|
||||
ERROR_LOG(WIIMOTE, "Bluetooth appears to be disconnected. "
|
||||
"Wiimote %i will be disconnected.", index + 1);
|
||||
Close();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
else if (!r)
|
||||
{
|
||||
// Disconnect
|
||||
Close();
|
||||
r = 0;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int Wiimote::IOWrite(unsigned char* buf, int len)
|
||||
int Wiimote::IOWrite(u8 const* buf, int len)
|
||||
{
|
||||
return write(int_sock, buf, len);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <regex>
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
#include <ctime>
|
||||
|
||||
#include <windows.h>
|
||||
#include <dbt.h>
|
||||
@@ -32,6 +35,8 @@
|
||||
#include <bthdef.h>
|
||||
#include <BluetoothAPIs.h>
|
||||
|
||||
//#define AUTHENTICATE_WIIMOTES
|
||||
|
||||
typedef struct _HIDD_ATTRIBUTES
|
||||
{
|
||||
ULONG Size;
|
||||
@@ -53,6 +58,8 @@ typedef BOOL (__stdcall *PBth_BluetoothFindRadioClose)(HBLUETOOTH_RADIO_FIND);
|
||||
typedef DWORD (__stdcall *PBth_BluetoothGetRadioInfo)(HANDLE, PBLUETOOTH_RADIO_INFO);
|
||||
typedef DWORD (__stdcall *PBth_BluetoothRemoveDevice)(const BLUETOOTH_ADDRESS*);
|
||||
typedef DWORD (__stdcall *PBth_BluetoothSetServiceState)(HANDLE, const BLUETOOTH_DEVICE_INFO*, const GUID*, DWORD);
|
||||
typedef DWORD (__stdcall *PBth_BluetoothAuthenticateDevice)(HWND, HANDLE, BLUETOOTH_DEVICE_INFO*, PWCHAR, ULONG);
|
||||
typedef DWORD (__stdcall *PBth_BluetoothEnumerateInstalledServices)(HANDLE, BLUETOOTH_DEVICE_INFO*, DWORD*, GUID*);
|
||||
|
||||
PHidD_GetHidGuid HidD_GetHidGuid = NULL;
|
||||
PHidD_GetAttributes HidD_GetAttributes = NULL;
|
||||
@@ -67,12 +74,16 @@ PBth_BluetoothFindRadioClose Bth_BluetoothFindRadioClose = NULL;
|
||||
PBth_BluetoothGetRadioInfo Bth_BluetoothGetRadioInfo = NULL;
|
||||
PBth_BluetoothRemoveDevice Bth_BluetoothRemoveDevice = NULL;
|
||||
PBth_BluetoothSetServiceState Bth_BluetoothSetServiceState = NULL;
|
||||
PBth_BluetoothAuthenticateDevice Bth_BluetoothAuthenticateDevice = NULL;
|
||||
PBth_BluetoothEnumerateInstalledServices Bth_BluetoothEnumerateInstalledServices = NULL;
|
||||
|
||||
HINSTANCE hid_lib = NULL;
|
||||
HINSTANCE bthprops_lib = NULL;
|
||||
|
||||
static int initialized = 0;
|
||||
|
||||
std::unordered_map<BTH_ADDR, std::time_t> g_connect_times;
|
||||
|
||||
inline void init_lib()
|
||||
{
|
||||
if (!initialized)
|
||||
@@ -109,12 +120,15 @@ inline void init_lib()
|
||||
Bth_BluetoothGetRadioInfo = (PBth_BluetoothGetRadioInfo)GetProcAddress(bthprops_lib, "BluetoothGetRadioInfo");
|
||||
Bth_BluetoothRemoveDevice = (PBth_BluetoothRemoveDevice)GetProcAddress(bthprops_lib, "BluetoothRemoveDevice");
|
||||
Bth_BluetoothSetServiceState = (PBth_BluetoothSetServiceState)GetProcAddress(bthprops_lib, "BluetoothSetServiceState");
|
||||
Bth_BluetoothAuthenticateDevice = (PBth_BluetoothAuthenticateDevice)GetProcAddress(bthprops_lib, "BluetoothAuthenticateDevice");
|
||||
Bth_BluetoothEnumerateInstalledServices = (PBth_BluetoothEnumerateInstalledServices)GetProcAddress(bthprops_lib, "BluetoothEnumerateInstalledServices");
|
||||
|
||||
if (!Bth_BluetoothFindDeviceClose || !Bth_BluetoothFindFirstDevice ||
|
||||
!Bth_BluetoothFindFirstRadio || !Bth_BluetoothFindNextDevice ||
|
||||
!Bth_BluetoothFindNextRadio || !Bth_BluetoothFindRadioClose ||
|
||||
!Bth_BluetoothGetRadioInfo || !Bth_BluetoothRemoveDevice ||
|
||||
!Bth_BluetoothSetServiceState)
|
||||
!Bth_BluetoothSetServiceState || !Bth_BluetoothAuthenticateDevice ||
|
||||
!Bth_BluetoothEnumerateInstalledServices)
|
||||
{
|
||||
PanicAlertT("Failed to load bthprops.cpl");
|
||||
exit(EXIT_FAILURE);
|
||||
@@ -127,311 +141,335 @@ inline void init_lib()
|
||||
namespace WiimoteReal
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
void ProcessWiimotes(bool new_scan, T& callback);
|
||||
|
||||
bool AttachWiimote(HANDLE hRadio, const BLUETOOTH_RADIO_INFO&, BLUETOOTH_DEVICE_INFO_STRUCT&);
|
||||
void RemoveWiimote(BLUETOOTH_DEVICE_INFO_STRUCT&);
|
||||
bool ForgetWiimote(BLUETOOTH_DEVICE_INFO_STRUCT&);
|
||||
|
||||
WiimoteScanner::WiimoteScanner()
|
||||
: m_run_thread()
|
||||
, m_want_wiimotes()
|
||||
{
|
||||
init_lib();
|
||||
}
|
||||
|
||||
WiimoteScanner::~WiimoteScanner()
|
||||
{
|
||||
// TODO: what do we want here?
|
||||
ProcessWiimotes(false, [](HANDLE, BLUETOOTH_RADIO_INFO&, BLUETOOTH_DEVICE_INFO_STRUCT& btdi)
|
||||
{
|
||||
RemoveWiimote(btdi);
|
||||
});
|
||||
}
|
||||
|
||||
void WiimoteScanner::Update()
|
||||
{
|
||||
bool forgot_some = false;
|
||||
|
||||
ProcessWiimotes(false, [&](HANDLE, BLUETOOTH_RADIO_INFO&, BLUETOOTH_DEVICE_INFO_STRUCT& btdi)
|
||||
{
|
||||
forgot_some |= ForgetWiimote(btdi);
|
||||
});
|
||||
|
||||
// Some hacks that allows disconnects to be detected before connections are handled
|
||||
// workaround for wiimote 1 moving to slot 2 on temporary disconnect
|
||||
if (forgot_some)
|
||||
SLEEP(100);
|
||||
}
|
||||
|
||||
// Find and connect wiimotes.
|
||||
// Does not replace already found wiimotes even if they are disconnected.
|
||||
// wm is an array of max_wiimotes wiimotes
|
||||
// Returns the total number of found and connected wiimotes.
|
||||
int FindWiimotes(Wiimote** wm, int max_wiimotes)
|
||||
std::vector<Wiimote*> WiimoteScanner::FindWiimotes()
|
||||
{
|
||||
GUID device_id;
|
||||
HANDLE dev;
|
||||
HDEVINFO device_info;
|
||||
int found_wiimotes = 0;
|
||||
DWORD len;
|
||||
SP_DEVICE_INTERFACE_DATA device_data;
|
||||
PSP_DEVICE_INTERFACE_DETAIL_DATA detail_data = NULL;
|
||||
HIDD_ATTRIBUTES attr;
|
||||
|
||||
init_lib();
|
||||
|
||||
// Count the number of already found wiimotes
|
||||
for (int i = 0; i < MAX_WIIMOTES; ++i)
|
||||
ProcessWiimotes(true, [](HANDLE hRadio, const BLUETOOTH_RADIO_INFO& rinfo, BLUETOOTH_DEVICE_INFO_STRUCT& btdi)
|
||||
{
|
||||
if (wm[i])
|
||||
found_wiimotes++;
|
||||
}
|
||||
|
||||
device_data.cbSize = sizeof(device_data);
|
||||
ForgetWiimote(btdi);
|
||||
AttachWiimote(hRadio, rinfo, btdi);
|
||||
});
|
||||
|
||||
// Get the device id
|
||||
GUID device_id;
|
||||
HidD_GetHidGuid(&device_id);
|
||||
|
||||
// Get all hid devices connected
|
||||
device_info = SetupDiGetClassDevs(&device_id, NULL, NULL, (DIGCF_DEVICEINTERFACE | DIGCF_PRESENT));
|
||||
HDEVINFO const device_info = SetupDiGetClassDevs(&device_id, NULL, NULL, (DIGCF_DEVICEINTERFACE | DIGCF_PRESENT));
|
||||
|
||||
for (int index = 0; found_wiimotes < max_wiimotes; ++index)
|
||||
std::vector<Wiimote*> wiimotes;
|
||||
|
||||
SP_DEVICE_INTERFACE_DATA device_data;
|
||||
device_data.cbSize = sizeof(device_data);
|
||||
PSP_DEVICE_INTERFACE_DETAIL_DATA detail_data = NULL;
|
||||
|
||||
for (int index = 0; SetupDiEnumDeviceInterfaces(device_info, NULL, &device_id, index, &device_data); ++index)
|
||||
{
|
||||
if (detail_data)
|
||||
{
|
||||
free(detail_data);
|
||||
detail_data = NULL;
|
||||
}
|
||||
|
||||
// Query the next hid device info
|
||||
if (!SetupDiEnumDeviceInterfaces(device_info, NULL, &device_id, index, &device_data))
|
||||
break;
|
||||
|
||||
// Get the size of the data block required
|
||||
DWORD len;
|
||||
SetupDiGetDeviceInterfaceDetail(device_info, &device_data, NULL, 0, &len, NULL);
|
||||
detail_data = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(len);
|
||||
detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
||||
|
||||
// Query the data for this device
|
||||
if (!SetupDiGetDeviceInterfaceDetail(device_info, &device_data, detail_data, len, NULL, NULL))
|
||||
continue;
|
||||
|
||||
// Determine if this wiimote has already been found.
|
||||
bool found = false;
|
||||
for(int i = 0; i < MAX_WIIMOTES; i++)
|
||||
if (SetupDiGetDeviceInterfaceDetail(device_info, &device_data, detail_data, len, NULL, NULL))
|
||||
{
|
||||
if(wm[i] && (wm[i]->devicepath == detail_data->DevicePath))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
auto const wm = new Wiimote;
|
||||
wm->devicepath = detail_data->DevicePath;
|
||||
wiimotes.push_back(wm);
|
||||
}
|
||||
if (found)
|
||||
continue;
|
||||
|
||||
// Open new device
|
||||
dev = CreateFile(detail_data->DevicePath,
|
||||
(GENERIC_READ | GENERIC_WRITE),
|
||||
(FILE_SHARE_READ | FILE_SHARE_WRITE),
|
||||
NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
|
||||
if (dev == INVALID_HANDLE_VALUE)
|
||||
continue;
|
||||
|
||||
// Get device attributes
|
||||
attr.Size = sizeof(attr);
|
||||
HidD_GetAttributes(dev, &attr);
|
||||
|
||||
// Find an unused slot
|
||||
unsigned int k = 0;
|
||||
for (; k < MAX_WIIMOTES && !(WIIMOTE_SRC_REAL & g_wiimote_sources[k] && !wm[k]); ++k);
|
||||
wm[k] = new Wiimote(k);
|
||||
wm[k]->dev_handle = dev;
|
||||
wm[k]->devicepath = detail_data->DevicePath;
|
||||
|
||||
if (!wm[k]->Connect())
|
||||
{
|
||||
ERROR_LOG(WIIMOTE, "Unable to connect to wiimote %i.", wm[k]->index + 1);
|
||||
delete wm[k];
|
||||
wm[k] = NULL;
|
||||
CloseHandle(dev);
|
||||
}
|
||||
else
|
||||
{
|
||||
++found_wiimotes;
|
||||
}
|
||||
}
|
||||
|
||||
if (detail_data)
|
||||
free(detail_data);
|
||||
}
|
||||
|
||||
SetupDiDestroyDeviceInfoList(device_info);
|
||||
|
||||
return found_wiimotes;
|
||||
// Don't mind me, just a random sleep to fix stuff on Windows
|
||||
//if (!wiimotes.empty())
|
||||
// SLEEP(2000);
|
||||
|
||||
return wiimotes;
|
||||
}
|
||||
|
||||
bool WiimoteScanner::IsReady() const
|
||||
{
|
||||
// TODO: don't search for a radio each time
|
||||
|
||||
BLUETOOTH_FIND_RADIO_PARAMS radioParam;
|
||||
radioParam.dwSize = sizeof(radioParam);
|
||||
|
||||
HANDLE hRadio;
|
||||
HBLUETOOTH_RADIO_FIND hFindRadio = Bth_BluetoothFindFirstRadio(&radioParam, &hRadio);
|
||||
|
||||
if (NULL != hFindRadio)
|
||||
{
|
||||
Bth_BluetoothFindRadioClose(hFindRadio);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Connect to a wiimote with a known device path.
|
||||
bool Wiimote::Connect()
|
||||
{
|
||||
if (IsConnected()) return false;
|
||||
if (IsConnected())
|
||||
return false;
|
||||
|
||||
if (!dev_handle)
|
||||
dev_handle = CreateFile(devicepath.c_str(),
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
// Having no FILE_SHARE_WRITE disallows us from connecting to the same wiimote twice.
|
||||
// This is what "WiiYourself" does.
|
||||
FILE_SHARE_READ,
|
||||
NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
|
||||
|
||||
if (dev_handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
dev_handle = CreateFile(devicepath.c_str(),
|
||||
(GENERIC_READ | GENERIC_WRITE),
|
||||
(FILE_SHARE_READ | FILE_SHARE_WRITE),
|
||||
NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
|
||||
if (dev_handle == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
}
|
||||
|
||||
hid_overlap.hEvent = CreateEvent(NULL, 1, 1, _T(""));
|
||||
hid_overlap.Offset = 0;
|
||||
hid_overlap.OffsetHigh = 0;
|
||||
|
||||
m_connected = true;
|
||||
|
||||
// Try a handshake to see if the device is actually connected
|
||||
if (!Handshake())
|
||||
{
|
||||
m_connected = false;
|
||||
dev_handle = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set LEDs
|
||||
SetLEDs(WIIMOTE_LED_1 << index);
|
||||
#if 0
|
||||
HIDD_ATTRIBUTES attr;
|
||||
attr.Size = sizeof(attr);
|
||||
if (!HidD_GetAttributes(dev_handle, &attr))
|
||||
{
|
||||
CloseHandle(dev_handle);
|
||||
dev_handle = 0;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_wiimote_thread = std::thread(std::mem_fun(&Wiimote::ThreadFunc), this);
|
||||
hid_overlap_read = OVERLAPPED();
|
||||
hid_overlap_read.hEvent = CreateEvent(NULL, true, false, NULL);
|
||||
|
||||
hid_overlap_write = OVERLAPPED();
|
||||
hid_overlap_write.hEvent = CreateEvent(NULL, true, false, NULL);
|
||||
|
||||
// TODO: thread isn't started here now, do this elsewhere
|
||||
// This isn't as drastic as it sounds, since the process in which the threads
|
||||
// reside is normal priority. Needed for keeping audio reports at a decent rate
|
||||
/*
|
||||
if (!SetThreadPriority(m_wiimote_thread.native_handle(), THREAD_PRIORITY_TIME_CRITICAL))
|
||||
{
|
||||
ERROR_LOG(WIIMOTE, "Failed to set wiimote thread priority");
|
||||
}
|
||||
|
||||
NOTICE_LOG(WIIMOTE, "Connected to wiimote %i.", index + 1);
|
||||
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
void Wiimote::RealDisconnect()
|
||||
void Wiimote::Disconnect()
|
||||
{
|
||||
if (!IsConnected())
|
||||
return;
|
||||
|
||||
m_connected = false;
|
||||
|
||||
if (m_wiimote_thread.joinable())
|
||||
m_wiimote_thread.join();
|
||||
|
||||
CloseHandle(dev_handle);
|
||||
dev_handle = 0;
|
||||
|
||||
ResetEvent(&hid_overlap);
|
||||
CloseHandle(hid_overlap_read.hEvent);
|
||||
CloseHandle(hid_overlap_write.hEvent);
|
||||
}
|
||||
|
||||
bool Wiimote::IsOpen() const
|
||||
bool Wiimote::IsConnected() const
|
||||
{
|
||||
return IsConnected();
|
||||
return dev_handle != 0;
|
||||
}
|
||||
|
||||
int Wiimote::IORead(unsigned char* buf)
|
||||
// positive = read packet
|
||||
// negative = didn't read packet
|
||||
// zero = error
|
||||
int Wiimote::IORead(u8* buf)
|
||||
{
|
||||
DWORD b, r;
|
||||
|
||||
init_lib();
|
||||
|
||||
if (!IsConnected())
|
||||
return 0;
|
||||
|
||||
// used below for a warning
|
||||
*buf = 0;
|
||||
if (!ReadFile(dev_handle, buf, MAX_PAYLOAD, &b, &hid_overlap))
|
||||
|
||||
DWORD bytes;
|
||||
ResetEvent(hid_overlap_read.hEvent);
|
||||
if (!ReadFile(dev_handle, buf, MAX_PAYLOAD - 1, &bytes, &hid_overlap_read))
|
||||
{
|
||||
// Partial read
|
||||
b = GetLastError();
|
||||
auto const err = GetLastError();
|
||||
|
||||
if ((b == ERROR_HANDLE_EOF) || (b == ERROR_DEVICE_NOT_CONNECTED))
|
||||
if (ERROR_IO_PENDING == err)
|
||||
{
|
||||
auto const r = WaitForSingleObject(hid_overlap_read.hEvent, WIIMOTE_DEFAULT_TIMEOUT);
|
||||
if (WAIT_TIMEOUT == r)
|
||||
{
|
||||
// Timeout - cancel and continue
|
||||
if (*buf)
|
||||
WARN_LOG(WIIMOTE, "Packet ignored. This may indicate a problem (timeout is %i ms).",
|
||||
WIIMOTE_DEFAULT_TIMEOUT);
|
||||
|
||||
CancelIo(dev_handle);
|
||||
bytes = -1;
|
||||
}
|
||||
else if (WAIT_FAILED == r)
|
||||
{
|
||||
WARN_LOG(WIIMOTE, "A wait error occured on reading from wiimote %i.", index + 1);
|
||||
bytes = 0;
|
||||
}
|
||||
else if (WAIT_OBJECT_0 == r)
|
||||
{
|
||||
if (!GetOverlappedResult(dev_handle, &hid_overlap_read, &bytes, TRUE))
|
||||
{
|
||||
WARN_LOG(WIIMOTE, "GetOverlappedResult failed on wiimote %i.", index + 1);
|
||||
bytes = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bytes = 0;
|
||||
}
|
||||
}
|
||||
else if (ERROR_HANDLE_EOF == err)
|
||||
{
|
||||
// Remote disconnect
|
||||
RealDisconnect();
|
||||
return 0;
|
||||
bytes = 0;
|
||||
}
|
||||
|
||||
r = WaitForSingleObject(hid_overlap.hEvent, WIIMOTE_DEFAULT_TIMEOUT);
|
||||
if (r == WAIT_TIMEOUT)
|
||||
else if (ERROR_DEVICE_NOT_CONNECTED == err)
|
||||
{
|
||||
// Timeout - cancel and continue
|
||||
|
||||
if (*buf)
|
||||
WARN_LOG(WIIMOTE, "Packet ignored. This may indicate a problem (timeout is %i ms).",
|
||||
WIIMOTE_DEFAULT_TIMEOUT);
|
||||
|
||||
CancelIo(dev_handle);
|
||||
ResetEvent(hid_overlap.hEvent);
|
||||
return 0;
|
||||
// Remote disconnect
|
||||
bytes = 0;
|
||||
}
|
||||
else if (r == WAIT_FAILED)
|
||||
else
|
||||
{
|
||||
WARN_LOG(WIIMOTE, "A wait error occured on reading from wiimote %i.", index + 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!GetOverlappedResult(dev_handle, &hid_overlap, &b, 0))
|
||||
{
|
||||
return 0;
|
||||
bytes = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// This needs to be done even if ReadFile fails, essential during init
|
||||
// Move the data over one, so we can add back in data report indicator byte (here, 0xa1)
|
||||
memmove(buf + 1, buf, MAX_PAYLOAD - 1);
|
||||
buf[0] = 0xa1;
|
||||
if (bytes > 0)
|
||||
{
|
||||
// Move the data over one, so we can add back in data report indicator byte (here, 0xa1)
|
||||
std::copy_n(buf, MAX_PAYLOAD - 1, buf + 1);
|
||||
buf[0] = 0xa1;
|
||||
|
||||
ResetEvent(hid_overlap.hEvent);
|
||||
return MAX_PAYLOAD; // XXX
|
||||
// TODO: is this really needed?
|
||||
bytes = MAX_PAYLOAD;
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
int Wiimote::IOWrite(unsigned char* buf, int len)
|
||||
int Wiimote::IOWrite(const u8* buf, int len)
|
||||
{
|
||||
DWORD bytes, dw;
|
||||
int i;
|
||||
|
||||
init_lib();
|
||||
|
||||
if (!IsConnected())
|
||||
return 0;
|
||||
|
||||
switch (stack)
|
||||
{
|
||||
case MSBT_STACK_UNKNOWN:
|
||||
{
|
||||
// Try to auto-detect the stack type
|
||||
if (i = WriteFile(dev_handle, buf + 1, 22, &bytes, &hid_overlap))
|
||||
{
|
||||
// Bluesoleil will always return 1 here, even if it's not connected
|
||||
stack = MSBT_STACK_BLUESOLEIL;
|
||||
return i;
|
||||
}
|
||||
case MSBT_STACK_UNKNOWN:
|
||||
{
|
||||
// Try to auto-detect the stack type
|
||||
stack = MSBT_STACK_BLUESOLEIL;
|
||||
if (IOWrite(buf, len))
|
||||
return 1;
|
||||
|
||||
if (i = HidD_SetOutputReport(dev_handle, buf + 1, len - 1))
|
||||
{
|
||||
stack = MSBT_STACK_MS;
|
||||
return i;
|
||||
}
|
||||
stack = MSBT_STACK_MS;
|
||||
if (IOWrite(buf, len))
|
||||
return 1;
|
||||
|
||||
dw = GetLastError();
|
||||
// Checking for 121 = timeout on semaphore/device off/disconnected to
|
||||
// avoid trouble with other stacks toshiba/widcomm
|
||||
if (dw == 121)
|
||||
{
|
||||
NOTICE_LOG(WIIMOTE, "IOWrite[MSBT_STACK_UNKNOWN]: Timeout");
|
||||
RealDisconnect();
|
||||
}
|
||||
else ERROR_LOG(WIIMOTE,
|
||||
"IOWrite[MSBT_STACK_UNKNOWN]: ERROR: %08x", dw);
|
||||
return 0;
|
||||
}
|
||||
stack = MSBT_STACK_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
case MSBT_STACK_MS:
|
||||
{
|
||||
auto result = HidD_SetOutputReport(dev_handle, const_cast<u8*>(buf) + 1, len - 1);
|
||||
//FlushFileBuffers(dev_handle);
|
||||
|
||||
case MSBT_STACK_MS:
|
||||
i = HidD_SetOutputReport(dev_handle, buf + 1, len - 1);
|
||||
dw = GetLastError();
|
||||
|
||||
if (dw == 121)
|
||||
if (!result)
|
||||
{
|
||||
auto err = GetLastError();
|
||||
if (err == 121)
|
||||
{
|
||||
// Semaphore timeout
|
||||
NOTICE_LOG(WIIMOTE, "WiimoteIOWrite[MSBT_STACK_MS]: Unable to send data to wiimote");
|
||||
RealDisconnect();
|
||||
return 0;
|
||||
}
|
||||
return i;
|
||||
else
|
||||
{
|
||||
WARN_LOG(WIIMOTE, "IOWrite[MSBT_STACK_MS]: ERROR: %08x", err);
|
||||
}
|
||||
}
|
||||
|
||||
case MSBT_STACK_BLUESOLEIL:
|
||||
return WriteFile(dev_handle, buf + 1, 22, &bytes, &hid_overlap);
|
||||
return result;
|
||||
break;
|
||||
}
|
||||
case MSBT_STACK_BLUESOLEIL:
|
||||
{
|
||||
u8 big_buf[MAX_PAYLOAD];
|
||||
if (len < MAX_PAYLOAD)
|
||||
{
|
||||
std::copy(buf, buf + len, big_buf);
|
||||
std::fill(big_buf + len, big_buf + MAX_PAYLOAD, 0);
|
||||
buf = big_buf;
|
||||
}
|
||||
|
||||
ResetEvent(hid_overlap_write.hEvent);
|
||||
DWORD bytes = 0;
|
||||
if (WriteFile(dev_handle, buf + 1, MAX_PAYLOAD - 1, &bytes, &hid_overlap_write))
|
||||
{
|
||||
// WriteFile always returns true with bluesoleil.
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const err = GetLastError();
|
||||
if (ERROR_IO_PENDING == err)
|
||||
{
|
||||
CancelIo(dev_handle);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int UnPair()
|
||||
// invokes callback for each found wiimote bluetooth device
|
||||
template <typename T>
|
||||
void ProcessWiimotes(bool new_scan, T& callback)
|
||||
{
|
||||
// TODO:
|
||||
return 0;
|
||||
}
|
||||
|
||||
// WiiMote Pair-Up, function will return amount of either new paired or unpaired devices
|
||||
// negative number on failure
|
||||
int PairUp(bool unpair)
|
||||
{
|
||||
init_lib();
|
||||
|
||||
// match strings like "Nintendo RVL-WBC-01", "Nintendo RVL-CNT-01", "Nintendo RVL-CNT-01-TR"
|
||||
const std::wregex wiimote_device_name(L"Nintendo RVL-\\w{3}-\\d{2}(-\\w{2})?");
|
||||
|
||||
int nPaired = 0;
|
||||
const std::wregex wiimote_device_name(L"Nintendo RVL-.*");
|
||||
|
||||
BLUETOOTH_DEVICE_SEARCH_PARAMS srch;
|
||||
srch.dwSize = sizeof(srch);
|
||||
@@ -441,83 +479,50 @@ int PairUp(bool unpair)
|
||||
// fConnected BT Devices
|
||||
srch.fReturnConnected = true;
|
||||
srch.fReturnUnknown = true;
|
||||
srch.fIssueInquiry = true;
|
||||
srch.cTimeoutMultiplier = 2; // == (2 * 1.28) seconds
|
||||
srch.fIssueInquiry = new_scan;
|
||||
// multiple of 1.28 seconds
|
||||
srch.cTimeoutMultiplier = 2;
|
||||
|
||||
BLUETOOTH_FIND_RADIO_PARAMS radioParam;
|
||||
radioParam.dwSize = sizeof(radioParam);
|
||||
|
||||
HANDLE hRadio;
|
||||
|
||||
// TODO: save radio(s) in the WiimoteScanner constructor?
|
||||
|
||||
// Enumerate BT radios
|
||||
HBLUETOOTH_RADIO_FIND hFindRadio = Bth_BluetoothFindFirstRadio(&radioParam, &hRadio);
|
||||
|
||||
if (NULL == hFindRadio)
|
||||
return -1;
|
||||
|
||||
while (hFindRadio)
|
||||
{
|
||||
BLUETOOTH_RADIO_INFO radioInfo;
|
||||
radioInfo.dwSize = sizeof(radioInfo);
|
||||
|
||||
// TODO: check for SUCCEEDED()
|
||||
Bth_BluetoothGetRadioInfo(hRadio, &radioInfo);
|
||||
|
||||
srch.hRadio = hRadio;
|
||||
|
||||
BLUETOOTH_DEVICE_INFO btdi;
|
||||
btdi.dwSize = sizeof(btdi);
|
||||
|
||||
// Enumerate BT devices
|
||||
HBLUETOOTH_DEVICE_FIND hFindDevice = Bth_BluetoothFindFirstDevice(&srch, &btdi);
|
||||
while (hFindDevice)
|
||||
auto const rinfo_result = Bth_BluetoothGetRadioInfo(hRadio, &radioInfo);
|
||||
if (ERROR_SUCCESS == rinfo_result)
|
||||
{
|
||||
// btdi.szName is sometimes missings it's content - it's a bt feature..
|
||||
DEBUG_LOG(WIIMOTE, "authed %i connected %i remembered %i ",
|
||||
btdi.fAuthenticated, btdi.fConnected, btdi.fRemembered);
|
||||
srch.hRadio = hRadio;
|
||||
|
||||
if (std::regex_match(btdi.szName, wiimote_device_name))
|
||||
BLUETOOTH_DEVICE_INFO btdi;
|
||||
btdi.dwSize = sizeof(btdi);
|
||||
|
||||
// Enumerate BT devices
|
||||
HBLUETOOTH_DEVICE_FIND hFindDevice = Bth_BluetoothFindFirstDevice(&srch, &btdi);
|
||||
while (hFindDevice)
|
||||
{
|
||||
if (unpair)
|
||||
{
|
||||
if (SUCCEEDED(Bth_BluetoothRemoveDevice(&btdi.Address)))
|
||||
{
|
||||
NOTICE_LOG(WIIMOTE,
|
||||
"Pair-Up: Automatically removed BT Device on shutdown: %08x",
|
||||
GetLastError());
|
||||
++nPaired;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (false == btdi.fConnected)
|
||||
{
|
||||
// TODO: improve the read of the BT driver, esp. when batteries
|
||||
// of the wiimote are removed while being fConnected
|
||||
if (btdi.fRemembered)
|
||||
{
|
||||
// Make Windows forget old expired pairing. We can pretty
|
||||
// much ignore the return value here. It either worked
|
||||
// (ERROR_SUCCESS), or the device did not exist
|
||||
// (ERROR_NOT_FOUND). In both cases, there is nothing left.
|
||||
Bth_BluetoothRemoveDevice(&btdi.Address);
|
||||
}
|
||||
// btdi.szName is sometimes missings it's content - it's a bt feature..
|
||||
DEBUG_LOG(WIIMOTE, "authed %i connected %i remembered %i ",
|
||||
btdi.fAuthenticated, btdi.fConnected, btdi.fRemembered);
|
||||
|
||||
// Activate service
|
||||
const DWORD hr = Bth_BluetoothSetServiceState(hRadio, &btdi,
|
||||
&HumanInterfaceDeviceServiceClass_UUID, BLUETOOTH_SERVICE_ENABLE);
|
||||
if (SUCCEEDED(hr))
|
||||
++nPaired;
|
||||
else
|
||||
ERROR_LOG(WIIMOTE, "Pair-Up: BluetoothSetServiceState() returned %08x", hr);
|
||||
}
|
||||
if (std::regex_match(btdi.szName, wiimote_device_name))
|
||||
{
|
||||
callback(hRadio, radioInfo, btdi);
|
||||
}
|
||||
}
|
||||
|
||||
if (false == Bth_BluetoothFindNextDevice(hFindDevice, &btdi))
|
||||
{
|
||||
Bth_BluetoothFindDeviceClose(hFindDevice);
|
||||
hFindDevice = NULL;
|
||||
if (false == Bth_BluetoothFindNextDevice(hFindDevice, &btdi))
|
||||
{
|
||||
Bth_BluetoothFindDeviceClose(hFindDevice);
|
||||
hFindDevice = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -527,8 +532,84 @@ int PairUp(bool unpair)
|
||||
hFindRadio = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nPaired;
|
||||
void RemoveWiimote(BLUETOOTH_DEVICE_INFO_STRUCT& btdi)
|
||||
{
|
||||
//if (btdi.fConnected)
|
||||
{
|
||||
if (SUCCEEDED(Bth_BluetoothRemoveDevice(&btdi.Address)))
|
||||
{
|
||||
NOTICE_LOG(WIIMOTE, "Removed BT Device", GetLastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AttachWiimote(HANDLE hRadio, const BLUETOOTH_RADIO_INFO& radio_info, BLUETOOTH_DEVICE_INFO_STRUCT& btdi)
|
||||
{
|
||||
// We don't want "remembered" devices.
|
||||
// SetServiceState will just fail with them..
|
||||
if (!btdi.fConnected && !btdi.fRemembered)
|
||||
{
|
||||
auto const& wm_addr = btdi.Address.rgBytes;
|
||||
|
||||
NOTICE_LOG(WIIMOTE, "Found wiimote (%02x:%02x:%02x:%02x:%02x:%02x). Enabling HID service.",
|
||||
wm_addr[0], wm_addr[1], wm_addr[2], wm_addr[3], wm_addr[4], wm_addr[5]);
|
||||
|
||||
#if defined(AUTHENTICATE_WIIMOTES)
|
||||
// Authenticate
|
||||
auto const& radio_addr = radio_info.address.rgBytes;
|
||||
const DWORD auth_result = Bth_BluetoothAuthenticateDevice(NULL, hRadio, &btdi,
|
||||
std::vector<WCHAR>(radio_addr, radio_addr + 6).data(), 6);
|
||||
|
||||
if (ERROR_SUCCESS != auth_result)
|
||||
ERROR_LOG(WIIMOTE, "AttachWiimote: BluetoothAuthenticateDevice returned %08x", auth_result);
|
||||
|
||||
DWORD pcServices = 16;
|
||||
GUID guids[16];
|
||||
// If this is not done, the Wii device will not remember the pairing
|
||||
const DWORD srv_result = Bth_BluetoothEnumerateInstalledServices(hRadio, &btdi, &pcServices, guids);
|
||||
|
||||
if (ERROR_SUCCESS != srv_result)
|
||||
ERROR_LOG(WIIMOTE, "AttachWiimote: BluetoothEnumerateInstalledServices returned %08x", srv_result);
|
||||
#endif
|
||||
// Activate service
|
||||
const DWORD hr = Bth_BluetoothSetServiceState(hRadio, &btdi,
|
||||
&HumanInterfaceDeviceServiceClass_UUID, BLUETOOTH_SERVICE_ENABLE);
|
||||
|
||||
g_connect_times[btdi.Address.ullLong] = std::time(nullptr);
|
||||
|
||||
if (FAILED(hr))
|
||||
ERROR_LOG(WIIMOTE, "AttachWiimote: BluetoothSetServiceState returned %08x", hr);
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Removes remembered non-connected devices
|
||||
bool ForgetWiimote(BLUETOOTH_DEVICE_INFO_STRUCT& btdi)
|
||||
{
|
||||
if (!btdi.fConnected && btdi.fRemembered)
|
||||
{
|
||||
// Time to avoid RemoveDevice after SetServiceState.
|
||||
// Sometimes SetServiceState takes a while..
|
||||
auto const avoid_forget_seconds = 5.0;
|
||||
|
||||
auto pair_time = g_connect_times.find(btdi.Address.ullLong);
|
||||
if (pair_time == g_connect_times.end()
|
||||
|| std::difftime(time(nullptr), pair_time->second) >= avoid_forget_seconds)
|
||||
{
|
||||
// Make Windows forget about device so it will re-find it if visible.
|
||||
// This is also required to detect a disconnect for some reason..
|
||||
NOTICE_LOG(WIIMOTE, "Removing remembered wiimote.");
|
||||
Bth_BluetoothRemoveDevice(&btdi.Address);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -40,8 +40,11 @@
|
||||
{
|
||||
IOBluetoothDevice *device = [l2capChannel getDevice];
|
||||
WiimoteReal::Wiimote *wm = NULL;
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lk(WiimoteReal::g_refresh_lock);
|
||||
|
||||
for (int i = 0; i < MAX_WIIMOTES; i++) {
|
||||
for (int i = 0; i < MAX_WIIMOTES; i++)
|
||||
{
|
||||
if (WiimoteReal::g_wiimotes[i] == NULL)
|
||||
continue;
|
||||
if ([device isEqual: WiimoteReal::g_wiimotes[i]->btd] == TRUE)
|
||||
@@ -77,8 +80,11 @@
|
||||
{
|
||||
IOBluetoothDevice *device = [l2capChannel getDevice];
|
||||
WiimoteReal::Wiimote *wm = NULL;
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lk(WiimoteReal::g_refresh_lock);
|
||||
|
||||
for (int i = 0; i < MAX_WIIMOTES; i++) {
|
||||
for (int i = 0; i < MAX_WIIMOTES; i++)
|
||||
{
|
||||
if (WiimoteReal::g_wiimotes[i] == NULL)
|
||||
continue;
|
||||
if ([device isEqual: WiimoteReal::g_wiimotes[i]->btd] == TRUE)
|
||||
@@ -92,41 +98,47 @@
|
||||
|
||||
WARN_LOG(WIIMOTE, "Lost channel to wiimote %i", wm->index + 1);
|
||||
|
||||
wm->RealDisconnect();
|
||||
wm->Disconnect();
|
||||
}
|
||||
@end
|
||||
|
||||
namespace WiimoteReal
|
||||
{
|
||||
|
||||
// Find wiimotes.
|
||||
// wm is an array of max_wiimotes wiimotes
|
||||
// Returns the total number of found wiimotes.
|
||||
int FindWiimotes(Wiimote **wm, int max_wiimotes)
|
||||
WiimoteScanner::WiimoteScanner()
|
||||
: m_run_thread()
|
||||
, m_want_wiimotes()
|
||||
{}
|
||||
|
||||
WiimoteScanner::~WiimoteScanner()
|
||||
{}
|
||||
|
||||
void WiimoteScanner::Update()
|
||||
{}
|
||||
|
||||
std::vector<Wiimote*> WiimoteScanner::FindWiimotes()
|
||||
{
|
||||
// TODO: find the device in the constructor and save it for later
|
||||
|
||||
std::vector<Wiimote*> wiimotes;
|
||||
IOBluetoothHostController *bth;
|
||||
IOBluetoothDeviceInquiry *bti;
|
||||
SearchBT *sbt;
|
||||
NSEnumerator *en;
|
||||
int found_devices = 0, found_wiimotes = 0;
|
||||
|
||||
// Count the number of already found wiimotes
|
||||
for (int i = 0; i < MAX_WIIMOTES; ++i)
|
||||
if (wm[i])
|
||||
found_wiimotes++;
|
||||
|
||||
bth = [[IOBluetoothHostController alloc] init];
|
||||
if ([bth addressAsString] == nil) {
|
||||
if ([bth addressAsString] == nil)
|
||||
{
|
||||
WARN_LOG(WIIMOTE, "No bluetooth host controller");
|
||||
[bth release];
|
||||
return found_wiimotes;
|
||||
return wiimotes;
|
||||
}
|
||||
|
||||
sbt = [[SearchBT alloc] init];
|
||||
sbt->maxDevices = max_wiimotes - found_wiimotes;
|
||||
sbt->maxDevices = 32;
|
||||
bti = [[IOBluetoothDeviceInquiry alloc] init];
|
||||
[bti setDelegate: sbt];
|
||||
[bti setInquiryLength: 5];
|
||||
[bti setInquiryLength: 2];
|
||||
|
||||
if ([bti start] == kIOReturnSuccess)
|
||||
[bti retain];
|
||||
@@ -136,10 +148,10 @@ int FindWiimotes(Wiimote **wm, int max_wiimotes)
|
||||
CFRunLoopRun();
|
||||
|
||||
[bti stop];
|
||||
found_devices = [[bti foundDevices] count];
|
||||
int found_devices = [[bti foundDevices] count];
|
||||
|
||||
NOTICE_LOG(WIIMOTE, "Found %i bluetooth device%c", found_devices,
|
||||
found_devices == 1 ? '\0' : 's');
|
||||
if (found_devices)
|
||||
NOTICE_LOG(WIIMOTE, "Found %i bluetooth devices", found_devices);
|
||||
|
||||
en = [[bti foundDevices] objectEnumerator];
|
||||
for (int i = 0; i < found_devices; i++)
|
||||
@@ -147,42 +159,44 @@ int FindWiimotes(Wiimote **wm, int max_wiimotes)
|
||||
IOBluetoothDevice *dev = [en nextObject];
|
||||
if (!IsValidBluetoothName([[dev name] UTF8String]))
|
||||
continue;
|
||||
// Find an unused slot
|
||||
for (int k = 0; k < MAX_WIIMOTES; k++) {
|
||||
if (wm[k] != NULL ||
|
||||
!(g_wiimote_sources[k] & WIIMOTE_SRC_REAL))
|
||||
continue;
|
||||
|
||||
wm[k] = new Wiimote(k);
|
||||
wm[k]->btd = dev;
|
||||
found_wiimotes++;
|
||||
break;
|
||||
}
|
||||
Wiimote *wm = new Wiimote();
|
||||
wm->btd = dev;
|
||||
wiimotes.push_back(wm);
|
||||
}
|
||||
|
||||
[bth release];
|
||||
[bti release];
|
||||
[sbt release];
|
||||
|
||||
return found_wiimotes;
|
||||
return wiimotes;
|
||||
}
|
||||
|
||||
bool WiimoteScanner::IsReady() const
|
||||
{
|
||||
// TODO: only return true when a BT device is present
|
||||
return true;
|
||||
}
|
||||
|
||||
// Connect to a wiimote with a known address.
|
||||
bool Wiimote::Connect()
|
||||
{
|
||||
ConnectBT *cbt = [[ConnectBT alloc] init];
|
||||
|
||||
if (IsConnected())
|
||||
return false;
|
||||
|
||||
ConnectBT *cbt = [[ConnectBT alloc] init];
|
||||
|
||||
[btd openL2CAPChannelSync: &cchan
|
||||
withPSM: kBluetoothL2CAPPSMHIDControl delegate: cbt];
|
||||
[btd openL2CAPChannelSync: &ichan
|
||||
withPSM: kBluetoothL2CAPPSMHIDInterrupt delegate: cbt];
|
||||
if (ichan == NULL || cchan == NULL) {
|
||||
if (ichan == NULL || cchan == NULL)
|
||||
{
|
||||
ERROR_LOG(WIIMOTE, "Unable to open L2CAP channels "
|
||||
"for wiimote %i", index + 1);
|
||||
RealDisconnect();
|
||||
Disconnect();
|
||||
|
||||
[cbt release];
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -196,40 +210,37 @@ bool Wiimote::Connect()
|
||||
|
||||
m_connected = true;
|
||||
|
||||
Handshake();
|
||||
SetLEDs(WIIMOTE_LED_1 << index);
|
||||
|
||||
m_wiimote_thread = std::thread(std::mem_fun(&Wiimote::ThreadFunc), this);
|
||||
|
||||
[cbt release];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Disconnect a wiimote.
|
||||
void Wiimote::RealDisconnect()
|
||||
void Wiimote::Disconnect()
|
||||
{
|
||||
if (btd != NULL)
|
||||
[btd closeConnection];
|
||||
|
||||
if (ichan != NULL)
|
||||
[ichan release];
|
||||
|
||||
if (cchan != NULL)
|
||||
[cchan release];
|
||||
|
||||
btd = NULL;
|
||||
cchan = NULL;
|
||||
ichan = NULL;
|
||||
|
||||
if (!IsConnected())
|
||||
return;
|
||||
|
||||
NOTICE_LOG(WIIMOTE, "Disconnecting wiimote %i", index + 1);
|
||||
|
||||
m_connected = false;
|
||||
|
||||
if (m_wiimote_thread.joinable())
|
||||
m_wiimote_thread.join();
|
||||
|
||||
[btd closeConnection];
|
||||
[ichan release];
|
||||
[cchan release];
|
||||
btd = NULL;
|
||||
cchan = NULL;
|
||||
ichan = NULL;
|
||||
}
|
||||
|
||||
bool Wiimote::IsOpen() const
|
||||
bool Wiimote::IsConnected() const
|
||||
{
|
||||
return IsConnected();
|
||||
return m_connected;
|
||||
}
|
||||
|
||||
int Wiimote::IORead(unsigned char *buf)
|
||||
@@ -246,14 +257,14 @@ int Wiimote::IORead(unsigned char *buf)
|
||||
return bytes;
|
||||
}
|
||||
|
||||
int Wiimote::IOWrite(unsigned char *buf, int len)
|
||||
int Wiimote::IOWrite(const unsigned char *buf, int len)
|
||||
{
|
||||
IOReturn ret;
|
||||
|
||||
if (!IsConnected())
|
||||
return 0;
|
||||
|
||||
ret = [ichan writeAsync: buf length: len refcon: nil];
|
||||
ret = [ichan writeAsync: const_cast<void*>((void *)buf) length: len refcon: nil];
|
||||
|
||||
if (ret == kIOReturnSuccess)
|
||||
return len;
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include "IniFile.h"
|
||||
#include "StringUtil.h"
|
||||
#include "Timer.h"
|
||||
#include "Host.h"
|
||||
#include "ConfigManager.h"
|
||||
|
||||
#include "WiimoteReal.h"
|
||||
|
||||
@@ -33,36 +35,43 @@ unsigned int g_wiimote_sources[MAX_WIIMOTES];
|
||||
namespace WiimoteReal
|
||||
{
|
||||
|
||||
void HandleFoundWiimotes(const std::vector<Wiimote*>&);
|
||||
void TryToConnectWiimote(Wiimote*);
|
||||
void HandleWiimoteDisconnect(int index);
|
||||
void DoneWithWiimote(int index);
|
||||
|
||||
bool g_real_wiimotes_initialized = false;
|
||||
unsigned int g_wiimotes_found = 0;
|
||||
|
||||
std::mutex g_refresh_lock;
|
||||
std::recursive_mutex g_refresh_lock;
|
||||
|
||||
Wiimote *g_wiimotes[MAX_WIIMOTES];
|
||||
Wiimote* g_wiimotes[MAX_WIIMOTES];
|
||||
|
||||
Wiimote::Wiimote(const unsigned int _index)
|
||||
: index(_index)
|
||||
WiimoteScanner g_wiimote_scanner;
|
||||
|
||||
Wiimote::Wiimote()
|
||||
: index()
|
||||
#ifdef __APPLE__
|
||||
, inputlen(0)
|
||||
, btd(), ichan(), cchan(), inputlen(), m_connected()
|
||||
#elif defined(__linux__) && HAVE_BLUEZ
|
||||
, cmd_sock(-1), int_sock(-1)
|
||||
#elif defined(_WIN32)
|
||||
, dev_handle(0), stack(MSBT_STACK_UNKNOWN)
|
||||
#endif
|
||||
, leds(0), m_last_data_report(Report((u8 *)NULL, 0))
|
||||
, m_channel(0), m_connected(false)
|
||||
, m_last_data_report(Report((u8 *)NULL, 0))
|
||||
, m_channel(0), m_run_thread(false)
|
||||
{
|
||||
#if defined(__linux__) && HAVE_BLUEZ
|
||||
bdaddr = (bdaddr_t){{0, 0, 0, 0, 0, 0}};
|
||||
#endif
|
||||
|
||||
DisableDataReporting();
|
||||
}
|
||||
|
||||
Wiimote::~Wiimote()
|
||||
{
|
||||
RealDisconnect();
|
||||
StopThread();
|
||||
|
||||
if (IsConnected())
|
||||
Disconnect();
|
||||
|
||||
ClearReadQueue();
|
||||
|
||||
// clear write queue
|
||||
@@ -71,26 +80,28 @@ Wiimote::~Wiimote()
|
||||
delete[] rpt.first;
|
||||
}
|
||||
|
||||
// Silly, copying data n stuff, o well, don't use this too often
|
||||
void Wiimote::SendPacket(const u8 rpt_id, const void* const data, const unsigned int size)
|
||||
// to be called from CPU thread
|
||||
void Wiimote::QueueReport(u8 rpt_id, const void* _data, unsigned int size)
|
||||
{
|
||||
auto const data = static_cast<const u8*>(_data);
|
||||
|
||||
Report rpt;
|
||||
rpt.second = size + 2;
|
||||
rpt.first = new u8[rpt.second];
|
||||
rpt.first[0] = 0xA1;
|
||||
rpt.first[0] = WM_SET_REPORT | WM_BT_OUTPUT;
|
||||
rpt.first[1] = rpt_id;
|
||||
memcpy(rpt.first + 2, data, size);
|
||||
std::copy(data, data + size, rpt.first + 2);
|
||||
m_write_reports.Push(rpt);
|
||||
}
|
||||
|
||||
void Wiimote::DisableDataReporting()
|
||||
{
|
||||
wm_report_mode rpt;
|
||||
wm_report_mode rpt = {};
|
||||
rpt.mode = WM_REPORT_CORE;
|
||||
rpt.all_the_time = 0;
|
||||
rpt.continuous = 0;
|
||||
rpt.rumble = 0;
|
||||
SendPacket(WM_REPORT_MODE, &rpt, sizeof(rpt));
|
||||
QueueReport(WM_REPORT_MODE, &rpt, sizeof(rpt));
|
||||
}
|
||||
|
||||
void Wiimote::ClearReadQueue()
|
||||
@@ -111,7 +122,7 @@ void Wiimote::ControlChannel(const u16 channel, const void* const data, const u3
|
||||
{
|
||||
// Check for custom communication
|
||||
if (99 == channel)
|
||||
Disconnect();
|
||||
EmuStop();
|
||||
else
|
||||
{
|
||||
InterruptChannel(channel, data, size);
|
||||
@@ -124,24 +135,24 @@ void Wiimote::ControlChannel(const u16 channel, const void* const data, const u3
|
||||
}
|
||||
}
|
||||
|
||||
void Wiimote::InterruptChannel(const u16 channel, const void* const data, const u32 size)
|
||||
void Wiimote::InterruptChannel(const u16 channel, const void* const _data, const u32 size)
|
||||
{
|
||||
if (0 == m_channel) // first interrupt/control channel sent
|
||||
// first interrupt/control channel sent
|
||||
if (channel != m_channel)
|
||||
{
|
||||
m_channel = channel;
|
||||
|
||||
ClearReadQueue();
|
||||
|
||||
// request status
|
||||
wm_request_status rpt;
|
||||
rpt.rumble = 0;
|
||||
SendPacket(WM_REQUEST_STATUS, &rpt, sizeof(rpt));
|
||||
EmuStart();
|
||||
}
|
||||
|
||||
m_channel = channel; // this right?
|
||||
|
||||
auto const data = static_cast<const u8*>(_data);
|
||||
|
||||
Report rpt;
|
||||
rpt.first = new u8[size];
|
||||
rpt.second = (u8)size;
|
||||
memcpy(rpt.first, (u8*)data, size);
|
||||
std::copy(data, data + size, rpt.first);
|
||||
|
||||
// Convert output DATA packets to SET_REPORT packets.
|
||||
// Nintendo Wiimotes work without this translation, but 3rd
|
||||
@@ -150,13 +161,27 @@ void Wiimote::InterruptChannel(const u16 channel, const void* const data, const
|
||||
{
|
||||
rpt.first[0] = WM_SET_REPORT | WM_BT_OUTPUT;
|
||||
}
|
||||
|
||||
if (rpt.first[0] == (WM_SET_REPORT | WM_BT_OUTPUT) &&
|
||||
rpt.first[1] == 0x18 && rpt.second == 23)
|
||||
|
||||
// Disallow games from turning off all of the LEDs.
|
||||
// It makes wiimote connection status confusing.
|
||||
if (rpt.first[1] == WM_LEDS)
|
||||
{
|
||||
m_audio_reports.Push(rpt);
|
||||
return;
|
||||
}
|
||||
auto& leds_rpt = *reinterpret_cast<wm_leds*>(&rpt.first[2]);
|
||||
if (0 == leds_rpt.leds)
|
||||
{
|
||||
// Turn on ALL of the LEDs.
|
||||
leds_rpt.leds = 0xf;
|
||||
}
|
||||
}
|
||||
else if (rpt.first[1] == WM_WRITE_SPEAKER_DATA
|
||||
&& !SConfig::GetInstance().m_WiimoteEnableSpeaker)
|
||||
{
|
||||
// Translate speaker data reports into rumble reports.
|
||||
rpt.first[1] = WM_CMD_RUMBLE;
|
||||
// Keep only the rumble bit.
|
||||
rpt.first[2] &= 0x1;
|
||||
rpt.second = 3;
|
||||
}
|
||||
|
||||
m_write_reports.Push(rpt);
|
||||
}
|
||||
@@ -168,7 +193,14 @@ bool Wiimote::Read()
|
||||
rpt.first = new unsigned char[MAX_PAYLOAD];
|
||||
rpt.second = IORead(rpt.first);
|
||||
|
||||
if (rpt.second > 0 && m_channel > 0) {
|
||||
if (0 == rpt.second)
|
||||
{
|
||||
WARN_LOG(WIIMOTE, "Wiimote::IORead failed. Disconnecting wiimote %d.", index + 1);
|
||||
Disconnect();
|
||||
}
|
||||
|
||||
if (rpt.second > 0 && m_channel > 0)
|
||||
{
|
||||
// Add it to queue
|
||||
m_read_reports.Push(rpt);
|
||||
return true;
|
||||
@@ -180,23 +212,25 @@ bool Wiimote::Read()
|
||||
|
||||
bool Wiimote::Write()
|
||||
{
|
||||
Report rpt;
|
||||
|
||||
if (last_audio_report.GetTimeDifference() > 5 && m_audio_reports.Pop(rpt))
|
||||
if (!m_write_reports.Empty())
|
||||
{
|
||||
IOWrite(rpt.first, rpt.second);
|
||||
last_audio_report.Update();
|
||||
Report const& rpt = m_write_reports.Front();
|
||||
|
||||
delete[] rpt.first;
|
||||
return true;
|
||||
bool const is_speaker_data = rpt.first[1] == WM_WRITE_SPEAKER_DATA;
|
||||
|
||||
if (!is_speaker_data || m_last_audio_report.GetTimeDifference() > 5)
|
||||
{
|
||||
IOWrite(rpt.first, rpt.second);
|
||||
|
||||
if (is_speaker_data)
|
||||
m_last_audio_report.Update();
|
||||
|
||||
delete[] rpt.first;
|
||||
m_write_reports.Pop();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (m_write_reports.Pop(rpt))
|
||||
{
|
||||
IOWrite(rpt.first, rpt.second);
|
||||
delete[] rpt.first;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -221,6 +255,12 @@ Report Wiimote::ProcessReadQueue()
|
||||
|
||||
void Wiimote::Update()
|
||||
{
|
||||
if (!IsConnected())
|
||||
{
|
||||
HandleWiimoteDisconnect(index);
|
||||
return;
|
||||
}
|
||||
|
||||
// Pop through the queued reports
|
||||
Report const rpt = ProcessReadQueue();
|
||||
|
||||
@@ -234,123 +274,151 @@ void Wiimote::Update()
|
||||
delete[] rpt.first;
|
||||
}
|
||||
|
||||
void Wiimote::Disconnect()
|
||||
bool Wiimote::Prepare(int _index)
|
||||
{
|
||||
index = _index;
|
||||
|
||||
// core buttons, no continuous reporting
|
||||
u8 const mode_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_CMD_REPORT_TYPE, 0, 0x30};
|
||||
|
||||
// Set the active LEDs and turn on rumble.
|
||||
u8 const led_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_CMD_LED, u8(WIIMOTE_LED_1 << index | 0x1)};
|
||||
|
||||
// Turn off rumble
|
||||
u8 rumble_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_CMD_RUMBLE, 0};
|
||||
|
||||
// Request status report
|
||||
u8 const req_status_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_REQUEST_STATUS, 0};
|
||||
// TODO: check for sane response?
|
||||
|
||||
return (IOWrite(mode_report, sizeof(mode_report))
|
||||
&& IOWrite(led_report, sizeof(led_report))
|
||||
&& (SLEEP(200), IOWrite(rumble_report, sizeof(rumble_report)))
|
||||
&& IOWrite(req_status_report, sizeof(req_status_report)));
|
||||
}
|
||||
|
||||
void Wiimote::EmuStart()
|
||||
{
|
||||
DisableDataReporting();
|
||||
}
|
||||
|
||||
void Wiimote::EmuStop()
|
||||
{
|
||||
m_channel = 0;
|
||||
|
||||
DisableDataReporting();
|
||||
|
||||
NOTICE_LOG(WIIMOTE, "Stopping wiimote data reporting");
|
||||
}
|
||||
|
||||
bool Wiimote::IsConnected() const
|
||||
unsigned int CalculateWantedWiimotes()
|
||||
{
|
||||
return m_connected;
|
||||
// Figure out how many real wiimotes are required
|
||||
unsigned int wanted_wiimotes = 0;
|
||||
for (unsigned int i = 0; i < MAX_WIIMOTES; ++i)
|
||||
if (WIIMOTE_SRC_REAL & g_wiimote_sources[i] && !g_wiimotes[i])
|
||||
++wanted_wiimotes;
|
||||
|
||||
return wanted_wiimotes;
|
||||
}
|
||||
|
||||
// Rumble briefly
|
||||
void Wiimote::Rumble()
|
||||
void WiimoteScanner::WantWiimotes(bool do_want)
|
||||
{
|
||||
if (!IsConnected())
|
||||
return;
|
||||
|
||||
unsigned char buffer = 0x01;
|
||||
DEBUG_LOG(WIIMOTE, "Starting rumble...");
|
||||
SendRequest(WM_CMD_RUMBLE, &buffer, 1);
|
||||
|
||||
SLEEP(200);
|
||||
|
||||
DEBUG_LOG(WIIMOTE, "Stopping rumble...");
|
||||
buffer = 0x00;
|
||||
SendRequest(WM_CMD_RUMBLE, &buffer, 1);
|
||||
m_want_wiimotes = do_want;
|
||||
}
|
||||
|
||||
// Set the active LEDs.
|
||||
// leds is a bitwise or of WIIMOTE_LED_1 through WIIMOTE_LED_4.
|
||||
void Wiimote::SetLEDs(int new_leds)
|
||||
void WiimoteScanner::StartScanning()
|
||||
{
|
||||
unsigned char buffer;
|
||||
|
||||
if (!IsConnected())
|
||||
return;
|
||||
|
||||
// Remove the lower 4 bits because they control rumble
|
||||
buffer = leds = (new_leds & 0xF0);
|
||||
|
||||
SendRequest(WM_CMD_LED, &buffer, 1);
|
||||
if (!m_run_thread && IsReady())
|
||||
{
|
||||
m_run_thread = true;
|
||||
m_scan_thread = std::thread(std::mem_fun(&WiimoteScanner::ThreadFunc), this);
|
||||
}
|
||||
}
|
||||
|
||||
// Send a handshake
|
||||
bool Wiimote::Handshake()
|
||||
void WiimoteScanner::StopScanning()
|
||||
{
|
||||
// Set buffer[0] to 0x04 for continuous reporting
|
||||
unsigned char buffer[2] = {0x04, 0x30};
|
||||
|
||||
if (!IsConnected())
|
||||
return 0;
|
||||
|
||||
DEBUG_LOG(WIIMOTE, "Sending handshake to wiimote");
|
||||
|
||||
return SendRequest(WM_CMD_REPORT_TYPE, buffer, 2);
|
||||
m_run_thread = false;
|
||||
if (m_scan_thread.joinable())
|
||||
{
|
||||
m_scan_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
// Send a packet to the wiimote.
|
||||
// report_type should be one of WIIMOTE_CMD_LED, WIIMOTE_CMD_RUMBLE, etc.
|
||||
bool Wiimote::SendRequest(unsigned char report_type, unsigned char* data, int length)
|
||||
void CheckForDisconnectedWiimotes()
|
||||
{
|
||||
unsigned char buffer[32] = {WM_SET_REPORT | WM_BT_OUTPUT, report_type};
|
||||
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
||||
|
||||
memcpy(buffer + 2, data, length);
|
||||
for (unsigned int i = 0; i != MAX_WIIMOTES; ++i)
|
||||
if (g_wiimotes[i] && !g_wiimotes[i]->IsConnected())
|
||||
HandleWiimoteDisconnect(i);
|
||||
}
|
||||
|
||||
return (IOWrite(buffer, length + 2) != 0);
|
||||
void WiimoteScanner::ThreadFunc()
|
||||
{
|
||||
Common::SetCurrentThreadName("Wiimote Scanning Thread");
|
||||
|
||||
NOTICE_LOG(WIIMOTE, "Wiimote scanning has started");
|
||||
|
||||
while (m_run_thread)
|
||||
{
|
||||
std::vector<Wiimote*> found_wiimotes;
|
||||
|
||||
//NOTICE_LOG(WIIMOTE, "in loop");
|
||||
|
||||
if (m_want_wiimotes)
|
||||
found_wiimotes = FindWiimotes();
|
||||
else
|
||||
{
|
||||
// Does stuff needed to detect disconnects on Windows
|
||||
Update();
|
||||
}
|
||||
|
||||
//NOTICE_LOG(WIIMOTE, "after update");
|
||||
|
||||
// TODO: this is a fairly lame place for this
|
||||
CheckForDisconnectedWiimotes();
|
||||
|
||||
HandleFoundWiimotes(found_wiimotes);
|
||||
|
||||
//std::this_thread::yield();
|
||||
Common::SleepCurrentThread(500);
|
||||
}
|
||||
|
||||
NOTICE_LOG(WIIMOTE, "Wiimote scanning has stopped");
|
||||
}
|
||||
|
||||
void Wiimote::StartThread()
|
||||
{
|
||||
m_run_thread = true;
|
||||
m_wiimote_thread = std::thread(std::mem_fun(&Wiimote::ThreadFunc), this);
|
||||
}
|
||||
|
||||
void Wiimote::StopThread()
|
||||
{
|
||||
m_run_thread = false;
|
||||
if (m_wiimote_thread.joinable())
|
||||
m_wiimote_thread.join();
|
||||
}
|
||||
|
||||
void Wiimote::ThreadFunc()
|
||||
{
|
||||
char thname[] = "Wiimote # Thread";
|
||||
thname[8] = (char)('1' + index);
|
||||
Common::SetCurrentThreadName(thname);
|
||||
|
||||
// rumble briefly
|
||||
Rumble();
|
||||
Common::SetCurrentThreadName("Wiimote Device Thread");
|
||||
|
||||
// main loop
|
||||
while (IsOpen())
|
||||
while (m_run_thread && IsConnected())
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
while (Write()) {}
|
||||
Common::SleepCurrentThread(1);
|
||||
// Reading happens elsewhere on OSX
|
||||
bool const did_something = Write();
|
||||
#else
|
||||
bool const did_something = Write() || Read();
|
||||
#endif
|
||||
if (!did_something)
|
||||
Common::SleepCurrentThread(1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
// Connect all discovered wiimotes
|
||||
// Return the number of wiimotes that successfully connected.
|
||||
static int ConnectWiimotes(Wiimote** wm)
|
||||
{
|
||||
int connected = 0;
|
||||
|
||||
for (int i = 0; i < MAX_WIIMOTES; ++i)
|
||||
{
|
||||
if (!wm[i] || wm[i]->IsConnected())
|
||||
continue;
|
||||
|
||||
if (wm[i]->Connect())
|
||||
++connected;
|
||||
else
|
||||
{
|
||||
delete wm[i];
|
||||
wm[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return connected;
|
||||
}
|
||||
#endif
|
||||
|
||||
void LoadSettings()
|
||||
{
|
||||
std::string ini_filename = File::GetUserPath(D_CONFIG_IDX) + WIIMOTE_INI_NAME ".ini";
|
||||
@@ -368,116 +436,181 @@ void LoadSettings()
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int Initialize()
|
||||
// config dialog calls this when some settings change
|
||||
void Initialize()
|
||||
{
|
||||
// Return if already initialized
|
||||
if (SConfig::GetInstance().m_WiimoteContinuousScanning)
|
||||
g_wiimote_scanner.StartScanning();
|
||||
else
|
||||
g_wiimote_scanner.StopScanning();
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
||||
|
||||
g_wiimote_scanner.WantWiimotes(0 != CalculateWantedWiimotes());
|
||||
|
||||
if (g_real_wiimotes_initialized)
|
||||
return g_wiimotes_found;
|
||||
return;
|
||||
|
||||
memset(g_wiimotes, 0, sizeof(g_wiimotes));
|
||||
NOTICE_LOG(WIIMOTE, "WiimoteReal::Initialize");
|
||||
|
||||
// Only call FindWiimotes with the number of slots configured for real wiimotes
|
||||
unsigned int wanted_wiimotes = 0;
|
||||
for (unsigned int i = 0; i < MAX_WIIMOTES; ++i)
|
||||
if (WIIMOTE_SRC_REAL & g_wiimote_sources[i])
|
||||
++wanted_wiimotes;
|
||||
|
||||
// Don't bother initializing if we don't want any real wiimotes
|
||||
if (0 == wanted_wiimotes)
|
||||
{
|
||||
g_wiimotes_found = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Initialized
|
||||
g_real_wiimotes_initialized = true;
|
||||
|
||||
g_wiimotes_found = FindWiimotes(g_wiimotes, wanted_wiimotes);
|
||||
|
||||
DEBUG_LOG(WIIMOTE, "Found %i Real Wiimotes, %i wanted",
|
||||
g_wiimotes_found, wanted_wiimotes);
|
||||
|
||||
#ifndef _WIN32
|
||||
atexit(WiimoteReal::Shutdown);
|
||||
g_wiimotes_found = ConnectWiimotes(g_wiimotes);
|
||||
#endif
|
||||
|
||||
DEBUG_LOG(WIIMOTE, "Connected to %i Real Wiimotes", g_wiimotes_found);
|
||||
|
||||
return g_wiimotes_found;
|
||||
}
|
||||
|
||||
void Shutdown(void)
|
||||
{
|
||||
if (false == g_real_wiimotes_initialized)
|
||||
{
|
||||
g_wiimote_scanner.StopScanning();
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
||||
|
||||
if (!g_real_wiimotes_initialized)
|
||||
return;
|
||||
|
||||
// Uninitialized
|
||||
NOTICE_LOG(WIIMOTE, "WiimoteReal::Shutdown");
|
||||
|
||||
g_real_wiimotes_initialized = false;
|
||||
|
||||
// Delete wiimotes
|
||||
for (unsigned int i = 0; i < MAX_WIIMOTES; ++i)
|
||||
if (g_wiimotes[i])
|
||||
HandleWiimoteDisconnect(i);
|
||||
}
|
||||
|
||||
void ChangeWiimoteSource(unsigned int index, int source)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
||||
|
||||
g_wiimote_sources[index] = source;
|
||||
g_wiimote_scanner.WantWiimotes(0 != CalculateWantedWiimotes());
|
||||
|
||||
// kill real connection (or swap to different slot)
|
||||
DoneWithWiimote(index);
|
||||
}
|
||||
|
||||
// reconnect to emu
|
||||
Host_ConnectWiimote(index, false);
|
||||
if (WIIMOTE_SRC_EMU & source)
|
||||
Host_ConnectWiimote(index, true);
|
||||
}
|
||||
|
||||
void TryToConnectWiimote(Wiimote* wm)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lk(g_refresh_lock);
|
||||
|
||||
for (unsigned int i = 0; i != MAX_WIIMOTES; ++i)
|
||||
{
|
||||
if (WIIMOTE_SRC_REAL & g_wiimote_sources[i]
|
||||
&& !g_wiimotes[i])
|
||||
{
|
||||
delete g_wiimotes[i];
|
||||
g_wiimotes[i] = NULL;
|
||||
if (wm->Connect() && wm->Prepare(i))
|
||||
{
|
||||
NOTICE_LOG(WIIMOTE, "Connected to wiimote %i.", i + 1);
|
||||
|
||||
std::swap(g_wiimotes[i], wm);
|
||||
g_wiimotes[i]->StartThread();
|
||||
|
||||
Host_ConnectWiimote(i, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_wiimote_scanner.WantWiimotes(0 != CalculateWantedWiimotes());
|
||||
|
||||
lk.unlock();
|
||||
|
||||
delete wm;
|
||||
}
|
||||
|
||||
void DoneWithWiimote(int index)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
||||
|
||||
if (g_wiimotes[index])
|
||||
{
|
||||
g_wiimotes[index]->StopThread();
|
||||
|
||||
// First see if we can use this real wiimote in another slot.
|
||||
for (unsigned int i = 0; i != MAX_WIIMOTES; ++i)
|
||||
{
|
||||
if (WIIMOTE_SRC_REAL & g_wiimote_sources[i]
|
||||
&& !g_wiimotes[i])
|
||||
{
|
||||
if (g_wiimotes[index]->Prepare(i))
|
||||
{
|
||||
std::swap(g_wiimotes[i], g_wiimotes[index]);
|
||||
g_wiimotes[i]->StartThread();
|
||||
|
||||
Host_ConnectWiimote(i, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// else, just disconnect the wiimote
|
||||
HandleWiimoteDisconnect(index);
|
||||
}
|
||||
|
||||
void HandleWiimoteDisconnect(int index)
|
||||
{
|
||||
Wiimote* wm = NULL;
|
||||
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
||||
std::swap(wm, g_wiimotes[index]);
|
||||
g_wiimote_scanner.WantWiimotes(0 != CalculateWantedWiimotes());
|
||||
}
|
||||
|
||||
if (wm)
|
||||
{
|
||||
delete wm;
|
||||
NOTICE_LOG(WIIMOTE, "Disconnected wiimote %i.", index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void HandleFoundWiimotes(const std::vector<Wiimote*>& wiimotes)
|
||||
{
|
||||
std::for_each(wiimotes.begin(), wiimotes.end(), TryToConnectWiimote);
|
||||
}
|
||||
|
||||
// This is called from the GUI thread
|
||||
void Refresh()
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(g_refresh_lock);
|
||||
|
||||
#ifdef _WIN32
|
||||
Shutdown();
|
||||
Initialize();
|
||||
#else
|
||||
// Make sure real wiimotes have been initialized
|
||||
if (!g_real_wiimotes_initialized)
|
||||
g_wiimote_scanner.StopScanning();
|
||||
|
||||
{
|
||||
Initialize();
|
||||
return;
|
||||
std::unique_lock<std::recursive_mutex> lk(g_refresh_lock);
|
||||
std::vector<Wiimote*> found_wiimotes;
|
||||
|
||||
if (0 != CalculateWantedWiimotes())
|
||||
{
|
||||
// Don't hang dolphin when searching
|
||||
lk.unlock();
|
||||
found_wiimotes = g_wiimote_scanner.FindWiimotes();
|
||||
lk.lock();
|
||||
}
|
||||
|
||||
// Find the number of slots configured for real wiimotes
|
||||
unsigned int wanted_wiimotes = 0;
|
||||
for (unsigned int i = 0; i < MAX_WIIMOTES; ++i)
|
||||
if (WIIMOTE_SRC_REAL & g_wiimote_sources[i])
|
||||
++wanted_wiimotes;
|
||||
CheckForDisconnectedWiimotes();
|
||||
|
||||
// Remove wiimotes that are paired with slots no longer configured for a
|
||||
// real wiimote or that are disconnected
|
||||
for (unsigned int i = 0; i < MAX_WIIMOTES; ++i)
|
||||
if (g_wiimotes[i] && (!(WIIMOTE_SRC_REAL & g_wiimote_sources[i]) ||
|
||||
!g_wiimotes[i]->IsConnected()))
|
||||
// Brief rumble for already connected wiimotes.
|
||||
for (int i = 0; i != MAX_WIIMOTES; ++i)
|
||||
{
|
||||
if (g_wiimotes[i])
|
||||
{
|
||||
delete g_wiimotes[i];
|
||||
g_wiimotes[i] = NULL;
|
||||
--g_wiimotes_found;
|
||||
g_wiimotes[i]->StopThread();
|
||||
g_wiimotes[i]->Prepare(i);
|
||||
g_wiimotes[i]->StartThread();
|
||||
}
|
||||
|
||||
// Scan for wiimotes if we want more
|
||||
if (wanted_wiimotes > g_wiimotes_found)
|
||||
{
|
||||
// Scan for wiimotes
|
||||
unsigned int num_wiimotes = FindWiimotes(g_wiimotes, wanted_wiimotes);
|
||||
|
||||
DEBUG_LOG(WIIMOTE, "Found %i Real Wiimotes, %i wanted", num_wiimotes, wanted_wiimotes);
|
||||
|
||||
// Connect newly found wiimotes.
|
||||
int num_new_wiimotes = ConnectWiimotes(g_wiimotes);
|
||||
|
||||
DEBUG_LOG(WIIMOTE, "Connected to %i additional Real Wiimotes", num_new_wiimotes);
|
||||
|
||||
g_wiimotes_found = num_wiimotes;
|
||||
}
|
||||
#endif
|
||||
|
||||
HandleFoundWiimotes(found_wiimotes);
|
||||
}
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
void InterruptChannel(int _WiimoteNumber, u16 _channelID, const void* _pData, u32 _Size)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(g_refresh_lock);
|
||||
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
||||
|
||||
if (g_wiimotes[_WiimoteNumber])
|
||||
g_wiimotes[_WiimoteNumber]->InterruptChannel(_channelID, _pData, _Size);
|
||||
@@ -485,7 +618,7 @@ void InterruptChannel(int _WiimoteNumber, u16 _channelID, const void* _pData, u3
|
||||
|
||||
void ControlChannel(int _WiimoteNumber, u16 _channelID, const void* _pData, u32 _Size)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(g_refresh_lock);
|
||||
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
||||
|
||||
if (g_wiimotes[_WiimoteNumber])
|
||||
g_wiimotes[_WiimoteNumber]->ControlChannel(_channelID, _pData, _Size);
|
||||
@@ -495,15 +628,21 @@ void ControlChannel(int _WiimoteNumber, u16 _channelID, const void* _pData, u32
|
||||
// Read the Wiimote once
|
||||
void Update(int _WiimoteNumber)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(g_refresh_lock);
|
||||
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
||||
|
||||
if (g_wiimotes[_WiimoteNumber])
|
||||
g_wiimotes[_WiimoteNumber]->Update();
|
||||
|
||||
// Wiimote::Update() may remove the wiimote if it was disconnected.
|
||||
if (!g_wiimotes[_WiimoteNumber])
|
||||
{
|
||||
Host_ConnectWiimote(_WiimoteNumber, false);
|
||||
}
|
||||
}
|
||||
|
||||
void StateChange(EMUSTATE_CHANGE newState)
|
||||
{
|
||||
//std::lock_guard<std::mutex> lk(g_refresh_lock);
|
||||
//std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
||||
|
||||
// TODO: disable/enable auto reporting, maybe
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#define WIIMOTE_REAL_H
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "WiimoteRealBase.h"
|
||||
#include "ChunkFile.h"
|
||||
@@ -42,7 +43,7 @@ class Wiimote : NonCopyable
|
||||
{
|
||||
friend class WiimoteEmu::Wiimote;
|
||||
public:
|
||||
Wiimote(const unsigned int _index);
|
||||
Wiimote();
|
||||
~Wiimote();
|
||||
|
||||
void ControlChannel(const u16 channel, const void* const data, const u32 size);
|
||||
@@ -53,16 +54,29 @@ public:
|
||||
|
||||
bool Read();
|
||||
bool Write();
|
||||
bool Connect();
|
||||
bool IsConnected() const;
|
||||
bool IsOpen() const;
|
||||
void Disconnect();
|
||||
void DisableDataReporting();
|
||||
void Rumble();
|
||||
void SendPacket(const u8 rpt_id, const void* const data, const unsigned int size);
|
||||
void RealDisconnect();
|
||||
|
||||
const unsigned int index;
|
||||
void StartThread();
|
||||
void StopThread();
|
||||
|
||||
// "handshake" / stop packets
|
||||
void EmuStart();
|
||||
void EmuStop();
|
||||
|
||||
// connecting and disconnecting from physical devices
|
||||
// (using address inserted by FindWiimotes)
|
||||
bool Connect();
|
||||
void Disconnect();
|
||||
|
||||
// TODO: change to something like IsRelevant
|
||||
bool IsConnected() const;
|
||||
|
||||
bool Prepare(int index);
|
||||
|
||||
void DisableDataReporting();
|
||||
|
||||
void QueueReport(u8 rpt_id, const void* data, unsigned int size);
|
||||
|
||||
int index;
|
||||
|
||||
#if defined(__APPLE__)
|
||||
IOBluetoothDevice *btd;
|
||||
@@ -70,21 +84,19 @@ public:
|
||||
IOBluetoothL2CAPChannel *cchan;
|
||||
char input[MAX_PAYLOAD];
|
||||
int inputlen;
|
||||
bool m_connected;
|
||||
#elif defined(__linux__) && HAVE_BLUEZ
|
||||
bdaddr_t bdaddr; // Bluetooth address
|
||||
int cmd_sock; // Command socket
|
||||
int int_sock; // Interrupt socket
|
||||
|
||||
void Close();
|
||||
|
||||
#elif defined(_WIN32)
|
||||
std::string devicepath; // Unique wiimote reference
|
||||
//ULONGLONG btaddr; // Bluetooth address
|
||||
HANDLE dev_handle; // HID handle
|
||||
OVERLAPPED hid_overlap; // Overlap handle
|
||||
OVERLAPPED hid_overlap_read, hid_overlap_write; // Overlap handle
|
||||
enum win_bt_stack_t stack; // Type of bluetooth stack to use
|
||||
#endif
|
||||
unsigned char leds; // Currently lit leds
|
||||
|
||||
protected:
|
||||
Report m_last_data_report;
|
||||
@@ -92,23 +104,58 @@ protected:
|
||||
|
||||
private:
|
||||
void ClearReadQueue();
|
||||
bool SendRequest(unsigned char report_type, unsigned char* data, int length);
|
||||
bool Handshake();
|
||||
void SetLEDs(int leds);
|
||||
int IORead(unsigned char* buf);
|
||||
int IOWrite(unsigned char* buf, int len);
|
||||
|
||||
int IORead(u8* buf);
|
||||
int IOWrite(u8 const* buf, int len);
|
||||
|
||||
void ThreadFunc();
|
||||
|
||||
bool m_connected;
|
||||
bool m_run_thread;
|
||||
std::thread m_wiimote_thread;
|
||||
|
||||
Common::FifoQueue<Report> m_read_reports;
|
||||
Common::FifoQueue<Report> m_write_reports;
|
||||
Common::FifoQueue<Report> m_audio_reports;
|
||||
|
||||
Common::Timer last_audio_report;
|
||||
Common::Timer m_last_audio_report;
|
||||
};
|
||||
|
||||
extern std::mutex g_refresh_lock;
|
||||
class WiimoteScanner
|
||||
{
|
||||
public:
|
||||
WiimoteScanner();
|
||||
~WiimoteScanner();
|
||||
|
||||
bool IsReady() const;
|
||||
|
||||
void WantWiimotes(bool do_want);
|
||||
|
||||
void StartScanning();
|
||||
void StopScanning();
|
||||
|
||||
std::vector<Wiimote*> FindWiimotes();
|
||||
|
||||
// function called when not looking for more wiimotes
|
||||
void Update();
|
||||
|
||||
private:
|
||||
void ThreadFunc();
|
||||
|
||||
std::thread m_scan_thread;
|
||||
|
||||
volatile bool m_run_thread;
|
||||
volatile bool m_want_wiimotes;
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
|
||||
#elif defined(__linux__) && HAVE_BLUEZ
|
||||
int device_id;
|
||||
int device_sock;
|
||||
#endif
|
||||
};
|
||||
|
||||
extern std::recursive_mutex g_refresh_lock;
|
||||
extern WiimoteScanner g_wiimote_scanner;
|
||||
extern Wiimote *g_wiimotes[4];
|
||||
|
||||
void InterruptChannel(int _WiimoteNumber, u16 _channelID, const void* _pData, u32 _Size);
|
||||
@@ -119,6 +166,7 @@ void DoState(PointerWrap &p);
|
||||
void StateChange(EMUSTATE_CHANGE newState);
|
||||
|
||||
int FindWiimotes(Wiimote** wm, int max_wiimotes);
|
||||
void ChangeWiimoteSource(unsigned int index, int source);
|
||||
|
||||
bool IsValidBluetoothName(const std::string& name);
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#define WM_SET_REPORT 0xA0
|
||||
#endif
|
||||
|
||||
// TODO: duplicated in WiimoteHid.h
|
||||
// Commands
|
||||
#define WM_CMD_RUMBLE 0x10
|
||||
#define WM_CMD_LED 0x11
|
||||
@@ -66,8 +67,9 @@
|
||||
|
||||
// End Wiimote internal codes
|
||||
|
||||
#define MAX_PAYLOAD 32
|
||||
#define WIIMOTE_DEFAULT_TIMEOUT 30
|
||||
// It's 23. NOT 32!
|
||||
#define MAX_PAYLOAD 23
|
||||
#define WIIMOTE_DEFAULT_TIMEOUT 1000
|
||||
|
||||
#ifdef _WIN32
|
||||
// Available bluetooth stacks for Windows.
|
||||
|
||||
@@ -211,8 +211,7 @@ void CWII_IPC_HLE_WiiMote::EventConnectionAccepted()
|
||||
void CWII_IPC_HLE_WiiMote::EventDisconnect()
|
||||
{
|
||||
// Send disconnect message to plugin
|
||||
u8 Message = WIIMOTE_DISCONNECT;
|
||||
Wiimote::ControlChannel(m_ConnectionHandle & 0xFF, 99, &Message, 0);
|
||||
Wiimote::ControlChannel(m_ConnectionHandle & 0xFF, 99, NULL, 0);
|
||||
|
||||
m_ConnectionState = CONN_INACTIVE;
|
||||
// Clear channel flags
|
||||
|
||||
@@ -28,8 +28,6 @@
|
||||
#undef _interlockedbittestandreset
|
||||
#undef _interlockedbittestandset64
|
||||
#undef _interlockedbittestandreset64
|
||||
#else
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
|
||||
#include "../../Core.h"
|
||||
@@ -514,4 +512,4 @@ void Interpreter::fsqrtx(UGeckoInstruction _inst)
|
||||
rPS0(_inst.FD) = sqrt(b);
|
||||
UpdateFPRF(rPS0(_inst.FD));
|
||||
if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,7 @@
|
||||
#include "Interpreter.h"
|
||||
#include "../../Core.h"
|
||||
|
||||
#include "../JitCommon/JitBase.h"
|
||||
#include "../JitCommon/JitCache.h"
|
||||
#include "../JitInterface.h"
|
||||
|
||||
#include "Interpreter_FPUtils.h"
|
||||
|
||||
@@ -363,34 +362,24 @@ void Interpreter::dcbf(UGeckoInstruction _inst)
|
||||
{
|
||||
NPC = PC + 12;
|
||||
}*/
|
||||
// Invalidate the jit block cache on dcbf
|
||||
if (jit)
|
||||
{
|
||||
u32 address = Helper_Get_EA_X(_inst);
|
||||
jit->GetBlockCache()->InvalidateICache(address & ~0x1f, 32);
|
||||
}
|
||||
JitInterface::InvalidateICache(address & ~0x1f, 32);
|
||||
}
|
||||
|
||||
void Interpreter::dcbi(UGeckoInstruction _inst)
|
||||
{
|
||||
// Removes a block from data cache. Since we don't emulate the data cache, we don't need to do anything to the data cache
|
||||
// However, we invalidate the jit block cache on dcbi
|
||||
if (jit)
|
||||
{
|
||||
u32 address = Helper_Get_EA_X(_inst);
|
||||
jit->GetBlockCache()->InvalidateICache(address & ~0x1f, 32);
|
||||
}
|
||||
JitInterface::InvalidateICache(address & ~0x1f, 32);
|
||||
}
|
||||
|
||||
void Interpreter::dcbst(UGeckoInstruction _inst)
|
||||
{
|
||||
// Cache line flush. Since we don't emulate the data cache, we don't need to do anything.
|
||||
// Invalidate the jit block cache on dcbst in case new code has been loaded via the data cache
|
||||
if (jit)
|
||||
{
|
||||
u32 address = Helper_Get_EA_X(_inst);
|
||||
jit->GetBlockCache()->InvalidateICache(address & ~0x1f, 32);
|
||||
}
|
||||
JitInterface::InvalidateICache(address & ~0x1f, 32);
|
||||
}
|
||||
|
||||
void Interpreter::dcbt(UGeckoInstruction _inst)
|
||||
@@ -409,7 +398,7 @@ void Interpreter::dcbz(UGeckoInstruction _inst)
|
||||
// HACK but works... we think
|
||||
if (!Core::g_CoreStartupParameter.bDCBZOFF)
|
||||
Memory::Memset(Helper_Get_EA_X(_inst) & (~31), 0, 32);
|
||||
if (!jit)
|
||||
if (!JitInterface::GetCore())
|
||||
PowerPC::CheckExceptions();
|
||||
}
|
||||
|
||||
|
||||
@@ -26,13 +26,6 @@
|
||||
#undef _interlockedbittestandreset
|
||||
#undef _interlockedbittestandset64
|
||||
#undef _interlockedbittestandreset64
|
||||
#else
|
||||
static const unsigned short FPU_ROUND_NEAR = 0 << 10;
|
||||
static const unsigned short FPU_ROUND_DOWN = 1 << 10;
|
||||
static const unsigned short FPU_ROUND_UP = 2 << 10;
|
||||
static const unsigned short FPU_ROUND_CHOP = 3 << 10;
|
||||
static const unsigned short FPU_ROUND_MASK = 3 << 10;
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
|
||||
#include "CPUDetect.h"
|
||||
@@ -43,6 +36,7 @@ static const unsigned short FPU_ROUND_MASK = 3 << 10;
|
||||
#include "../../HW/SystemTimers.h"
|
||||
#include "../../Core.h"
|
||||
#include "Interpreter.h"
|
||||
#include "FPURoundMode.h"
|
||||
|
||||
#include "Interpreter_FPUtils.h"
|
||||
|
||||
@@ -61,38 +55,11 @@ mffsx: 80036650 (huh?)
|
||||
// That is, set rounding mode etc when entering jit code or the interpreter loop
|
||||
// Restore rounding mode when calling anything external
|
||||
|
||||
const u32 MASKS = 0x1F80; // mask away the interrupts.
|
||||
const u32 DAZ = 0x40;
|
||||
const u32 FTZ = 0x8000;
|
||||
|
||||
static void FPSCRtoFPUSettings(UReg_FPSCR fp)
|
||||
{
|
||||
// Set FPU rounding mode to mimic the PowerPC's
|
||||
#ifdef _M_IX86
|
||||
// This shouldn't really be needed anymore since we use SSE
|
||||
#ifdef _WIN32
|
||||
const int table[4] =
|
||||
{
|
||||
_RC_NEAR,
|
||||
_RC_CHOP,
|
||||
_RC_UP,
|
||||
_RC_DOWN
|
||||
};
|
||||
_set_controlfp(_MCW_RC, table[fp.RN]);
|
||||
#else
|
||||
const unsigned short table[4] =
|
||||
{
|
||||
FPU_ROUND_NEAR,
|
||||
FPU_ROUND_CHOP,
|
||||
FPU_ROUND_UP,
|
||||
FPU_ROUND_DOWN
|
||||
};
|
||||
unsigned short mode;
|
||||
asm ("fstcw %0" : "=m" (mode) : );
|
||||
mode = (mode & ~FPU_ROUND_MASK) | table[fp.RN];
|
||||
asm ("fldcw %0" : : "m" (mode));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
FPURoundMode::SetRoundMode(fp.RN);
|
||||
|
||||
if (fp.VE || fp.OE || fp.UE || fp.ZE || fp.XE)
|
||||
{
|
||||
//PanicAlert("FPSCR - exceptions enabled. Please report. VE=%i OE=%i UE=%i ZE=%i XE=%i",
|
||||
@@ -101,14 +68,6 @@ static void FPSCRtoFPUSettings(UReg_FPSCR fp)
|
||||
}
|
||||
|
||||
// Also corresponding SSE rounding mode setting
|
||||
static const u32 ssetable[4] =
|
||||
{
|
||||
(0 << 13) | MASKS,
|
||||
(3 << 13) | MASKS,
|
||||
(2 << 13) | MASKS,
|
||||
(1 << 13) | MASKS,
|
||||
};
|
||||
u32 csr = ssetable[FPSCR.RN];
|
||||
if (FPSCR.NI)
|
||||
{
|
||||
// Either one of these two breaks Beyond Good & Evil.
|
||||
@@ -116,7 +75,7 @@ static void FPSCRtoFPUSettings(UReg_FPSCR fp)
|
||||
// csr |= DAZ;
|
||||
// csr |= FTZ;
|
||||
}
|
||||
_mm_setcsr(csr);
|
||||
FPURoundMode::SetSIMDMode(FPSCR.RN);
|
||||
}
|
||||
|
||||
void Interpreter::mtfsb0x(UGeckoInstruction _inst)
|
||||
|
||||
@@ -267,7 +267,7 @@ static GekkoOPTemplate table31[] =
|
||||
{19, Interpreter::mfcr, {"mfcr", OPTYPE_SYSTEM, FL_OUT_D, 0, 0, 0, 0}},
|
||||
{83, Interpreter::mfmsr, {"mfmsr", OPTYPE_SYSTEM, FL_OUT_D, 0, 0, 0, 0}},
|
||||
{144, Interpreter::mtcrf, {"mtcrf", OPTYPE_SYSTEM, 0, 0, 0, 0, 0}},
|
||||
{146, Interpreter::mtmsr, {"mtmsr", OPTYPE_SYSTEM, FL_ENDBLOCK, 0, 0, 0, 0}},
|
||||
{146, Interpreter::mtmsr, {"mtmsr", OPTYPE_SYSTEM, FL_IN_S | FL_ENDBLOCK, 0, 0, 0, 0}},
|
||||
{210, Interpreter::mtsr, {"mtsr", OPTYPE_SYSTEM, 0, 0, 0, 0, 0}},
|
||||
{242, Interpreter::mtsrin, {"mtsrin", OPTYPE_SYSTEM, 0, 0, 0, 0, 0}},
|
||||
{339, Interpreter::mfspr, {"mfspr", OPTYPE_SPR, FL_OUT_D, 0, 0, 0, 0}},
|
||||
@@ -297,6 +297,7 @@ static GekkoOPTemplate table31_2[] =
|
||||
{10, Interpreter::addcx, {"addcx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT, 0, 0, 0, 0}},
|
||||
{522, Interpreter::addcx, {"addcox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT, 0, 0, 0, 0}},
|
||||
{138, Interpreter::addex, {"addex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT, 0, 0, 0, 0}},
|
||||
{650, Interpreter::addex, {"addeox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT, 0, 0, 0, 0}},
|
||||
{234, Interpreter::addmex, {"addmex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT, 0, 0, 0, 0}},
|
||||
{202, Interpreter::addzex, {"addzex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT, 0, 0, 0, 0}},
|
||||
{491, Interpreter::divwx, {"divwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39, 0, 0, 0}},
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
#include "Common.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
#include "Thunk.h"
|
||||
#include "../../HLE/HLE.h"
|
||||
#include "../../Core.h"
|
||||
@@ -341,7 +341,7 @@ void Jit64::WriteRfiExitDestInEAX()
|
||||
Cleanup();
|
||||
ABI_CallFunction(reinterpret_cast<void *>(&PowerPC::CheckExceptions));
|
||||
SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount));
|
||||
JMP(asm_routines.dispatcher, true);
|
||||
JMP(asm_routines.outerLoop, true);
|
||||
}
|
||||
|
||||
void Jit64::WriteExceptionExit()
|
||||
@@ -360,7 +360,7 @@ void Jit64::WriteExternalExceptionExit()
|
||||
MOV(32, R(EAX), M(&PC));
|
||||
MOV(32, M(&NPC), R(EAX));
|
||||
ABI_CallFunction(reinterpret_cast<void *>(&PowerPC::CheckExternalExceptions));
|
||||
SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount));
|
||||
SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount));
|
||||
JMP(asm_routines.dispatcher, true);
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
if (js.memcheck) \
|
||||
SetJumpTarget(memException);
|
||||
|
||||
class Jit64 : public JitBase
|
||||
class Jit64 : public Jitx86Base
|
||||
{
|
||||
private:
|
||||
GPRRegCache gpr;
|
||||
|
||||
@@ -310,6 +310,7 @@ static GekkoOPTemplate table31_2[] =
|
||||
{10, &Jit64::addcx}, //"addcx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT}},
|
||||
{522, &Jit64::addcx}, //"addcox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT}},
|
||||
{138, &Jit64::addex}, //"addex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
|
||||
{650, &Jit64::addex}, //"addeox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
|
||||
{234, &Jit64::addmex}, //"addmex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
|
||||
{202, &Jit64::addzex}, //"addzex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
|
||||
{491, &Jit64::divwx}, //"divwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39}},
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
#include "x64Emitter.h"
|
||||
|
||||
#include "../../HW/Memmap.h"
|
||||
@@ -24,7 +24,7 @@
|
||||
#include "../../CoreTiming.h"
|
||||
#include "MemoryUtil.h"
|
||||
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
#include "Jit.h"
|
||||
#include "../JitCommon/JitCache.h"
|
||||
|
||||
@@ -204,7 +204,7 @@ void Jit64AsmRoutineManager::Generate()
|
||||
MOV(32, M(&NPC), R(EAX));
|
||||
ABI_CallFunction(reinterpret_cast<void *>(&PowerPC::CheckExternalExceptions));
|
||||
SetJumpTarget(noExtException);
|
||||
|
||||
|
||||
TEST(32, M((void*)PowerPC::GetStatePtr()), Imm32(0xFFFFFFFF));
|
||||
J_CC(CC_Z, outerLoop, true);
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#ifndef _JIT64ASM_H
|
||||
#define _JIT64ASM_H
|
||||
|
||||
#include "x64Emitter.h"
|
||||
#include "../JitCommon/JitAsmCommon.h"
|
||||
|
||||
// In Dolphin, we don't use inline assembly. Instead, we generate all machine-near
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "../../HW/Memmap.h"
|
||||
#include "../PPCTables.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
|
||||
#include "Jit.h"
|
||||
#include "JitAsm.h"
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "../PPCTables.h"
|
||||
#include "CPUDetect.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
|
||||
#include "Jit.h"
|
||||
#include "JitAsm.h"
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "../PPCTables.h"
|
||||
#include "CPUDetect.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
|
||||
#include "Jit.h"
|
||||
#include "JitAsm.h"
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "../PowerPC.h"
|
||||
#include "../PPCTables.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
#include "Thunk.h"
|
||||
|
||||
#include "Jit.h"
|
||||
|
||||
@@ -2009,8 +2009,10 @@ void JitIL::WriteCode() {
|
||||
}
|
||||
|
||||
void ProfiledReJit() {
|
||||
jit->SetCodePtr(jit->js.rewriteStart);
|
||||
DoWriteCode(&((JitIL *)jit)->ibuild, (JitIL *)jit, true, false);
|
||||
jit->js.curBlock->codeSize = (int)(jit->GetCodePtr() - jit->js.rewriteStart);
|
||||
jit->GetBlockCache()->FinalizeBlock(jit->js.curBlock->blockNum, jit->jo.enableBlocklink, jit->js.curBlock->normalEntry);
|
||||
JitIL *jitil = (JitIL *)jit;
|
||||
jitil->SetCodePtr(jitil->js.rewriteStart);
|
||||
DoWriteCode(&jitil->ibuild, jitil, true, false);
|
||||
jitil->js.curBlock->codeSize = (int)(jitil->GetCodePtr() - jitil->js.rewriteStart);
|
||||
jitil->GetBlockCache()->FinalizeBlock(jitil->js.curBlock->blockNum, jitil->jo.enableBlocklink,
|
||||
jitil->js.curBlock->normalEntry);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#include "Common.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
#include "Thunk.h"
|
||||
#include "../../HLE/HLE.h"
|
||||
#include "../../Core.h"
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
#define DISABLE64
|
||||
#endif
|
||||
|
||||
class JitIL : public JitBase
|
||||
class JitIL : public Jitx86Base
|
||||
{
|
||||
private:
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
#include "x64Emitter.h"
|
||||
|
||||
#include "../../HW/Memmap.h"
|
||||
@@ -25,7 +25,7 @@
|
||||
#include "MemoryUtil.h"
|
||||
#include "CPUDetect.h"
|
||||
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
#include "Thunk.h"
|
||||
|
||||
#include "../../HW/GPFifo.h"
|
||||
@@ -211,14 +211,14 @@ void JitILAsmRoutineManager::Generate()
|
||||
doTiming = GetCodePtr();
|
||||
|
||||
ABI_CallFunction(reinterpret_cast<void *>(&CoreTiming::Advance));
|
||||
|
||||
|
||||
testExceptions = GetCodePtr();
|
||||
MOV(32, R(EAX), M(&PC));
|
||||
MOV(32, M(&NPC), R(EAX));
|
||||
ABI_CallFunction(reinterpret_cast<void *>(&PowerPC::CheckExceptions));
|
||||
MOV(32, R(EAX), M(&NPC));
|
||||
MOV(32, M(&PC), R(EAX));
|
||||
|
||||
|
||||
TEST(32, M((void*)PowerPC::GetStatePtr()), Imm32(0xFFFFFFFF));
|
||||
J_CC(CC_Z, outer_loop, true);
|
||||
//Landing pad for drec space
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "../../HW/Memmap.h"
|
||||
#include "../PPCTables.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
|
||||
#include "JitIL.h"
|
||||
#include "JitILAsm.h"
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "../PPCTables.h"
|
||||
#include "CPUDetect.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
|
||||
#include "JitIL.h"
|
||||
#include "JitILAsm.h"
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#include "../PPCTables.h"
|
||||
#include "CPUDetect.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
|
||||
#include "JitIL.h"
|
||||
#include "JitILAsm.h"
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "../PowerPC.h"
|
||||
#include "../PPCTables.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
#include "Thunk.h"
|
||||
|
||||
#include "JitIL.h"
|
||||
|
||||
@@ -311,6 +311,7 @@ static GekkoOPTemplate table31_2[] =
|
||||
{10, &JitIL::Default}, //"addcx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT}},
|
||||
{522, &JitIL::Default}, //"addcox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT}},
|
||||
{138, &JitIL::addex}, //"addex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
|
||||
{650, &JitIL::addex}, //"addeox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
|
||||
{234, &JitIL::Default}, //"addmex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
|
||||
{202, &JitIL::addzex}, //"addzex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
|
||||
{491, &JitIL::Default}, //"divwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39}},
|
||||
|
||||
@@ -0,0 +1,498 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "Common.h"
|
||||
#include "../../HLE/HLE.h"
|
||||
#include "../../Core.h"
|
||||
#include "../../PatchEngine.h"
|
||||
#include "../../CoreTiming.h"
|
||||
#include "../../ConfigManager.h"
|
||||
#include "../PowerPC.h"
|
||||
#include "../Profiler.h"
|
||||
#include "../PPCTables.h"
|
||||
#include "../PPCAnalyst.h"
|
||||
#include "../../HW/Memmap.h"
|
||||
#include "../../HW/GPFifo.h"
|
||||
#include "Jit.h"
|
||||
#include "JitArm_Tables.h"
|
||||
#include "ArmEmitter.h"
|
||||
#include "../JitInterface.h"
|
||||
|
||||
using namespace ArmGen;
|
||||
using namespace PowerPC;
|
||||
|
||||
static int CODE_SIZE = 1024*1024*32;
|
||||
namespace CPUCompare
|
||||
{
|
||||
extern u32 m_BlockStart;
|
||||
}
|
||||
|
||||
void JitArm::Init()
|
||||
{
|
||||
AllocCodeSpace(CODE_SIZE);
|
||||
blocks.Init();
|
||||
asm_routines.Init();
|
||||
// TODO: Investigate why the register cache crashes when only doing Init with
|
||||
// the pointer to this. Seems for some reason it doesn't set the emitter pointer
|
||||
// In the class for some reason?
|
||||
gpr.Init(this);
|
||||
gpr.SetEmitter(this);
|
||||
fpr.Init(this);
|
||||
fpr.SetEmitter(this);
|
||||
jo.enableBlocklink = true;
|
||||
jo.optimizeGatherPipe = false;
|
||||
}
|
||||
|
||||
void JitArm::ClearCache()
|
||||
{
|
||||
ClearCodeSpace();
|
||||
blocks.Clear();
|
||||
}
|
||||
|
||||
void JitArm::Shutdown()
|
||||
{
|
||||
FreeCodeSpace();
|
||||
blocks.Shutdown();
|
||||
asm_routines.Shutdown();
|
||||
}
|
||||
|
||||
// This is only called by Default() in this file. It will execute an instruction with the interpreter functions.
|
||||
void JitArm::WriteCallInterpreter(UGeckoInstruction inst)
|
||||
{
|
||||
gpr.Flush();
|
||||
fpr.Flush();
|
||||
Interpreter::_interpreterInstruction instr = GetInterpreterOp(inst);
|
||||
MOVI2R(R0, inst.hex);
|
||||
MOVI2R(R12, (u32)instr);
|
||||
BL(R12);
|
||||
}
|
||||
void JitArm::unknown_instruction(UGeckoInstruction inst)
|
||||
{
|
||||
PanicAlert("unknown_instruction %08x - Fix me ;)", inst.hex);
|
||||
}
|
||||
|
||||
void JitArm::Default(UGeckoInstruction _inst)
|
||||
{
|
||||
WriteCallInterpreter(_inst.hex);
|
||||
}
|
||||
|
||||
void JitArm::HLEFunction(UGeckoInstruction _inst)
|
||||
{
|
||||
gpr.Flush();
|
||||
fpr.Flush();
|
||||
MOVI2R(R0, js.compilerPC);
|
||||
MOVI2R(R1, _inst.hex);
|
||||
QuickCallFunction(R14, (void*)&HLE::Execute);
|
||||
ARMReg rA = gpr.GetReg();
|
||||
LDR(rA, R9, STRUCT_OFF(PowerPC::ppcState, npc));
|
||||
WriteExitDestInR(rA);
|
||||
}
|
||||
|
||||
void JitArm::DoNothing(UGeckoInstruction _inst)
|
||||
{
|
||||
// Yup, just don't do anything.
|
||||
}
|
||||
|
||||
static const bool ImHereDebug = false;
|
||||
static const bool ImHereLog = false;
|
||||
static std::map<u32, int> been_here;
|
||||
|
||||
static void ImHere()
|
||||
{
|
||||
static File::IOFile f;
|
||||
if (ImHereLog)
|
||||
{
|
||||
if (!f)
|
||||
{
|
||||
f.Open("log32.txt", "w");
|
||||
}
|
||||
fprintf(f.GetHandle(), "%08x\n", PC);
|
||||
}
|
||||
if (been_here.find(PC) != been_here.end())
|
||||
{
|
||||
been_here.find(PC)->second++;
|
||||
if ((been_here.find(PC)->second) & 1023)
|
||||
return;
|
||||
}
|
||||
DEBUG_LOG(DYNA_REC, "I'm here - PC = %08x , LR = %08x", PC, LR);
|
||||
been_here[PC] = 1;
|
||||
}
|
||||
|
||||
void JitArm::Cleanup()
|
||||
{
|
||||
if (jo.optimizeGatherPipe && js.fifoBytesThisBlock > 0)
|
||||
QuickCallFunction(R14, (void*)&GPFifo::CheckGatherPipe);
|
||||
}
|
||||
void JitArm::DoDownCount()
|
||||
{
|
||||
ARMReg rA = gpr.GetReg();
|
||||
ARMReg rB = gpr.GetReg();
|
||||
MOVI2R(rA, (u32)&CoreTiming::downcount);
|
||||
LDR(rB, rA);
|
||||
if(js.downcountAmount < 255) // We can enlarge this if we used rotations
|
||||
{
|
||||
SUBS(rB, rB, js.downcountAmount);
|
||||
STR(rA, rB);
|
||||
}
|
||||
else
|
||||
{
|
||||
ARMReg rC = gpr.GetReg(false);
|
||||
MOVI2R(rC, js.downcountAmount);
|
||||
SUBS(rB, rB, rC);
|
||||
STR(rA, rB);
|
||||
}
|
||||
gpr.Unlock(rA, rB);
|
||||
}
|
||||
void JitArm::WriteExitDestInR(ARMReg Reg)
|
||||
{
|
||||
STR(R9, Reg, STRUCT_OFF(PowerPC::ppcState, pc));
|
||||
Cleanup();
|
||||
DoDownCount();
|
||||
MOVI2R(Reg, (u32)asm_routines.dispatcher);
|
||||
B(Reg);
|
||||
gpr.Unlock(Reg);
|
||||
}
|
||||
void JitArm::WriteRfiExitDestInR(ARMReg Reg)
|
||||
{
|
||||
STR(R9, Reg, STRUCT_OFF(PowerPC::ppcState, pc));
|
||||
Cleanup();
|
||||
DoDownCount();
|
||||
|
||||
MOVI2R(Reg, (u32)asm_routines.testExceptions);
|
||||
B(Reg);
|
||||
gpr.Unlock(Reg); // This was locked in the instruction beforehand
|
||||
}
|
||||
void JitArm::WriteExceptionExit()
|
||||
{
|
||||
ARMReg A = gpr.GetReg(false);
|
||||
Cleanup();
|
||||
DoDownCount();
|
||||
|
||||
MOVI2R(A, (u32)asm_routines.testExceptions);
|
||||
B(A);
|
||||
}
|
||||
void JitArm::WriteExit(u32 destination, int exit_num)
|
||||
{
|
||||
Cleanup();
|
||||
|
||||
DoDownCount();
|
||||
//If nobody has taken care of this yet (this can be removed when all branches are done)
|
||||
JitBlock *b = js.curBlock;
|
||||
b->exitAddress[exit_num] = destination;
|
||||
b->exitPtrs[exit_num] = GetWritableCodePtr();
|
||||
|
||||
// Link opportunity!
|
||||
int block = blocks.GetBlockNumberFromStartAddress(destination);
|
||||
if (block >= 0 && jo.enableBlocklink)
|
||||
{
|
||||
// It exists! Joy of joy!
|
||||
B(blocks.GetBlock(block)->checkedEntry);
|
||||
b->linkStatus[exit_num] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ARMReg A = gpr.GetReg(false);
|
||||
MOVI2R(A, destination);
|
||||
STR(R9, A, STRUCT_OFF(PowerPC::ppcState, pc));
|
||||
MOVI2R(A, (u32)asm_routines.dispatcher);
|
||||
B(A);
|
||||
}
|
||||
}
|
||||
|
||||
void STACKALIGN JitArm::Run()
|
||||
{
|
||||
CompiledCode pExecAddr = (CompiledCode)asm_routines.enterCode;
|
||||
pExecAddr();
|
||||
}
|
||||
|
||||
void JitArm::SingleStep()
|
||||
{
|
||||
CompiledCode pExecAddr = (CompiledCode)asm_routines.enterCode;
|
||||
pExecAddr();
|
||||
}
|
||||
|
||||
void JitArm::Trace()
|
||||
{
|
||||
char regs[500] = "";
|
||||
char fregs[750] = "";
|
||||
|
||||
#ifdef JIT_LOG_GPR
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
char reg[50];
|
||||
sprintf(reg, "r%02d: %08x ", i, PowerPC::ppcState.gpr[i]);
|
||||
strncat(regs, reg, 500);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef JIT_LOG_FPR
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
char reg[50];
|
||||
sprintf(reg, "f%02d: %016x ", i, riPS0(i));
|
||||
strncat(fregs, reg, 750);
|
||||
}
|
||||
#endif
|
||||
|
||||
DEBUG_LOG(DYNA_REC, "JITARM PC: %08x SRR0: %08x SRR1: %08x CRfast: %02x%02x%02x%02x%02x%02x%02x%02x FPSCR: %08x MSR: %08x LR: %08x %s %s",
|
||||
PC, SRR0, SRR1, PowerPC::ppcState.cr_fast[0], PowerPC::ppcState.cr_fast[1], PowerPC::ppcState.cr_fast[2], PowerPC::ppcState.cr_fast[3],
|
||||
PowerPC::ppcState.cr_fast[4], PowerPC::ppcState.cr_fast[5], PowerPC::ppcState.cr_fast[6], PowerPC::ppcState.cr_fast[7], PowerPC::ppcState.fpscr,
|
||||
PowerPC::ppcState.msr, PowerPC::ppcState.spr[8], regs, fregs);
|
||||
}
|
||||
|
||||
void JitArm::PrintDebug(UGeckoInstruction inst, u32 level)
|
||||
{
|
||||
if (level > 0)
|
||||
printf("Start: %08x OP '%s' Info\n", (u32)GetCodePtr(), PPCTables::GetInstructionName(inst));
|
||||
if (level > 1)
|
||||
{
|
||||
GekkoOPInfo* Info = GetOpInfo(inst.hex);
|
||||
printf("\tOuts\n");
|
||||
if (Info->flags & FL_OUT_A)
|
||||
printf("\t-OUT_A: %x\n", inst.RA);
|
||||
if(Info->flags & FL_OUT_D)
|
||||
printf("\t-OUT_D: %x\n", inst.RD);
|
||||
printf("\tIns\n");
|
||||
// A, AO, B, C, S
|
||||
if(Info->flags & FL_IN_A)
|
||||
printf("\t-IN_A: %x\n", inst.RA);
|
||||
if(Info->flags & FL_IN_A0)
|
||||
printf("\t-IN_A0: %x\n", inst.RA);
|
||||
if(Info->flags & FL_IN_B)
|
||||
printf("\t-IN_B: %x\n", inst.RB);
|
||||
if(Info->flags & FL_IN_C)
|
||||
printf("\t-IN_C: %x\n", inst.RC);
|
||||
if(Info->flags & FL_IN_S)
|
||||
printf("\t-IN_S: %x\n", inst.RS);
|
||||
}
|
||||
}
|
||||
|
||||
void STACKALIGN JitArm::Jit(u32 em_address)
|
||||
{
|
||||
if (GetSpaceLeft() < 0x10000 || blocks.IsFull() || Core::g_CoreStartupParameter.bJITNoBlockCache)
|
||||
{
|
||||
ClearCache();
|
||||
}
|
||||
|
||||
int block_num = blocks.AllocateBlock(em_address);
|
||||
JitBlock *b = blocks.GetBlock(block_num);
|
||||
const u8* BlockPtr = DoJit(em_address, &code_buffer, b);
|
||||
blocks.FinalizeBlock(block_num, jo.enableBlocklink, BlockPtr);
|
||||
}
|
||||
void JitArm::Break(UGeckoInstruction inst)
|
||||
{
|
||||
BKPT(0x4444);
|
||||
}
|
||||
|
||||
const u8* JitArm::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBlock *b)
|
||||
{
|
||||
int blockSize = code_buf->GetSize();
|
||||
|
||||
// Memory exception on instruction fetch
|
||||
bool memory_exception = false;
|
||||
|
||||
// A broken block is a block that does not end in a branch
|
||||
bool broken_block = false;
|
||||
|
||||
if (Core::g_CoreStartupParameter.bEnableDebugging)
|
||||
{
|
||||
// Comment out the following to disable breakpoints (speed-up)
|
||||
blockSize = 1;
|
||||
broken_block = true;
|
||||
Trace();
|
||||
}
|
||||
|
||||
if (em_address == 0)
|
||||
{
|
||||
Core::SetState(Core::CORE_PAUSE);
|
||||
PanicAlert("ERROR: Compiling at 0. LR=%08x CTR=%08x", LR, CTR);
|
||||
}
|
||||
|
||||
if (Core::g_CoreStartupParameter.bMMU && (em_address & JIT_ICACHE_VMEM_BIT))
|
||||
{
|
||||
if (!Memory::TranslateAddress(em_address, Memory::FLAG_OPCODE))
|
||||
{
|
||||
// Memory exception occurred during instruction fetch
|
||||
memory_exception = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int size = 0;
|
||||
js.isLastInstruction = false;
|
||||
js.blockStart = em_address;
|
||||
js.fifoBytesThisBlock = 0;
|
||||
js.curBlock = b;
|
||||
js.block_flags = 0;
|
||||
js.cancel = false;
|
||||
|
||||
// Analyze the block, collect all instructions it is made of (including inlining,
|
||||
// if that is enabled), reorder instructions for optimal performance, and join joinable instructions.
|
||||
u32 nextPC = em_address;
|
||||
u32 merged_addresses[32];
|
||||
const int capacity_of_merged_addresses = sizeof(merged_addresses) / sizeof(merged_addresses[0]);
|
||||
int size_of_merged_addresses = 0;
|
||||
if (!memory_exception)
|
||||
{
|
||||
// If there is a memory exception inside a block (broken_block==true), compile up to that instruction.
|
||||
nextPC = PPCAnalyst::Flatten(em_address, &size, &js.st, &js.gpa, &js.fpa, broken_block, code_buf, blockSize, merged_addresses, capacity_of_merged_addresses, size_of_merged_addresses);
|
||||
}
|
||||
PPCAnalyst::CodeOp *ops = code_buf->codebuffer;
|
||||
|
||||
const u8 *start = GetCodePtr();
|
||||
b->checkedEntry = start;
|
||||
b->runCount = 0;
|
||||
|
||||
// Downcount flag check, Only valid for linked blocks
|
||||
{
|
||||
FixupBranch skip = B_CC(CC_PL);
|
||||
ARMABI_MOVI2M((u32)&PC, js.blockStart);
|
||||
ARMReg rA = gpr.GetReg(false);
|
||||
MOVI2R(rA, (u32)asm_routines.doTiming);
|
||||
B(rA);
|
||||
SetJumpTarget(skip);
|
||||
}
|
||||
|
||||
const u8 *normalEntry = GetCodePtr();
|
||||
b->normalEntry = normalEntry;
|
||||
|
||||
if(ImHereDebug)
|
||||
QuickCallFunction(R14, (void *)&ImHere); //Used to get a trace of the last few blocks before a crash, sometimes VERY useful
|
||||
|
||||
if (js.fpa.any)
|
||||
{
|
||||
// This block uses FPU - needs to add FP exception bailout
|
||||
ARMReg A = gpr.GetReg();
|
||||
ARMReg C = gpr.GetReg();
|
||||
Operand2 Shift(2, 10); // 1 << 13
|
||||
MOVI2R(C, js.blockStart); // R3
|
||||
LDR(A, R9, STRUCT_OFF(PowerPC::ppcState, msr));
|
||||
TST(A, Shift);
|
||||
FixupBranch b1 = B_CC(CC_NEQ);
|
||||
STR(R9, C, STRUCT_OFF(PowerPC::ppcState, pc));
|
||||
MOVI2R(A, (u32)asm_routines.fpException);
|
||||
B(A);
|
||||
SetJumpTarget(b1);
|
||||
gpr.Unlock(A, C);
|
||||
}
|
||||
// Conditionally add profiling code.
|
||||
if (Profiler::g_ProfileBlocks) {
|
||||
ARMReg rA = gpr.GetReg();
|
||||
ARMReg rB = gpr.GetReg();
|
||||
MOVI2R(rA, (u32)&b->runCount); // Load in to register
|
||||
LDR(rB, rA); // Load the actual value in to R11.
|
||||
ADD(rB, rB, 1); // Add one to the value
|
||||
STR(rA, rB); // Now store it back in the memory location
|
||||
// get start tic
|
||||
PROFILER_QUERY_PERFORMANCE_COUNTER(&b->ticStart);
|
||||
gpr.Unlock(rA, rB);
|
||||
}
|
||||
gpr.Start(js.gpa);
|
||||
fpr.Start(js.fpa);
|
||||
js.downcountAmount = 0;
|
||||
if (!Core::g_CoreStartupParameter.bEnableDebugging)
|
||||
{
|
||||
for (int i = 0; i < size_of_merged_addresses; ++i)
|
||||
{
|
||||
const u32 address = merged_addresses[i];
|
||||
js.downcountAmount += PatchEngine::GetSpeedhackCycles(address);
|
||||
}
|
||||
}
|
||||
|
||||
js.skipnext = false;
|
||||
js.blockSize = size;
|
||||
js.compilerPC = nextPC;
|
||||
// Translate instructions
|
||||
for (int i = 0; i < (int)size; i++)
|
||||
{
|
||||
js.compilerPC = ops[i].address;
|
||||
js.op = &ops[i];
|
||||
js.instructionNumber = i;
|
||||
const GekkoOPInfo *opinfo = ops[i].opinfo;
|
||||
js.downcountAmount += (opinfo->numCyclesMinusOne + 1);
|
||||
|
||||
if (i == (int)size - 1)
|
||||
{
|
||||
// WARNING - cmp->branch merging will screw this up.
|
||||
js.isLastInstruction = true;
|
||||
js.next_inst = 0;
|
||||
if (Profiler::g_ProfileBlocks) {
|
||||
// CAUTION!!! push on stack regs you use, do your stuff, then pop
|
||||
PROFILER_VPUSH;
|
||||
// get end tic
|
||||
PROFILER_QUERY_PERFORMANCE_COUNTER(&b->ticStop);
|
||||
// tic counter += (end tic - start tic)
|
||||
PROFILER_ADD_DIFF_LARGE_INTEGER(&b->ticCounter, &b->ticStop, &b->ticStart);
|
||||
PROFILER_VPOP;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// help peephole optimizations
|
||||
js.next_inst = ops[i + 1].inst;
|
||||
js.next_compilerPC = ops[i + 1].address;
|
||||
}
|
||||
if (jo.optimizeGatherPipe && js.fifoBytesThisBlock >= 32)
|
||||
{
|
||||
js.fifoBytesThisBlock -= 32;
|
||||
// TODO: This needs thunkmanager for ARM
|
||||
//ARMABI_CallFunction(thunks.ProtectFunction((void *)&GPFifo::CheckGatherPipe, 0));
|
||||
}
|
||||
if (Core::g_CoreStartupParameter.bEnableDebugging)
|
||||
{
|
||||
// Add run count
|
||||
ARMReg RA = gpr.GetReg();
|
||||
ARMReg RB = gpr.GetReg();
|
||||
MOVI2R(RA, (u32)&opinfo->runCount);
|
||||
LDR(RB, RA);
|
||||
ADD(RB, RB, 1);
|
||||
STR(RA, RB);
|
||||
gpr.Unlock(RA, RB);
|
||||
}
|
||||
if (!ops[i].skip)
|
||||
{
|
||||
PrintDebug(ops[i].inst, 0);
|
||||
if (js.memcheck && (opinfo->flags & FL_USE_FPU))
|
||||
{
|
||||
// Don't do this yet
|
||||
BKPT(0x7777);
|
||||
}
|
||||
JitArmTables::CompileInstruction(ops[i]);
|
||||
if (js.memcheck && (opinfo->flags & FL_LOADSTORE))
|
||||
{
|
||||
// Don't do this yet
|
||||
BKPT(0x666);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (memory_exception)
|
||||
BKPT(0x500);
|
||||
if (broken_block)
|
||||
{
|
||||
printf("Broken Block going to 0x%08x\n", nextPC);
|
||||
WriteExit(nextPC, 0);
|
||||
}
|
||||
|
||||
b->flags = js.block_flags;
|
||||
b->codeSize = (u32)(GetCodePtr() - normalEntry);
|
||||
b->originalSize = size;
|
||||
FlushIcache();
|
||||
return start;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
// ========================
|
||||
// See comments in Jit.cpp.
|
||||
// ========================
|
||||
|
||||
// Mystery: Capcom vs SNK 800aa278
|
||||
|
||||
// CR flags approach:
|
||||
// * Store that "N+Z flag contains CR0" or "S+Z flag contains CR3".
|
||||
// * All flag altering instructions flush this
|
||||
// * A flush simply does a conditional write to the appropriate CRx.
|
||||
// * If flag available, branch code can become absolutely trivial.
|
||||
|
||||
// Settings
|
||||
// ----------
|
||||
#ifndef _JITARM_H
|
||||
#define _JITARM_H
|
||||
#include "../CPUCoreBase.h"
|
||||
#include "../PPCAnalyst.h"
|
||||
#include "JitArmCache.h"
|
||||
#include "JitRegCache.h"
|
||||
#include "JitFPRCache.h"
|
||||
#include "JitAsm.h"
|
||||
#include "../JitCommon/JitBase.h"
|
||||
|
||||
// Use these to control the instruction selection
|
||||
// #define INSTRUCTION_START Default(inst); return;
|
||||
// #define INSTRUCTION_START PPCTables::CountInstruction(inst);
|
||||
#define INSTRUCTION_START
|
||||
#define JITDISABLE(type) \
|
||||
if (Core::g_CoreStartupParameter.bJITOff || \
|
||||
Core::g_CoreStartupParameter.bJIT##type##Off) \
|
||||
{Default(inst); return;}
|
||||
|
||||
class JitArm : public JitBase, public ArmGen::ARMXCodeBlock
|
||||
{
|
||||
private:
|
||||
JitArmBlockCache blocks;
|
||||
|
||||
JitArmAsmRoutineManager asm_routines;
|
||||
|
||||
// TODO: Make arm specific versions of these, shouldn't be too hard to
|
||||
// make it so we allocate some space at the start(?) of code generation
|
||||
// and keep the registers in a cache. Will burn this bridge when we get to
|
||||
// it.
|
||||
ArmRegCache gpr;
|
||||
ArmFPRCache fpr;
|
||||
|
||||
PPCAnalyst::CodeBuffer code_buffer;
|
||||
void DoDownCount();
|
||||
|
||||
void PrintDebug(UGeckoInstruction inst, u32 level);
|
||||
|
||||
void Helper_UpdateCR1(ARMReg value);
|
||||
public:
|
||||
JitArm() : code_buffer(32000) {}
|
||||
~JitArm() {}
|
||||
|
||||
void Init();
|
||||
void Shutdown();
|
||||
|
||||
// Jit!
|
||||
|
||||
void Jit(u32 em_address);
|
||||
const u8* DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBlock *b);
|
||||
|
||||
JitBaseBlockCache *GetBlockCache() { return &blocks; }
|
||||
|
||||
const u8 *BackPatch(u8 *codePtr, int accessType, u32 em_address, void *ctx);
|
||||
|
||||
bool IsInCodeSpace(u8 *ptr) { return IsInSpace(ptr); }
|
||||
|
||||
void Trace();
|
||||
|
||||
void ClearCache();
|
||||
|
||||
const u8 *GetDispatcher() {
|
||||
return asm_routines.dispatcher;
|
||||
}
|
||||
CommonAsmRoutinesBase *GetAsmRoutines() {
|
||||
return &asm_routines;
|
||||
}
|
||||
|
||||
const char *GetName() {
|
||||
return "JITARM";
|
||||
}
|
||||
// Run!
|
||||
|
||||
void Run();
|
||||
void SingleStep();
|
||||
|
||||
// Utilities for use by opcodes
|
||||
|
||||
void WriteExit(u32 destination, int exit_num);
|
||||
void WriteExitDestInR(ARMReg Reg);
|
||||
void WriteRfiExitDestInR(ARMReg Reg);
|
||||
void WriteExceptionExit();
|
||||
void WriteCallInterpreter(UGeckoInstruction _inst);
|
||||
void Cleanup();
|
||||
|
||||
void GenerateRC(int cr = 0);
|
||||
void ComputeRC(int cr = 0);
|
||||
|
||||
// TODO: This shouldn't be here
|
||||
void StoreFromReg(ARMReg dest, ARMReg value, int accessSize, s32 offset);
|
||||
void LoadToReg(ARMReg dest, ARMReg addr, int accessSize, s32 offset);
|
||||
|
||||
// OPCODES
|
||||
void unknown_instruction(UGeckoInstruction _inst);
|
||||
void Default(UGeckoInstruction _inst);
|
||||
void DoNothing(UGeckoInstruction _inst);
|
||||
void HLEFunction(UGeckoInstruction _inst);
|
||||
|
||||
void DynaRunTable4(UGeckoInstruction _inst);
|
||||
void DynaRunTable19(UGeckoInstruction _inst);
|
||||
void DynaRunTable31(UGeckoInstruction _inst);
|
||||
void DynaRunTable59(UGeckoInstruction _inst);
|
||||
void DynaRunTable63(UGeckoInstruction _inst);
|
||||
|
||||
// Breakin shit
|
||||
void Break(UGeckoInstruction _inst);
|
||||
// Branch
|
||||
void bx(UGeckoInstruction _inst);
|
||||
void bcx(UGeckoInstruction _inst);
|
||||
void bclrx(UGeckoInstruction _inst);
|
||||
void sc(UGeckoInstruction _inst);
|
||||
void rfi(UGeckoInstruction _inst);
|
||||
void bcctrx(UGeckoInstruction _inst);
|
||||
|
||||
// Integer
|
||||
void addi(UGeckoInstruction _inst);
|
||||
void addis(UGeckoInstruction _inst);
|
||||
void addx(UGeckoInstruction _inst);
|
||||
void cmp (UGeckoInstruction _inst);
|
||||
void cmpi(UGeckoInstruction _inst);
|
||||
void cmpli(UGeckoInstruction _inst);
|
||||
void negx(UGeckoInstruction _inst);
|
||||
void mulli(UGeckoInstruction _inst);
|
||||
void ori(UGeckoInstruction _inst);
|
||||
void oris(UGeckoInstruction _inst);
|
||||
void orx(UGeckoInstruction _inst);
|
||||
void rlwimix(UGeckoInstruction _inst);
|
||||
void rlwinmx(UGeckoInstruction _inst);
|
||||
void extshx(UGeckoInstruction inst);
|
||||
void extsbx(UGeckoInstruction inst);
|
||||
|
||||
// System Registers
|
||||
void mtmsr(UGeckoInstruction _inst);
|
||||
void mtspr(UGeckoInstruction _inst);
|
||||
void mfspr(UGeckoInstruction _inst);
|
||||
|
||||
// LoadStore
|
||||
void icbi(UGeckoInstruction _inst);
|
||||
void lbz(UGeckoInstruction _inst);
|
||||
void lhz(UGeckoInstruction _inst);
|
||||
void lwz(UGeckoInstruction _inst);
|
||||
void lwzx(UGeckoInstruction _inst);
|
||||
void stw(UGeckoInstruction _inst);
|
||||
void stwu(UGeckoInstruction _inst);
|
||||
|
||||
// Floating point
|
||||
void fabsx(UGeckoInstruction _inst);
|
||||
void faddx(UGeckoInstruction _inst);
|
||||
void fmrx(UGeckoInstruction _inst);
|
||||
|
||||
// Floating point loadStore
|
||||
void lfs(UGeckoInstruction _inst);
|
||||
};
|
||||
|
||||
#endif // _JIT64_H
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
// Enable define below to enable oprofile integration. For this to work,
|
||||
// it requires at least oprofile version 0.9.4, and changing the build
|
||||
// system to link the Dolphin executable against libopagent. Since the
|
||||
// dependency is a little inconvenient and this is possibly a slight
|
||||
// performance hit, it's not enabled by default, but it's useful for
|
||||
// locating performance issues.
|
||||
|
||||
#include "../JitInterface.h"
|
||||
#include "JitArmCache.h"
|
||||
|
||||
|
||||
using namespace ArmGen;
|
||||
|
||||
void JitArmBlockCache::WriteLinkBlock(u8* location, const u8* address)
|
||||
{
|
||||
ARMXEmitter emit(location);
|
||||
emit.B(address);
|
||||
}
|
||||
void JitArmBlockCache::WriteDestroyBlock(const u8* location, u32 address)
|
||||
{
|
||||
ARMXEmitter emit((u8 *)location);
|
||||
emit.MOVI2R(R10, (u32)&PC);
|
||||
emit.MOVI2R(R11, address);
|
||||
emit.MOVI2R(R12, (u32)jit->GetAsmRoutines()->dispatcher);
|
||||
emit.STR(R10, R11);
|
||||
emit.B(R12);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _JITARMCACHE_H
|
||||
#define _JITARMCACHE_H
|
||||
|
||||
#include "../JitCommon/JitCache.h"
|
||||
|
||||
|
||||
typedef void (*CompiledCode)();
|
||||
|
||||
class JitArmBlockCache : public JitBaseBlockCache
|
||||
{
|
||||
private:
|
||||
void WriteLinkBlock(u8* location, const u8* address);
|
||||
void WriteDestroyBlock(const u8* location, u32 address);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,164 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
#include "../../HW/Memmap.h"
|
||||
#include "Jit.h"
|
||||
#include "../JitCommon/JitBackpatch.h"
|
||||
#include "StringUtil.h"
|
||||
|
||||
#ifdef _M_X64
|
||||
static void BackPatchError(const std::string &text, u8 *codePtr, u32 emAddress) {
|
||||
u64 code_addr = (u64)codePtr;
|
||||
disassembler disasm;
|
||||
char disbuf[256];
|
||||
memset(disbuf, 0, 256);
|
||||
#ifdef _M_IX86
|
||||
disasm.disasm32(0, code_addr, codePtr, disbuf);
|
||||
#else
|
||||
disasm.disasm64(0, code_addr, codePtr, disbuf);
|
||||
#endif
|
||||
PanicAlert("%s\n\n"
|
||||
"Error encountered accessing emulated address %08x.\n"
|
||||
"Culprit instruction: \n%s\nat %#llx",
|
||||
text.c_str(), emAddress, disbuf, code_addr);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// This generates some fairly heavy trampolines, but:
|
||||
// 1) It's really necessary. We don't know anything about the context.
|
||||
// 2) It doesn't really hurt. Only instructions that access I/O will get these, and there won't be
|
||||
// that many of them in a typical program/game.
|
||||
bool DisamLoadStore(const u32 inst, ARMReg &rD, u8 &accessSize, bool &Store)
|
||||
{
|
||||
u8 op = (inst >> 20) & 0xFF;
|
||||
printf("op: 0x%08x\n", op);
|
||||
switch (op)
|
||||
{
|
||||
case 0x58: // STR
|
||||
{
|
||||
rD = (ARMReg)((inst >> 16) & 0xF);
|
||||
Store = true;
|
||||
accessSize = 32;
|
||||
}
|
||||
break;
|
||||
case 0x59: // LDR
|
||||
{
|
||||
rD = (ARMReg)((inst >> 16) & 0xF);
|
||||
Store = false;
|
||||
accessSize = 32;
|
||||
}
|
||||
break;
|
||||
case 0x05: // LDRH
|
||||
{
|
||||
rD = (ARMReg)((inst >> 16) & 0xF);
|
||||
Store = false;
|
||||
accessSize = 16;
|
||||
}
|
||||
break;
|
||||
case 0x45 + 0x18: // LDRB
|
||||
{
|
||||
rD = (ARMReg)((inst >> 16) & 0xF);
|
||||
Store = false;
|
||||
accessSize = 8;
|
||||
}
|
||||
break;
|
||||
case 0x44 + 0x18: // STRB
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
const u8 *JitArm::BackPatch(u8 *codePtr, int accessType, u32 emAddress, void *ctx_void)
|
||||
{
|
||||
// TODO: This ctx needs to be filled with our information
|
||||
CONTEXT *ctx = (CONTEXT *)ctx_void;
|
||||
|
||||
// We need to get the destination register before we start
|
||||
u32 Value = *(u32*)codePtr;
|
||||
ARMReg rD;
|
||||
u8 accessSize;
|
||||
bool Store;
|
||||
|
||||
if (!DisamLoadStore(Value, rD, accessSize, Store))
|
||||
{
|
||||
printf("Invalid backpatch at location 0x%08x(0x%08x)\n", ctx->reg_pc, Value);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (Store)
|
||||
{
|
||||
const u32 ARMREGOFFSET = 4 * 7;
|
||||
ARMXEmitter emitter(codePtr - ARMREGOFFSET);
|
||||
switch (accessSize)
|
||||
{
|
||||
case 8: // 8bit
|
||||
//emitter.MOVI2R(R14, (u32)&Memory::Write_U8, false); // 1-2
|
||||
return 0;
|
||||
break;
|
||||
case 16: // 16bit
|
||||
//emitter.MOVI2R(R14, (u32)&Memory::Write_U16, false); // 1-2
|
||||
return 0;
|
||||
break;
|
||||
case 32: // 32bit
|
||||
emitter.MOVI2R(R14, (u32)&Memory::Write_U32, false); // 1-2
|
||||
break;
|
||||
}
|
||||
emitter.PUSH(4, R0, R1, R2, R3); // 3
|
||||
emitter.MOV(R0, rD); // Value - 4
|
||||
emitter.MOV(R1, R10); // Addr- 5
|
||||
emitter.BL(R14); // 6
|
||||
emitter.POP(4, R0, R1, R2, R3); // 7
|
||||
emitter.NOP(1); // 8
|
||||
u32 newPC = ctx->reg_pc - (ARMREGOFFSET + 4 * 4);
|
||||
ctx->reg_pc = newPC;
|
||||
emitter.FlushIcache();
|
||||
return codePtr;
|
||||
}
|
||||
else
|
||||
{
|
||||
const u32 ARMREGOFFSET = 4 * 6;
|
||||
ARMXEmitter emitter(codePtr - ARMREGOFFSET);
|
||||
switch (accessSize)
|
||||
{
|
||||
case 8: // 8bit
|
||||
emitter.MOVI2R(R14, (u32)&Memory::Read_U8, false); // 2
|
||||
break;
|
||||
case 16: // 16bit
|
||||
emitter.MOVI2R(R14, (u32)&Memory::Read_U16, false); // 2
|
||||
break;
|
||||
case 32: // 32bit
|
||||
emitter.MOVI2R(R14, (u32)&Memory::Read_U32, false); // 2
|
||||
break;
|
||||
}
|
||||
emitter.PUSH(4, R0, R1, R2, R3); // 3
|
||||
emitter.MOV(R0, R10); // 4
|
||||
emitter.BL(R14); // 5
|
||||
emitter.MOV(R14, R0); // 6
|
||||
emitter.POP(4, R0, R1, R2, R3); // 7
|
||||
emitter.MOV(rD, R14); // 8
|
||||
ctx->reg_pc -= ARMREGOFFSET + (4 * 4);
|
||||
emitter.FlushIcache();
|
||||
return codePtr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,347 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
#include "Common.h"
|
||||
#include "Thunk.h"
|
||||
|
||||
#include "../../Core.h"
|
||||
#include "../PowerPC.h"
|
||||
#include "../../CoreTiming.h"
|
||||
#include "../PPCTables.h"
|
||||
#include "ArmEmitter.h"
|
||||
|
||||
#include "Jit.h"
|
||||
#include "JitRegCache.h"
|
||||
#include "JitAsm.h"
|
||||
|
||||
// The branches are known good, or at least reasonably good.
|
||||
// No need for a disable-mechanism.
|
||||
|
||||
// If defined, clears CR0 at blr and bl-s. If the assumption that
|
||||
// flags never carry over between functions holds, then the task for
|
||||
// an optimizer becomes much easier.
|
||||
|
||||
// #define ACID_TEST
|
||||
|
||||
// Zelda and many more games seem to pass the Acid Test.
|
||||
|
||||
|
||||
using namespace ArmGen;
|
||||
void JitArm::sc(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Branch)
|
||||
|
||||
gpr.Flush();
|
||||
fpr.Flush();
|
||||
|
||||
ARMABI_MOVI2M((u32)&PC, js.compilerPC + 4); // Destroys R12 and R14
|
||||
ARMReg rA = gpr.GetReg();
|
||||
LDR(rA, R9, STRUCT_OFF(PowerPC::ppcState, Exceptions));
|
||||
ORR(rA, rA, EXCEPTION_SYSCALL);
|
||||
STR(R9, rA, STRUCT_OFF(PowerPC::ppcState, Exceptions));
|
||||
gpr.Unlock(rA);
|
||||
|
||||
WriteExceptionExit();
|
||||
}
|
||||
|
||||
void JitArm::rfi(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Branch)
|
||||
|
||||
gpr.Flush();
|
||||
fpr.Flush();
|
||||
|
||||
// See Interpreter rfi for details
|
||||
const u32 mask = 0x87C0FFFF;
|
||||
const u32 clearMSR13 = 0xFFFBFFFF; // Mask used to clear the bit MSR[13]
|
||||
// MSR = ((MSR & ~mask) | (SRR1 & mask)) & clearMSR13;
|
||||
// R0 = MSR location
|
||||
// R1 = MSR contents
|
||||
// R2 = Mask
|
||||
// R3 = Mask
|
||||
ARMReg rA = gpr.GetReg();
|
||||
ARMReg rB = gpr.GetReg();
|
||||
ARMReg rC = gpr.GetReg();
|
||||
ARMReg rD = gpr.GetReg();
|
||||
MOVI2R(rA, (u32)&MSR);
|
||||
MOVI2R(rB, (~mask) & clearMSR13);
|
||||
MOVI2R(rC, mask & clearMSR13);
|
||||
|
||||
LDR(rD, rA);
|
||||
|
||||
AND(rD, rD, rB); // rD = Masked MSR
|
||||
STR(rA, rD);
|
||||
|
||||
MOVI2R(rB, (u32)&SRR1);
|
||||
LDR(rB, rB); // rB contains SRR1 here
|
||||
|
||||
AND(rB, rB, rC); // rB contains masked SRR1 here
|
||||
ORR(rB, rD, rB); // rB = Masked MSR OR masked SRR1
|
||||
|
||||
STR(rA, rB); // STR rB in to rA
|
||||
|
||||
MOVI2R(rA, (u32)&SRR0);
|
||||
LDR(rA, rA);
|
||||
|
||||
gpr.Unlock(rB, rC, rD);
|
||||
WriteRfiExitDestInR(rA); // rA gets unlocked here
|
||||
//AND(32, M(&MSR), Imm32((~mask) & clearMSR13));
|
||||
//MOV(32, R(EAX), M(&SRR1));
|
||||
//AND(32, R(EAX), Imm32(mask & clearMSR13));
|
||||
//OR(32, M(&MSR), R(EAX));
|
||||
// NPC = SRR0;
|
||||
//MOV(32, R(EAX), M(&SRR0));
|
||||
//WriteRfiExitDestInEAX();
|
||||
}
|
||||
|
||||
void JitArm::bx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Branch)
|
||||
// We must always process the following sentence
|
||||
// even if the blocks are merged by PPCAnalyst::Flatten().
|
||||
if (inst.LK)
|
||||
ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4);
|
||||
|
||||
// If this is not the last instruction of a block,
|
||||
// we will skip the rest process.
|
||||
// Because PPCAnalyst::Flatten() merged the blocks.
|
||||
if (!js.isLastInstruction) {
|
||||
return;
|
||||
}
|
||||
|
||||
gpr.Flush();
|
||||
fpr.Flush();
|
||||
|
||||
u32 destination;
|
||||
if (inst.AA)
|
||||
destination = SignExt26(inst.LI << 2);
|
||||
else
|
||||
destination = js.compilerPC + SignExt26(inst.LI << 2);
|
||||
#ifdef ACID_TEST
|
||||
// TODO: Not implemented yet.
|
||||
//if (inst.LK)
|
||||
//AND(32, M(&PowerPC::ppcState.cr), Imm32(~(0xFF000000)));
|
||||
#endif
|
||||
if (destination == js.compilerPC)
|
||||
{
|
||||
//PanicAlert("Idle loop detected at %08x", destination);
|
||||
// CALL(ProtectFunction(&CoreTiming::Idle, 0));
|
||||
// JMP(Asm::testExceptions, true);
|
||||
// make idle loops go faster
|
||||
js.downcountAmount += 8;
|
||||
}
|
||||
WriteExit(destination, 0);
|
||||
}
|
||||
|
||||
void JitArm::bcx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Branch)
|
||||
// USES_CR
|
||||
_assert_msg_(DYNA_REC, js.isLastInstruction, "bcx not last instruction of block");
|
||||
|
||||
gpr.Flush();
|
||||
fpr.Flush();
|
||||
|
||||
ARMReg rA = gpr.GetReg();
|
||||
ARMReg rB = gpr.GetReg();
|
||||
FixupBranch pCTRDontBranch;
|
||||
if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0) // Decrement and test CTR
|
||||
{
|
||||
MOVI2R(rA, (u32)&CTR);
|
||||
LDR(rB, rA);
|
||||
SUBS(rB, rB, 1);
|
||||
STR(rA, rB);
|
||||
|
||||
//SUB(32, M(&CTR), Imm8(1));
|
||||
if (inst.BO & BO_BRANCH_IF_CTR_0)
|
||||
pCTRDontBranch = B_CC(CC_NEQ);
|
||||
else
|
||||
pCTRDontBranch = B_CC(CC_EQ);
|
||||
}
|
||||
|
||||
FixupBranch pConditionDontBranch;
|
||||
if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0) // Test a CR bit
|
||||
{
|
||||
LDRB(rA, R9, STRUCT_OFF(PowerPC::ppcState, cr_fast) + (inst.BI >> 2));
|
||||
TST(rA, 8 >> (inst.BI & 3));
|
||||
|
||||
//TEST(8, M(&PowerPC::ppcState.cr_fast[inst.BI >> 2]), Imm8(8 >> (inst.BI & 3)));
|
||||
if (inst.BO & BO_BRANCH_IF_TRUE) // Conditional branch
|
||||
pConditionDontBranch = B_CC(CC_EQ); // Zero
|
||||
else
|
||||
pConditionDontBranch = B_CC(CC_NEQ); // Not Zero
|
||||
}
|
||||
gpr.Unlock(rA, rB);
|
||||
if (inst.LK)
|
||||
ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4); // Careful, destroys R14, R12
|
||||
|
||||
u32 destination;
|
||||
if(inst.AA)
|
||||
destination = SignExt16(inst.BD << 2);
|
||||
else
|
||||
destination = js.compilerPC + SignExt16(inst.BD << 2);
|
||||
WriteExit(destination, 0);
|
||||
|
||||
if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0)
|
||||
SetJumpTarget( pConditionDontBranch );
|
||||
if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0)
|
||||
SetJumpTarget( pCTRDontBranch );
|
||||
|
||||
WriteExit(js.compilerPC + 4, 1);
|
||||
}
|
||||
void JitArm::bcctrx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Branch)
|
||||
|
||||
gpr.Flush();
|
||||
fpr.Flush();
|
||||
|
||||
// bcctrx doesn't decrement and/or test CTR
|
||||
_dbg_assert_msg_(POWERPC, inst.BO_2 & BO_DONT_DECREMENT_FLAG, "bcctrx with decrement and test CTR option is invalid!");
|
||||
|
||||
if (inst.BO_2 & BO_DONT_CHECK_CONDITION)
|
||||
{
|
||||
// BO_2 == 1z1zz -> b always
|
||||
|
||||
//NPC = CTR & 0xfffffffc;
|
||||
if(inst.LK_3)
|
||||
ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4);
|
||||
ARMReg rA = gpr.GetReg();
|
||||
ARMReg rB = gpr.GetReg();
|
||||
MOVI2R(rA, (u32)&CTR);
|
||||
MVN(rB, 0x3); // 0xFFFFFFFC
|
||||
LDR(rA, rA);
|
||||
AND(rA, rA, rB);
|
||||
gpr.Unlock(rB);
|
||||
WriteExitDestInR(rA);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Rare condition seen in (just some versions of?) Nintendo's NES Emulator
|
||||
|
||||
// BO_2 == 001zy -> b if false
|
||||
// BO_2 == 011zy -> b if true
|
||||
ARMReg rA = gpr.GetReg();
|
||||
ARMReg rB = gpr.GetReg();
|
||||
|
||||
LDRB(rA, R9, STRUCT_OFF(PowerPC::ppcState, cr_fast) + (inst.BI >> 2));
|
||||
TST(rA, 8 >> (inst.BI & 3));
|
||||
CCFlags branch;
|
||||
if (inst.BO_2 & BO_BRANCH_IF_TRUE)
|
||||
branch = CC_EQ;
|
||||
else
|
||||
branch = CC_NEQ;
|
||||
FixupBranch b = B_CC(branch);
|
||||
|
||||
MOVI2R(rA, (u32)&CTR);
|
||||
LDR(rA, rA);
|
||||
MVN(rB, 0x3); // 0xFFFFFFFC
|
||||
AND(rA, rA, rB);
|
||||
|
||||
if (inst.LK_3){
|
||||
ARMReg rC = gpr.GetReg(false);
|
||||
u32 Jumpto = js.compilerPC + 4;
|
||||
MOVI2R(rB, (u32)&LR);
|
||||
MOVI2R(rC, Jumpto);
|
||||
STR(rB, rC);
|
||||
//ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4);
|
||||
}
|
||||
gpr.Unlock(rB); // rA gets unlocked in WriteExitDestInR
|
||||
WriteExitDestInR(rA);
|
||||
|
||||
SetJumpTarget(b);
|
||||
WriteExit(js.compilerPC + 4, 1);
|
||||
}
|
||||
}
|
||||
void JitArm::bclrx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Branch)
|
||||
if (!js.isLastInstruction &&
|
||||
(inst.BO & (1 << 4)) && (inst.BO & (1 << 2))) {
|
||||
if (inst.LK)
|
||||
{
|
||||
ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4);
|
||||
}
|
||||
return;
|
||||
}
|
||||
gpr.Flush();
|
||||
fpr.Flush();
|
||||
|
||||
ARMReg rA = gpr.GetReg();
|
||||
ARMReg rB = gpr.GetReg();
|
||||
FixupBranch pCTRDontBranch;
|
||||
if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0) // Decrement and test CTR
|
||||
{
|
||||
MOVI2R(rA, (u32)&CTR);
|
||||
LDR(rB, rA);
|
||||
SUBS(rB, rB, 1);
|
||||
STR(rA, rB);
|
||||
|
||||
//SUB(32, M(&CTR), Imm8(1));
|
||||
if (inst.BO & BO_BRANCH_IF_CTR_0)
|
||||
pCTRDontBranch = B_CC(CC_NEQ);
|
||||
else
|
||||
pCTRDontBranch = B_CC(CC_EQ);
|
||||
}
|
||||
|
||||
FixupBranch pConditionDontBranch;
|
||||
if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0) // Test a CR bit
|
||||
{
|
||||
LDRB(rA, R9, STRUCT_OFF(PowerPC::ppcState, cr_fast) + (inst.BI >> 2));
|
||||
TST(rA, 8 >> (inst.BI & 3));
|
||||
//TEST(8, M(&PowerPC::ppcState.cr_fast[inst.BI >> 2]), Imm8(8 >> (inst.BI & 3)));
|
||||
if (inst.BO & BO_BRANCH_IF_TRUE) // Conditional branch
|
||||
pConditionDontBranch = B_CC(CC_EQ); // Zero
|
||||
else
|
||||
pConditionDontBranch = B_CC(CC_NEQ); // Not Zero
|
||||
}
|
||||
|
||||
// This below line can be used to prove that blr "eats flags" in practice.
|
||||
// This observation will let us do a lot of fun observations.
|
||||
#ifdef ACID_TEST
|
||||
// TODO: Not yet implemented
|
||||
// AND(32, M(&PowerPC::ppcState.cr), Imm32(~(0xFF000000)));
|
||||
#endif
|
||||
|
||||
//MOV(32, R(EAX), M(&LR));
|
||||
//AND(32, R(EAX), Imm32(0xFFFFFFFC));
|
||||
MOVI2R(rA, (u32)&LR);
|
||||
MVN(rB, 0x3); // 0xFFFFFFFC
|
||||
LDR(rA, rA);
|
||||
AND(rA, rA, rB);
|
||||
if (inst.LK){
|
||||
ARMReg rC = gpr.GetReg(false);
|
||||
u32 Jumpto = js.compilerPC + 4;
|
||||
MOVI2R(rB, (u32)&LR);
|
||||
MOVI2R(rC, Jumpto);
|
||||
STR(rB, rC);
|
||||
//ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4);
|
||||
}
|
||||
gpr.Unlock(rB); // rA gets unlocked in WriteExitDestInR
|
||||
WriteExitDestInR(rA);
|
||||
|
||||
if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0)
|
||||
SetJumpTarget( pConditionDontBranch );
|
||||
if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0)
|
||||
SetJumpTarget( pCTRDontBranch );
|
||||
WriteExit(js.compilerPC + 4, 1);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "Common.h"
|
||||
#include "Thunk.h"
|
||||
|
||||
#include "../../Core.h"
|
||||
#include "../PowerPC.h"
|
||||
#include "../../ConfigManager.h"
|
||||
#include "../../CoreTiming.h"
|
||||
#include "../PPCTables.h"
|
||||
#include "ArmEmitter.h"
|
||||
#include "../../HW/Memmap.h"
|
||||
|
||||
|
||||
#include "Jit.h"
|
||||
#include "JitRegCache.h"
|
||||
#include "JitFPRCache.h"
|
||||
#include "JitAsm.h"
|
||||
|
||||
void JitArm::Helper_UpdateCR1(ARMReg value)
|
||||
{
|
||||
// Should just update exception flags, not do any compares.
|
||||
PanicAlert("CR1");
|
||||
}
|
||||
|
||||
void JitArm::fabsx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(FloatingPoint)
|
||||
|
||||
ARMReg vD = fpr.R0(inst.FD);
|
||||
ARMReg vB = fpr.R0(inst.FB);
|
||||
|
||||
VABS(vD, vB);
|
||||
|
||||
if (inst.Rc) Helper_UpdateCR1(vD);
|
||||
}
|
||||
|
||||
void JitArm::faddx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(FloatingPoint)
|
||||
|
||||
ARMReg vD = fpr.R0(inst.FD);
|
||||
ARMReg vA = fpr.R0(inst.FA);
|
||||
ARMReg vB = fpr.R0(inst.FB);
|
||||
|
||||
VADD(vD, vA, vB);
|
||||
if (inst.Rc) Helper_UpdateCR1(vD);
|
||||
}
|
||||
|
||||
void JitArm::fmrx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(FloatingPoint)
|
||||
Default(inst); return;
|
||||
|
||||
ARMReg vD = fpr.R0(inst.FD);
|
||||
ARMReg vB = fpr.R0(inst.FB);
|
||||
|
||||
VMOV(vD, vB);
|
||||
|
||||
if (inst.Rc) Helper_UpdateCR1(vD);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,311 @@
|
||||
// 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);
|
||||
int crf = inst.CRFD;
|
||||
if (inst.SIMM_16 >= 0 && inst.SIMM_16 < 256)
|
||||
{
|
||||
CMP(RA, inst.SIMM_16);
|
||||
}
|
||||
else
|
||||
{
|
||||
ARMReg rA = gpr.GetReg();
|
||||
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;
|
||||
u32 uimm = (u32)inst.UIMM;
|
||||
if (uimm < 256)
|
||||
{
|
||||
CMP(RA, uimm);
|
||||
}
|
||||
else
|
||||
{
|
||||
MOVI2R(rA, (u32)inst.UIMM);
|
||||
CMP(RA, rA);
|
||||
}
|
||||
// Unsigned GenerateRC()
|
||||
|
||||
MOV(rA, 0x2); // Result == 0
|
||||
SetCC(CC_LO); MOV(rA, 0x8); // Result < 0
|
||||
SetCC(CC_HI); MOV(rA, 0x4); // Result > 0
|
||||
SetCC();
|
||||
|
||||
STRB(R9, rA, STRUCT_OFF(PowerPC::ppcState, cr_fast) + crf);
|
||||
gpr.Unlock(rA);
|
||||
|
||||
}
|
||||
// Wrong - 27/10/2012
|
||||
void JitArm::negx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
|
||||
Default(inst);return;
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ARMReg RD = gpr.R(inst.RD);
|
||||
|
||||
RSBS(RD, RA, 0);
|
||||
if (inst.Rc)
|
||||
{
|
||||
GenerateRC();
|
||||
}
|
||||
if (inst.OE)
|
||||
{
|
||||
BKPT(0x333);
|
||||
//GenerateOverflow();
|
||||
}
|
||||
}
|
||||
void JitArm::rlwimix(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
|
||||
u32 mask = Helper_Mask(inst.MB,inst.ME);
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ARMReg RS = gpr.R(inst.RS);
|
||||
ARMReg rA = gpr.GetReg();
|
||||
ARMReg rB = gpr.GetReg();
|
||||
MOVI2R(rA, mask);
|
||||
|
||||
Operand2 Shift(32 - inst.SH, ST_ROR, RS); // This rotates left, while ARM has only rotate right, so swap it.
|
||||
if (inst.Rc)
|
||||
{
|
||||
BIC (rB, RA, rA); // RA & ~mask
|
||||
AND (rA, rA, Shift);
|
||||
ORRS(RA, rB, rA);
|
||||
GenerateRC();
|
||||
}
|
||||
else
|
||||
{
|
||||
BIC (rB, RA, rA); // RA & ~mask
|
||||
AND (rA, rA, Shift);
|
||||
ORR(RA, rB, rA);
|
||||
}
|
||||
gpr.Unlock(rA, rB);
|
||||
}
|
||||
void JitArm::rlwinmx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
|
||||
u32 mask = Helper_Mask(inst.MB,inst.ME);
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ARMReg RS = gpr.R(inst.RS);
|
||||
ARMReg rA = gpr.GetReg();
|
||||
MOVI2R(rA, mask);
|
||||
|
||||
Operand2 Shift(32 - inst.SH, ST_ROR, RS); // This rotates left, while ARM has only rotate right, so swap it.
|
||||
if (inst.Rc)
|
||||
{
|
||||
ANDS(RA, rA, Shift);
|
||||
GenerateRC();
|
||||
}
|
||||
else
|
||||
AND (RA, rA, Shift);
|
||||
gpr.Unlock(rA);
|
||||
|
||||
//m_GPR[inst.RA] = _rotl(m_GPR[inst.RS],inst.SH) & mask;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,414 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "Common.h"
|
||||
#include "Thunk.h"
|
||||
|
||||
#include "../../Core.h"
|
||||
#include "../PowerPC.h"
|
||||
#include "../../ConfigManager.h"
|
||||
#include "../../CoreTiming.h"
|
||||
#include "../PPCTables.h"
|
||||
#include "ArmEmitter.h"
|
||||
#include "../../HW/Memmap.h"
|
||||
|
||||
|
||||
#include "Jit.h"
|
||||
#include "JitRegCache.h"
|
||||
#include "JitAsm.h"
|
||||
|
||||
#ifdef ANDROID
|
||||
#define FASTMEM 0
|
||||
#else
|
||||
#define FASTMEM 0
|
||||
#endif
|
||||
void JitArm::stw(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(LoadStore)
|
||||
|
||||
ARMReg RS = gpr.R(inst.RS);
|
||||
#if FASTMEM
|
||||
// R10 contains the dest address
|
||||
ARMReg Value = R11;
|
||||
ARMReg RA;
|
||||
if (inst.RA)
|
||||
RA = gpr.R(inst.RA);
|
||||
MOV(Value, RS);
|
||||
if (inst.RA)
|
||||
{
|
||||
MOVI2R(R10, inst.SIMM_16, false);
|
||||
ADD(R10, R10, RA);
|
||||
}
|
||||
else
|
||||
{
|
||||
MOVI2R(R10, (u32)inst.SIMM_16, false);
|
||||
NOP(1);
|
||||
}
|
||||
StoreFromReg(R10, Value, 32, 0);
|
||||
#else
|
||||
ARMReg ValueReg = gpr.GetReg();
|
||||
ARMReg Addr = gpr.GetReg();
|
||||
ARMReg Function = gpr.GetReg();
|
||||
|
||||
MOV(ValueReg, RS);
|
||||
if (inst.RA)
|
||||
{
|
||||
MOVI2R(Addr, inst.SIMM_16);
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ADD(Addr, Addr, RA);
|
||||
}
|
||||
else
|
||||
MOVI2R(Addr, (u32)inst.SIMM_16);
|
||||
|
||||
MOVI2R(Function, (u32)&Memory::Write_U32);
|
||||
PUSH(4, R0, R1, R2, R3);
|
||||
MOV(R0, ValueReg);
|
||||
MOV(R1, Addr);
|
||||
BL(Function);
|
||||
POP(4, R0, R1, R2, R3);
|
||||
gpr.Unlock(ValueReg, Addr, Function);
|
||||
#endif
|
||||
}
|
||||
void JitArm::stwu(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(LoadStore)
|
||||
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ARMReg RS = gpr.R(inst.RS);
|
||||
ARMReg ValueReg = gpr.GetReg();
|
||||
ARMReg Addr = gpr.GetReg();
|
||||
ARMReg Function = gpr.GetReg();
|
||||
|
||||
MOVI2R(Addr, inst.SIMM_16);
|
||||
ADD(Addr, Addr, RA);
|
||||
|
||||
// Check and set the update before writing since calling a function can
|
||||
// mess with the "special registers R11+ which may cause some issues.
|
||||
LDR(Function, R9, STRUCT_OFF(PowerPC::ppcState, Exceptions));
|
||||
CMP(Function, EXCEPTION_DSI);
|
||||
FixupBranch DoNotWrite = B_CC(CC_EQ);
|
||||
MOV(RA, Addr);
|
||||
SetJumpTarget(DoNotWrite);
|
||||
|
||||
MOV(ValueReg, RS);
|
||||
|
||||
MOVI2R(Function, (u32)&Memory::Write_U32);
|
||||
PUSH(4, R0, R1, R2, R3);
|
||||
MOV(R0, ValueReg);
|
||||
MOV(R1, Addr);
|
||||
BL(Function);
|
||||
POP(4, R0, R1, R2, R3);
|
||||
|
||||
gpr.Unlock(ValueReg, Addr, Function);
|
||||
}
|
||||
void JitArm::StoreFromReg(ARMReg dest, ARMReg value, int accessSize, s32 offset)
|
||||
{
|
||||
ARMReg rA = gpr.GetReg();
|
||||
|
||||
// All this gets replaced on backpatch
|
||||
MOVI2R(rA, Memory::MEMVIEW32_MASK, false); // 1-2
|
||||
AND(dest, dest, rA); // 3
|
||||
MOVI2R(rA, (u32)Memory::base, false); // 4-5
|
||||
ADD(dest, dest, rA); // 6
|
||||
switch (accessSize)
|
||||
{
|
||||
case 32:
|
||||
REV(value, value); // 7
|
||||
break;
|
||||
case 16:
|
||||
REV16(value, value);
|
||||
break;
|
||||
case 8:
|
||||
NOP(1);
|
||||
break;
|
||||
}
|
||||
switch (accessSize)
|
||||
{
|
||||
case 32:
|
||||
STR(dest, value); // 8
|
||||
break;
|
||||
case 16:
|
||||
// Not implemented
|
||||
break;
|
||||
case 8:
|
||||
// Not implemented
|
||||
break;
|
||||
}
|
||||
gpr.Unlock(rA);
|
||||
}
|
||||
void JitArm::LoadToReg(ARMReg dest, ARMReg addr, int accessSize, s32 offset)
|
||||
{
|
||||
ARMReg rA = gpr.GetReg();
|
||||
MOVI2R(rA, offset, false); // -3
|
||||
ADD(addr, addr, rA); // - 1
|
||||
|
||||
// All this gets replaced on backpatch
|
||||
MOVI2R(rA, Memory::MEMVIEW32_MASK, false); // 2
|
||||
AND(addr, addr, rA); // 3
|
||||
MOVI2R(rA, (u32)Memory::base, false); // 5
|
||||
ADD(addr, addr, rA); // 6
|
||||
switch (accessSize)
|
||||
{
|
||||
case 32:
|
||||
LDR(dest, addr); // 7
|
||||
break;
|
||||
case 16:
|
||||
LDRH(dest, addr);
|
||||
break;
|
||||
case 8:
|
||||
LDRB(dest, addr);
|
||||
break;
|
||||
}
|
||||
switch (accessSize)
|
||||
{
|
||||
case 32:
|
||||
REV(dest, dest); // 9
|
||||
break;
|
||||
case 16:
|
||||
REV16(dest, dest);
|
||||
break;
|
||||
case 8:
|
||||
NOP(1);
|
||||
break;
|
||||
|
||||
}
|
||||
gpr.Unlock(rA);
|
||||
}
|
||||
void JitArm::lbz(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(LoadStore)
|
||||
|
||||
ARMReg rA = gpr.GetReg();
|
||||
ARMReg rB = gpr.GetReg();
|
||||
ARMReg RD = gpr.R(inst.RD);
|
||||
LDR(rA, R9, STRUCT_OFF(PowerPC::ppcState, Exceptions));
|
||||
CMP(rA, EXCEPTION_DSI);
|
||||
FixupBranch DoNotLoad = B_CC(CC_EQ);
|
||||
#if FASTMEM
|
||||
// Backpatch route
|
||||
// Gets loaded in to RD
|
||||
// Address is in R10
|
||||
gpr.Unlock(rA, rB);
|
||||
if (inst.RA)
|
||||
{
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
MOV(R10, RA); // - 4
|
||||
}
|
||||
else
|
||||
MOV(R10, 0); // - 4
|
||||
LoadToReg(RD, R10, 8, inst.SIMM_16);
|
||||
#else
|
||||
|
||||
if (inst.RA)
|
||||
{
|
||||
MOVI2R(rB, inst.SIMM_16);
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ADD(rB, rB, RA);
|
||||
}
|
||||
else
|
||||
MOVI2R(rB, (u32)inst.SIMM_16);
|
||||
|
||||
MOVI2R(rA, (u32)&Memory::Read_U8);
|
||||
PUSH(4, R0, R1, R2, R3);
|
||||
MOV(R0, rB);
|
||||
BL(rA);
|
||||
MOV(rA, R0);
|
||||
POP(4, R0, R1, R2, R3);
|
||||
MOV(RD, rA);
|
||||
gpr.Unlock(rA, rB);
|
||||
#endif
|
||||
SetJumpTarget(DoNotLoad);
|
||||
}
|
||||
|
||||
void JitArm::lhz(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(LoadStore)
|
||||
|
||||
ARMReg rA = gpr.GetReg();
|
||||
ARMReg rB = gpr.GetReg();
|
||||
ARMReg RD = gpr.R(inst.RD);
|
||||
LDR(rA, R9, STRUCT_OFF(PowerPC::ppcState, Exceptions));
|
||||
CMP(rA, EXCEPTION_DSI);
|
||||
FixupBranch DoNotLoad = B_CC(CC_EQ);
|
||||
#if 0 // FASTMEM
|
||||
// Backpatch route
|
||||
// Gets loaded in to RD
|
||||
// Address is in R10
|
||||
gpr.Unlock(rA, rB);
|
||||
if (inst.RA)
|
||||
{
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
printf("lhz jump to here: 0x%08x\n", (u32)GetCodePtr());
|
||||
MOV(R10, RA); // - 4
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("lhz jump to here: 0x%08x\n", (u32)GetCodePtr());
|
||||
MOV(R10, 0); // - 4
|
||||
}
|
||||
LoadToReg(RD, R10, 16, (u32)inst.SIMM_16);
|
||||
#else
|
||||
|
||||
if (inst.RA)
|
||||
{
|
||||
MOVI2R(rB, inst.SIMM_16);
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ADD(rB, rB, RA);
|
||||
}
|
||||
else
|
||||
MOVI2R(rB, (u32)inst.SIMM_16);
|
||||
|
||||
MOVI2R(rA, (u32)&Memory::Read_U16);
|
||||
PUSH(4, R0, R1, R2, R3);
|
||||
MOV(R0, rB);
|
||||
BL(rA);
|
||||
MOV(rA, R0);
|
||||
POP(4, R0, R1, R2, R3);
|
||||
MOV(RD, rA);
|
||||
gpr.Unlock(rA, rB);
|
||||
#endif
|
||||
SetJumpTarget(DoNotLoad);
|
||||
}
|
||||
void JitArm::lwz(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(LoadStore)
|
||||
|
||||
ARMReg rA = gpr.GetReg();
|
||||
ARMReg rB = gpr.GetReg();
|
||||
ARMReg RD = gpr.R(inst.RD);
|
||||
LDR(rA, R9, STRUCT_OFF(PowerPC::ppcState, Exceptions));
|
||||
CMP(rA, EXCEPTION_DSI);
|
||||
FixupBranch DoNotLoad = B_CC(CC_EQ);
|
||||
|
||||
#if FASTMEM
|
||||
// Backpatch route
|
||||
// Gets loaded in to RD
|
||||
// Address is in R10
|
||||
gpr.Unlock(rA, rB);
|
||||
if (inst.RA)
|
||||
{
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
MOV(R10, RA); // - 4
|
||||
}
|
||||
else
|
||||
MOV(R10, 0); // - 4
|
||||
LoadToReg(RD, R10, 32, (u32)inst.SIMM_16);
|
||||
#else
|
||||
if (inst.RA)
|
||||
{
|
||||
MOVI2R(rB, inst.SIMM_16);
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ADD(rB, rB, RA);
|
||||
}
|
||||
else
|
||||
MOVI2R(rB, (u32)inst.SIMM_16);
|
||||
|
||||
MOVI2R(rA, (u32)&Memory::Read_U32);
|
||||
PUSH(4, R0, R1, R2, R3);
|
||||
MOV(R0, rB);
|
||||
BL(rA);
|
||||
MOV(rA, R0);
|
||||
POP(4, R0, R1, R2, R3);
|
||||
MOV(RD, rA);
|
||||
gpr.Unlock(rA, rB);
|
||||
#endif
|
||||
SetJumpTarget(DoNotLoad);
|
||||
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bSkipIdle &&
|
||||
(inst.hex & 0xFFFF0000) == 0x800D0000 &&
|
||||
(Memory::ReadUnchecked_U32(js.compilerPC + 4) == 0x28000000 ||
|
||||
(SConfig::GetInstance().m_LocalCoreStartupParameter.bWii && Memory::ReadUnchecked_U32(js.compilerPC + 4) == 0x2C000000)) &&
|
||||
Memory::ReadUnchecked_U32(js.compilerPC + 8) == 0x4182fff8)
|
||||
{
|
||||
gpr.Flush();
|
||||
fpr.Flush();
|
||||
|
||||
// if it's still 0, we can wait until the next event
|
||||
TST(RD, RD);
|
||||
FixupBranch noIdle = B_CC(CC_NEQ);
|
||||
rA = gpr.GetReg();
|
||||
|
||||
MOVI2R(rA, (u32)&PowerPC::OnIdle);
|
||||
MOVI2R(R0, PowerPC::ppcState.gpr[inst.RA] + (s32)(s16)inst.SIMM_16);
|
||||
BL(rA);
|
||||
|
||||
gpr.Unlock(rA);
|
||||
WriteExceptionExit();
|
||||
|
||||
SetJumpTarget(noIdle);
|
||||
|
||||
//js.compilerPC += 8;
|
||||
return;
|
||||
}
|
||||
}
|
||||
void JitArm::lwzx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(LoadStore)
|
||||
|
||||
ARMReg rA = gpr.GetReg();
|
||||
ARMReg rB = gpr.GetReg();
|
||||
|
||||
ARMReg RB = gpr.R(inst.RB);
|
||||
ARMReg RD = gpr.R(inst.RD);
|
||||
LDR(rA, R9, STRUCT_OFF(PowerPC::ppcState, Exceptions));
|
||||
CMP(rA, EXCEPTION_DSI);
|
||||
FixupBranch DoNotLoad = B_CC(CC_EQ);
|
||||
#if FASTMEM
|
||||
// Backpatch route
|
||||
// Gets loaded in to RD
|
||||
// Address is in R10
|
||||
gpr.Unlock(rA, rB);
|
||||
if (inst.RA)
|
||||
{
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ADD(R10, RA, RB); // - 4
|
||||
}
|
||||
else
|
||||
MOV(R10, RB); // -4
|
||||
LoadToReg(RD, R10, 32, 0);
|
||||
#else
|
||||
if (inst.RA)
|
||||
{
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ADD(rB, RA, RB);
|
||||
}
|
||||
else
|
||||
MOV(rB, RB);
|
||||
|
||||
MOVI2R(rA, (u32)&Memory::Read_U32);
|
||||
PUSH(4, R0, R1, R2, R3);
|
||||
MOV(R0, rB);
|
||||
BL(rA);
|
||||
MOV(rA, R0);
|
||||
POP(4, R0, R1, R2, R3);
|
||||
MOV(RD, rA);
|
||||
gpr.Unlock(rA, rB);
|
||||
#endif
|
||||
SetJumpTarget(DoNotLoad);
|
||||
//// u32 temp = Memory::Read_U32(_inst.RA ? (m_GPR[_inst.RA] + m_GPR[_inst.RB]) : m_GPR[_inst.RB]);
|
||||
}
|
||||
void JitArm::icbi(UGeckoInstruction inst)
|
||||
{
|
||||
Default(inst);
|
||||
WriteExit(js.compilerPC + 4, 0);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user