Merge branch 'master' into GLSL-master
Conflicts: CMakeLists.txt Source/Core/DolphinWX/CMakeLists.txt Source/Core/DolphinWX/Src/GLInterface.h Source/Core/VideoCommon/Src/PixelShaderGen.cpp Source/Core/VideoCommon/Src/TextureCacheBase.cpp Source/Core/VideoCommon/Src/VertexManagerBase.cpp Source/Plugins/Plugin_VideoDX11/Src/VertexManager.cpp Source/Plugins/Plugin_VideoDX9/Src/VertexManager.cpp Source/Plugins/Plugin_VideoOGL/Plugin_VideoOGL.vcxproj Source/Plugins/Plugin_VideoOGL/Plugin_VideoOGL.vcxproj.filters Source/Plugins/Plugin_VideoOGL/Src/GLUtil.h Source/Plugins/Plugin_VideoOGL/Src/PixelShaderCache.cpp Source/Plugins/Plugin_VideoOGL/Src/TextureCache.cpp Source/Plugins/Plugin_VideoOGL/Src/VertexManager.cpp Source/Plugins/Plugin_VideoOGL/Src/VertexShaderCache.cpp Source/Plugins/Plugin_VideoOGL/Src/main.cpp
This commit is contained in:
commit
a1c5e90083
443 files changed
+16217
-6374
No files matched your search
@@ -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)
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
@@ -54,7 +54,7 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
@@ -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" />
|
||||
@@ -600,4 +602,4 @@
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -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
|
||||
@@ -90,9 +90,7 @@ bool CBoot::Boot_WiiWAD(const char* _pFilename)
|
||||
u64 titleID = ContentLoader.GetTitleID();
|
||||
// create data directory
|
||||
File::CreateFullPath(Common::GetTitleDataPath(titleID));
|
||||
|
||||
if (titleID == TITLEID_SYSMENU)
|
||||
HLE_IPC_CreateVirtualFATFilesystem();
|
||||
|
||||
// setup wii mem
|
||||
if (!SetupWiiMemory(ContentLoader.GetCountry()))
|
||||
return false;
|
||||
|
||||
@@ -100,9 +100,6 @@ SConfig::SConfig()
|
||||
{
|
||||
// Make sure we have log manager
|
||||
LoadSettings();
|
||||
//Make sure we load any extra settings
|
||||
LoadSettingsWii();
|
||||
|
||||
}
|
||||
|
||||
void SConfig::Init()
|
||||
@@ -140,7 +137,7 @@ void SConfig::SaveSettings()
|
||||
ini.Get("General", "GCMPathes", &oldPaths, 0);
|
||||
for (int i = numPaths; i < oldPaths; i++)
|
||||
{
|
||||
TCHAR tmp[16];
|
||||
char tmp[16];
|
||||
sprintf(tmp, "GCMPath%i", i);
|
||||
ini.DeleteKey("General", tmp);
|
||||
}
|
||||
@@ -149,7 +146,7 @@ void SConfig::SaveSettings()
|
||||
|
||||
for (int i = 0; i < numPaths; i++)
|
||||
{
|
||||
TCHAR tmp[16];
|
||||
char tmp[16];
|
||||
sprintf(tmp, "GCMPath%i", i);
|
||||
ini.Set("General", tmp, m_ISOFolder[i]);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -285,7 +284,7 @@ void SConfig::LoadSettings()
|
||||
{
|
||||
for (int i = 0; i < numGCMPaths; i++)
|
||||
{
|
||||
TCHAR tmp[16];
|
||||
char tmp[16];
|
||||
sprintf(tmp, "GCMPath%i", i);
|
||||
std::string tmpPath;
|
||||
ini.Get("General", tmp, &tmpPath, "");
|
||||
@@ -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);
|
||||
@@ -428,18 +433,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);}
|
||||
|
||||
|
||||
@@ -98,8 +98,8 @@ void Console_Submit(const char *cmd)
|
||||
}
|
||||
CASE("dump")
|
||||
{
|
||||
TCHAR temp[256];
|
||||
TCHAR filename[256];
|
||||
char temp[256];
|
||||
char filename[256];
|
||||
u32 start;
|
||||
u32 end;
|
||||
sscanf(cmd, "%s %08x %08x %s", temp, &start, &end, filename);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -754,7 +754,8 @@ bool DSPAssembler::AssembleFile(const char *fname, int pass)
|
||||
{
|
||||
int disable_text = 0; // modified by Hermes
|
||||
|
||||
std::ifstream fsrc(fname);
|
||||
std::ifstream fsrc;
|
||||
OpenFStream(fsrc, fname, std::ios_base::in);
|
||||
|
||||
if (fsrc.fail())
|
||||
{
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
namespace Win32TAPHelper
|
||||
{
|
||||
|
||||
bool IsTAPDevice(const char *guid)
|
||||
bool IsTAPDevice(const TCHAR *guid)
|
||||
{
|
||||
HKEY netcard_key;
|
||||
LONG status;
|
||||
@@ -39,13 +39,13 @@ bool IsTAPDevice(const char *guid)
|
||||
|
||||
for (;;)
|
||||
{
|
||||
char enum_name[256];
|
||||
char unit_string[256];
|
||||
TCHAR enum_name[256];
|
||||
TCHAR unit_string[256];
|
||||
HKEY unit_key;
|
||||
char component_id_string[] = "ComponentId";
|
||||
char component_id[256];
|
||||
char net_cfg_instance_id_string[] = "NetCfgInstanceId";
|
||||
char net_cfg_instance_id[256];
|
||||
TCHAR component_id_string[] = _T("ComponentId");
|
||||
TCHAR component_id[256];
|
||||
TCHAR net_cfg_instance_id_string[] = _T("NetCfgInstanceId");
|
||||
TCHAR net_cfg_instance_id[256];
|
||||
DWORD data_type;
|
||||
|
||||
len = sizeof(enum_name);
|
||||
@@ -56,7 +56,7 @@ bool IsTAPDevice(const char *guid)
|
||||
else if (status != ERROR_SUCCESS)
|
||||
return false;
|
||||
|
||||
snprintf(unit_string, sizeof(unit_string), "%s\\%s", ADAPTER_KEY, enum_name);
|
||||
_sntprintf(unit_string, sizeof(unit_string), _T("%s\\%s"), ADAPTER_KEY, enum_name);
|
||||
|
||||
status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, unit_string, 0, KEY_READ, &unit_key);
|
||||
|
||||
@@ -78,8 +78,8 @@ bool IsTAPDevice(const char *guid)
|
||||
|
||||
if (status == ERROR_SUCCESS && data_type == REG_SZ)
|
||||
{
|
||||
if (!strcmp(component_id, TAP_COMPONENT_ID) &&
|
||||
!strcmp(net_cfg_instance_id, guid))
|
||||
if (!_tcscmp(component_id, TAP_COMPONENT_ID) &&
|
||||
!_tcscmp(net_cfg_instance_id, guid))
|
||||
{
|
||||
RegCloseKey(unit_key);
|
||||
RegCloseKey(netcard_key);
|
||||
@@ -96,7 +96,7 @@ bool IsTAPDevice(const char *guid)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetGUIDs(std::vector<std::string>& guids)
|
||||
bool GetGUIDs(std::vector<std::basic_string<TCHAR>>& guids)
|
||||
{
|
||||
LONG status;
|
||||
HKEY control_net_key;
|
||||
@@ -111,12 +111,12 @@ bool GetGUIDs(std::vector<std::string>& guids)
|
||||
|
||||
while (!found_all)
|
||||
{
|
||||
char enum_name[256];
|
||||
char connection_string[256];
|
||||
TCHAR enum_name[256];
|
||||
TCHAR connection_string[256];
|
||||
HKEY connection_key;
|
||||
char name_data[256];
|
||||
TCHAR name_data[256];
|
||||
DWORD name_type;
|
||||
const char name_string[] = "Name";
|
||||
const TCHAR name_string[] = _T("Name");
|
||||
|
||||
len = sizeof(enum_name);
|
||||
status = RegEnumKeyEx(control_net_key, i, enum_name,
|
||||
@@ -127,8 +127,8 @@ bool GetGUIDs(std::vector<std::string>& guids)
|
||||
else if (status != ERROR_SUCCESS)
|
||||
return false;
|
||||
|
||||
snprintf(connection_string, sizeof(connection_string),
|
||||
"%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, enum_name);
|
||||
_sntprintf(connection_string, sizeof(connection_string),
|
||||
_T("%s\\%s\\Connection"), NETWORK_CONNECTIONS_KEY, enum_name);
|
||||
|
||||
status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, connection_string,
|
||||
0, KEY_READ, &connection_key);
|
||||
@@ -165,15 +165,11 @@ bool GetGUIDs(std::vector<std::string>& guids)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTAP(HANDLE& adapter, const std::string device_guid)
|
||||
bool OpenTAP(HANDLE& adapter, const std::basic_string<TCHAR>& device_guid)
|
||||
{
|
||||
char device_path[256];
|
||||
auto const device_path = USERMODEDEVICEDIR + device_guid + TAPSUFFIX;
|
||||
|
||||
/* Open Windows TAP-Win32 adapter */
|
||||
snprintf(device_path, sizeof(device_path), "%s%s%s",
|
||||
USERMODEDEVICEDIR, device_guid.c_str(), TAPSUFFIX);
|
||||
|
||||
adapter = CreateFile(device_path, GENERIC_READ | GENERIC_WRITE, 0, 0,
|
||||
adapter = CreateFile(device_path.c_str(), GENERIC_READ | GENERIC_WRITE, 0, 0,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
|
||||
|
||||
if (adapter == INVALID_HANDLE_VALUE)
|
||||
@@ -192,7 +188,7 @@ bool CEXIETHERNET::Activate()
|
||||
return true;
|
||||
|
||||
DWORD len;
|
||||
std::vector<std::string> device_guids;
|
||||
std::vector<std::basic_string<TCHAR>> device_guids;
|
||||
|
||||
if (!Win32TAPHelper::GetGUIDs(device_guids))
|
||||
{
|
||||
|
||||
@@ -65,18 +65,18 @@
|
||||
// Registry keys
|
||||
//=================
|
||||
|
||||
#define ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
|
||||
#define ADAPTER_KEY _T("SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}")
|
||||
|
||||
#define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
|
||||
#define NETWORK_CONNECTIONS_KEY _T("SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}")
|
||||
|
||||
//======================
|
||||
// Filesystem prefixes
|
||||
//======================
|
||||
|
||||
#define USERMODEDEVICEDIR "\\\\.\\Global\\"
|
||||
#define SYSDEVICEDIR "\\Device\\"
|
||||
#define USERDEVICEDIR "\\DosDevices\\Global\\"
|
||||
#define TAPSUFFIX ".tap"
|
||||
#define USERMODEDEVICEDIR _T("\\\\.\\Global\\")
|
||||
#define SYSDEVICEDIR _T("\\Device\\")
|
||||
#define USERDEVICEDIR _T("\\DosDevices\\Global\\")
|
||||
#define TAPSUFFIX _T(".tap")
|
||||
|
||||
//=========================================================
|
||||
// TAP_COMPONENT_ID -- This string defines the TAP driver
|
||||
@@ -84,4 +84,4 @@
|
||||
// simultaneously.
|
||||
//=========================================================
|
||||
|
||||
#define TAP_COMPONENT_ID "tap0901"
|
||||
#define TAP_COMPONENT_ID _T("tap0901")
|
||||
@@ -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
|
||||
|
||||
@@ -432,7 +432,6 @@ std::string GCMemcard::DEntry_BIFlags(u8 index) const
|
||||
flags.push_back((x & 0x80) ? '1' : '0');
|
||||
x = x << 1;
|
||||
}
|
||||
flags.push_back(0);
|
||||
return flags;
|
||||
}
|
||||
|
||||
@@ -469,7 +468,6 @@ std::string GCMemcard::DEntry_IconFmt(u8 index) const
|
||||
format.push_back((x & 0x80) ? '1' : '0');
|
||||
x = x << 1;
|
||||
}
|
||||
format.push_back(0);
|
||||
return format;
|
||||
}
|
||||
|
||||
@@ -485,7 +483,6 @@ std::string GCMemcard::DEntry_AnimSpeed(u8 index) const
|
||||
speed.push_back((x & 0x80) ? '1' : '0');
|
||||
x = x << 1;
|
||||
}
|
||||
speed.push_back(0);
|
||||
return speed;
|
||||
}
|
||||
|
||||
@@ -498,7 +495,6 @@ std::string GCMemcard::DEntry_Permissions(u8 index) const
|
||||
permissionsString.push_back((Permissions & 16) ? 'x' : 'M');
|
||||
permissionsString.push_back((Permissions & 8) ? 'x' : 'C');
|
||||
permissionsString.push_back((Permissions & 4) ? 'P' : 'x');
|
||||
permissionsString.push_back(0);
|
||||
return permissionsString;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -133,7 +133,7 @@ unsigned int GetAttached()
|
||||
// input/output: ptr
|
||||
// input: mode
|
||||
//
|
||||
void DoState(unsigned char **ptr, int mode)
|
||||
void DoState(u8 **ptr, PointerWrap::Mode mode)
|
||||
{
|
||||
// TODO:
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#define _WIIMOTE_H_
|
||||
|
||||
#include "../../InputCommon/Src/InputConfig.h"
|
||||
#include "ChunkFile.h"
|
||||
|
||||
#define MAX_WIIMOTES 4
|
||||
|
||||
@@ -25,7 +26,7 @@ void Shutdown();
|
||||
void Initialize(void* const hwnd);
|
||||
|
||||
unsigned int GetAttached();
|
||||
void DoState(unsigned char **ptr, int mode);
|
||||
void DoState(u8 **ptr, PointerWrap::Mode mode);
|
||||
void EmuStateChange(EMUSTATE_CHANGE newState);
|
||||
InputPlugin *GetPlugin();
|
||||
|
||||
@@ -38,17 +39,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;
|
||||
@@ -298,7 +297,7 @@ void Wiimote::WriteData(const wm_write_data* const wd)
|
||||
{
|
||||
// writing the whole mii block each write :/
|
||||
std::ofstream file;
|
||||
file.open((File::GetUserPath(D_WIIUSER_IDX) + "mii.bin").c_str(), std::ios::binary | std::ios::out);
|
||||
OpenFStream(file, File::GetUserPath(D_WIIUSER_IDX) + "mii.bin", std::ios::binary | std::ios::out);
|
||||
file.write((char*)m_eeprom + 0x0FCA, 0x02f0);
|
||||
file.close();
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ void Wiimote::SpeakerData(wm_speaker_data* sd)
|
||||
File::Delete("rmtdump.wav");
|
||||
File::Delete("rmtdump.bin");
|
||||
atexit(stopdamnwav);
|
||||
ofile.open("rmtdump.bin", ofile.binary | ofile.out);
|
||||
OpenFStream(ofile, "rmtdump.bin", ofile.binary | ofile.out);
|
||||
wav.Start("rmtdump.wav", 6000/*Common::swap16(m_reg_speaker.sample_rate)*/);
|
||||
}
|
||||
wav.AddMonoSamples(samples, sd->length*2);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,10 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <regex>
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
#include <ctime>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <windows.h>
|
||||
#include <dbt.h>
|
||||
@@ -32,6 +35,9 @@
|
||||
#include <bthdef.h>
|
||||
#include <BluetoothAPIs.h>
|
||||
|
||||
//#define AUTHENTICATE_WIIMOTES
|
||||
#define SHARE_WRITE_WIIMOTES
|
||||
|
||||
typedef struct _HIDD_ATTRIBUTES
|
||||
{
|
||||
ULONG Size;
|
||||
@@ -53,6 +59,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 +75,21 @@ 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;
|
||||
|
||||
#ifdef SHARE_WRITE_WIIMOTES
|
||||
std::unordered_set<std::basic_string<TCHAR>> g_connected_wiimotes;
|
||||
std::mutex g_connected_wiimotes_lock;
|
||||
#endif
|
||||
|
||||
inline void init_lib()
|
||||
{
|
||||
if (!initialized)
|
||||
@@ -109,12 +126,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,312 +147,353 @@ 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)
|
||||
#ifdef SHARE_WRITE_WIIMOTES
|
||||
std::lock_guard<std::mutex> lk(g_connected_wiimotes_lock);
|
||||
if (g_connected_wiimotes.count(devicepath) != 0)
|
||||
return false;
|
||||
|
||||
auto const open_flags = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
||||
#else
|
||||
// Having no FILE_SHARE_WRITE disallows us from connecting to the same wiimote twice.
|
||||
// (And disallows using wiimotes in use by other programs)
|
||||
// This is what "WiiYourself" does.
|
||||
// Apparently this doesn't work for everyone. It might be their fault.
|
||||
auto const open_flags = FILE_SHARE_READ;
|
||||
#endif
|
||||
|
||||
dev_handle = CreateFile(devicepath.c_str(),
|
||||
GENERIC_READ | GENERIC_WRITE, open_flags,
|
||||
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);
|
||||
*/
|
||||
#ifdef SHARE_WRITE_WIIMOTES
|
||||
g_connected_wiimotes.insert(devicepath);
|
||||
#endif
|
||||
|
||||
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);
|
||||
|
||||
#ifdef SHARE_WRITE_WIIMOTES
|
||||
std::lock_guard<std::mutex> lk(g_connected_wiimotes_lock);
|
||||
g_connected_wiimotes.erase(devicepath);
|
||||
#endif
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
BLUETOOTH_DEVICE_SEARCH_PARAMS srch;
|
||||
srch.dwSize = sizeof(srch);
|
||||
srch.fReturnAuthenticated = true;
|
||||
@@ -441,83 +502,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 (IsValidBluetoothName(UTF16ToUTF8(btdi.szName)))
|
||||
{
|
||||
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 +555,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
|
||||
std::basic_string<TCHAR> 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.
|
||||
|
||||
@@ -33,9 +33,6 @@ std::string HLE_IPC_BuildFilename(std::string path_wii, int _size)
|
||||
{
|
||||
std::string path_full = File::GetUserPath(D_WIIROOT_IDX);
|
||||
|
||||
if ((path_wii.length() > 0) && (path_wii[1] == '0'))
|
||||
path_full += std::string("/title"); // this looks and feel like a hack...
|
||||
|
||||
// Replaces chars that FAT32 can't support with strings defined in /sys/replace
|
||||
for (auto i = replacements.begin(); i != replacements.end(); ++i)
|
||||
{
|
||||
@@ -48,39 +45,6 @@ std::string HLE_IPC_BuildFilename(std::string path_wii, int _size)
|
||||
return path_full;
|
||||
}
|
||||
|
||||
void HLE_IPC_CreateVirtualFATFilesystem()
|
||||
{
|
||||
const int cdbSize = 0x01400000;
|
||||
const std::string cdbPath = Common::GetTitleDataPath(TITLEID_SYSMENU) + "cdb.vff";
|
||||
if ((int)File::GetSize(cdbPath) < cdbSize)
|
||||
{
|
||||
// cdb.vff is a virtual Fat filesystem created on first launch of sysmenu
|
||||
// we create it here as it is faster ~3 minutes for me when sysmenu does it ~1 second created here
|
||||
const u8 cdbHDR[0x20] = {'V', 'F', 'F', 0x20, 0xfe, 0xff, 1, 0, 1, 0x40, 0, 0, 0, 0x20};
|
||||
const u8 cdbFAT[4] = {0xf0, 0xff, 0xff, 0xff};
|
||||
|
||||
File::IOFile cdbFile(cdbPath, "wb");
|
||||
if (cdbFile)
|
||||
{
|
||||
cdbFile.WriteBytes(cdbHDR, 0x20);
|
||||
cdbFile.WriteBytes(cdbFAT, 0x4);
|
||||
cdbFile.Seek(0x14020, SEEK_SET);
|
||||
cdbFile.WriteBytes(cdbFAT, 0x4);
|
||||
// 20 MiB file
|
||||
cdbFile.Seek(cdbSize - 1, SEEK_SET);
|
||||
// write the final 0 to 0 file from the second FAT to 20 MiB
|
||||
cdbFile.WriteBytes(cdbHDR + 14, 1);
|
||||
if (!cdbFile.IsGood())
|
||||
{
|
||||
cdbFile.Close();
|
||||
File::Delete(cdbPath);
|
||||
}
|
||||
cdbFile.Flush();
|
||||
cdbFile.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CWII_IPC_HLE_Device_FileIO::CWII_IPC_HLE_Device_FileIO(u32 _DeviceID, const std::string& _rDeviceName)
|
||||
: IWII_IPC_HLE_Device(_DeviceID, _rDeviceName, false) // not a real hardware
|
||||
, m_Mode(0)
|
||||
@@ -139,82 +103,81 @@ bool CWII_IPC_HLE_Device_FileIO::Open(u32 _CommandAddress, u32 _Mode)
|
||||
return true;
|
||||
}
|
||||
|
||||
File::IOFile CWII_IPC_HLE_Device_FileIO::OpenFile()
|
||||
// Opens file if needed.
|
||||
// Clears any error state.
|
||||
// Seeks to proper position position.
|
||||
void CWII_IPC_HLE_Device_FileIO::PrepareFile()
|
||||
{
|
||||
const char* open_mode = "";
|
||||
|
||||
switch (m_Mode)
|
||||
if (!m_file.IsOpen())
|
||||
{
|
||||
case ISFS_OPEN_READ:
|
||||
open_mode = "rb";
|
||||
break;
|
||||
|
||||
case ISFS_OPEN_WRITE:
|
||||
case ISFS_OPEN_RW:
|
||||
open_mode = "r+b";
|
||||
break;
|
||||
|
||||
default:
|
||||
PanicAlertT("FileIO: Unknown open mode : 0x%02x", m_Mode);
|
||||
break;
|
||||
const char* open_mode = "";
|
||||
|
||||
switch (m_Mode)
|
||||
{
|
||||
case ISFS_OPEN_READ:
|
||||
open_mode = "rb";
|
||||
break;
|
||||
|
||||
case ISFS_OPEN_WRITE:
|
||||
case ISFS_OPEN_RW:
|
||||
open_mode = "r+b";
|
||||
break;
|
||||
|
||||
default:
|
||||
PanicAlertT("FileIO: Unknown open mode : 0x%02x", m_Mode);
|
||||
break;
|
||||
}
|
||||
|
||||
m_file.Open(m_filepath, open_mode);
|
||||
}
|
||||
|
||||
return File::IOFile(m_filepath, open_mode);
|
||||
m_file.Clear();
|
||||
m_file.Seek(m_SeekPos, SEEK_SET);
|
||||
}
|
||||
|
||||
bool CWII_IPC_HLE_Device_FileIO::Seek(u32 _CommandAddress)
|
||||
{
|
||||
u32 ReturnValue = FS_RESULT_FATAL;
|
||||
const u32 SeekPosition = Memory::Read_U32(_CommandAddress + 0xC);
|
||||
const u32 SeekOffset = Memory::Read_U32(_CommandAddress + 0xC);
|
||||
const u32 Mode = Memory::Read_U32(_CommandAddress + 0x10);
|
||||
|
||||
if (auto file = OpenFile())
|
||||
PrepareFile();
|
||||
if (m_file)
|
||||
{
|
||||
ReturnValue = FS_RESULT_FATAL;
|
||||
|
||||
const u64 fileSize = file.GetSize();
|
||||
INFO_LOG(WII_IPC_FILEIO, "FileIO: Seek Pos: 0x%08x, Mode: %i (%s, Length=0x%08llx)", SeekPosition, Mode, m_Name.c_str(), fileSize);
|
||||
const u64 fileSize = m_file.GetSize();
|
||||
INFO_LOG(WII_IPC_FILEIO, "FileIO: Seek Pos: 0x%08x, Mode: %i (%s, Length=0x%08llx)", SeekOffset, Mode, m_Name.c_str(), fileSize);
|
||||
u64 wantedPos = 0;
|
||||
switch (Mode)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
if (SeekPosition <= fileSize)
|
||||
{
|
||||
m_SeekPos = SeekPosition;
|
||||
ReturnValue = m_SeekPos;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
u32 wantedPos = SeekPosition+m_SeekPos;
|
||||
if (wantedPos <= fileSize)
|
||||
{
|
||||
m_SeekPos = wantedPos;
|
||||
ReturnValue = m_SeekPos;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
u64 wantedPos = fileSize+m_SeekPos;
|
||||
if (wantedPos <= fileSize)
|
||||
{
|
||||
m_SeekPos = wantedPos;
|
||||
ReturnValue = m_SeekPos;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
PanicAlert("CWII_IPC_HLE_Device_FileIO Unsupported seek mode %i", Mode);
|
||||
ReturnValue = FS_RESULT_FATAL;
|
||||
break;
|
||||
}
|
||||
case 0:
|
||||
wantedPos = SeekOffset;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
wantedPos = m_SeekPos + (s32)SeekOffset;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
wantedPos = fileSize + (s32)SeekOffset;
|
||||
break;
|
||||
|
||||
default:
|
||||
PanicAlert("CWII_IPC_HLE_Device_FileIO Unsupported seek mode %i", Mode);
|
||||
ReturnValue = FS_RESULT_FATAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (wantedPos <= fileSize)
|
||||
{
|
||||
m_SeekPos = wantedPos;
|
||||
ReturnValue = m_SeekPos;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: This can't be right.
|
||||
ReturnValue = FS_FILE_NOT_EXIST;
|
||||
}
|
||||
Memory::Write_U32(ReturnValue, _CommandAddress + 0x4);
|
||||
@@ -227,9 +190,9 @@ bool CWII_IPC_HLE_Device_FileIO::Read(u32 _CommandAddress)
|
||||
u32 ReturnValue = FS_EACCESS;
|
||||
const u32 Address = Memory::Read_U32(_CommandAddress + 0xC); // Read to this memory address
|
||||
const u32 Size = Memory::Read_U32(_CommandAddress + 0x10);
|
||||
|
||||
|
||||
if (auto file = OpenFile())
|
||||
|
||||
PrepareFile();
|
||||
if (m_file)
|
||||
{
|
||||
if (m_Mode == ISFS_OPEN_WRITE)
|
||||
{
|
||||
@@ -238,9 +201,8 @@ bool CWII_IPC_HLE_Device_FileIO::Read(u32 _CommandAddress)
|
||||
else
|
||||
{
|
||||
INFO_LOG(WII_IPC_FILEIO, "FileIO: Read 0x%x bytes to 0x%08x from %s", Size, Address, m_Name.c_str());
|
||||
file.Seek(m_SeekPos, SEEK_SET);
|
||||
ReturnValue = (u32)fread(Memory::GetPointer(Address), 1, Size, file.GetHandle());
|
||||
if (ReturnValue != Size && ferror(file.GetHandle()))
|
||||
ReturnValue = (u32)fread(Memory::GetPointer(Address), 1, Size, m_file.GetHandle());
|
||||
if (ReturnValue != Size && ferror(m_file.GetHandle()))
|
||||
{
|
||||
ReturnValue = FS_EACCESS;
|
||||
}
|
||||
@@ -267,8 +229,8 @@ bool CWII_IPC_HLE_Device_FileIO::Write(u32 _CommandAddress)
|
||||
const u32 Address = Memory::Read_U32(_CommandAddress + 0xC); // Write data from this memory address
|
||||
const u32 Size = Memory::Read_U32(_CommandAddress + 0x10);
|
||||
|
||||
|
||||
if (auto file = OpenFile())
|
||||
PrepareFile();
|
||||
if (m_file)
|
||||
{
|
||||
if (m_Mode == ISFS_OPEN_READ)
|
||||
{
|
||||
@@ -277,8 +239,7 @@ bool CWII_IPC_HLE_Device_FileIO::Write(u32 _CommandAddress)
|
||||
else
|
||||
{
|
||||
INFO_LOG(WII_IPC_FILEIO, "FileIO: Write 0x%04x bytes from 0x%08x to %s", Size, Address, m_Name.c_str());
|
||||
file.Seek(m_SeekPos, SEEK_SET);
|
||||
if (file.WriteBytes(Memory::GetPointer(Address), Size))
|
||||
if (m_file.WriteBytes(Memory::GetPointer(Address), Size))
|
||||
{
|
||||
ReturnValue = Size;
|
||||
m_SeekPos += Size;
|
||||
@@ -308,9 +269,10 @@ bool CWII_IPC_HLE_Device_FileIO::IOCtl(u32 _CommandAddress)
|
||||
{
|
||||
case ISFS_IOCTL_GETFILESTATS:
|
||||
{
|
||||
if (auto file = OpenFile())
|
||||
PrepareFile();
|
||||
if (m_file)
|
||||
{
|
||||
u32 m_FileLength = (u32)file.GetSize();
|
||||
u32 m_FileLength = (u32)m_file.GetSize();
|
||||
|
||||
const u32 BufferOut = Memory::Read_U32(_CommandAddress + 0x18);
|
||||
INFO_LOG(WII_IPC_FILEIO, "FileIO: ISFS_IOCTL_GETFILESTATS");
|
||||
@@ -345,6 +307,7 @@ void CWII_IPC_HLE_Device_FileIO::DoState(PointerWrap &p)
|
||||
|
||||
p.Do(m_Mode);
|
||||
p.Do(m_SeekPos);
|
||||
|
||||
|
||||
m_file.Close();
|
||||
m_filepath = HLE_IPC_BuildFilename(m_Name, 64);
|
||||
}
|
||||
@@ -22,7 +22,6 @@
|
||||
#include "FileUtil.h"
|
||||
|
||||
std::string HLE_IPC_BuildFilename(std::string _pFilename, int _size);
|
||||
void HLE_IPC_CreateVirtualFATFilesystem();
|
||||
|
||||
class CWII_IPC_HLE_Device_FileIO : public IWII_IPC_HLE_Device
|
||||
{
|
||||
@@ -39,9 +38,9 @@ public:
|
||||
bool IOCtl(u32 _CommandAddress);
|
||||
void DoState(PointerWrap &p);
|
||||
|
||||
File::IOFile OpenFile();
|
||||
|
||||
private:
|
||||
void PrepareFile();
|
||||
|
||||
enum
|
||||
{
|
||||
ISFS_OPEN_READ = 1,
|
||||
@@ -77,6 +76,7 @@ private:
|
||||
|
||||
u32 m_Mode;
|
||||
u32 m_SeekPos;
|
||||
File::IOFile m_file;
|
||||
|
||||
std::string m_filepath;
|
||||
};
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
#include "CommonPaths.h"
|
||||
#include "IPC_HLE/WII_IPC_HLE_Device_usb.h"
|
||||
#include "../Movie.h"
|
||||
#include "StringUtil.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
@@ -926,7 +927,7 @@ u32 CWII_IPC_HLE_Device_es::ES_DIVerify(u8* _pTMD, u32 _sz)
|
||||
else
|
||||
{
|
||||
#ifdef _WIN32
|
||||
MoveFile(savePath.c_str(), (savePath + "../backup/").c_str());
|
||||
MoveFile(UTF8ToTStr(savePath).c_str(), UTF8ToTStr(savePath + "../backup/").c_str());
|
||||
#else
|
||||
File::CopyDir(savePath.c_str(),(savePath + "../backup/").c_str());
|
||||
File::DeleteDirRecursively(savePath.c_str());
|
||||
@@ -940,7 +941,7 @@ u32 CWII_IPC_HLE_Device_es::ES_DIVerify(u8* _pTMD, u32 _sz)
|
||||
if (File::Exists((savePath + "banner.bin").c_str()))
|
||||
File::DeleteDirRecursively(savePath);
|
||||
#ifdef _WIN32
|
||||
MoveFile((savePath + "../backup/").c_str(), savePath.c_str());
|
||||
MoveFile(UTF8ToTStr(savePath + "../backup/").c_str(), UTF8ToTStr(savePath).c_str());
|
||||
#else
|
||||
File::CopyDir((savePath + "../backup/").c_str(), savePath.c_str());
|
||||
File::DeleteDirRecursively((savePath + "../backup/").c_str());
|
||||
|
||||
@@ -377,7 +377,7 @@ s32 CWII_IPC_HLE_Device_fs::ExecuteCommand(u32 _Parameter, u32 _BufferIn, u32 _B
|
||||
u32 Addr = _BufferOut;
|
||||
Memory::Write_U32(OwnerID, Addr); Addr += 4;
|
||||
Memory::Write_U16(GroupID, Addr); Addr += 2;
|
||||
memcpy(Memory::GetPointer(Addr), Filename.c_str(), Filename.size()); Addr += 64;
|
||||
memcpy(Memory::GetPointer(Addr), Memory::GetPointer(_BufferIn), 64); Addr += 64;
|
||||
Memory::Write_U8(OwnerPerm, Addr); Addr += 1;
|
||||
Memory::Write_U8(GroupPerm, Addr); Addr += 1;
|
||||
Memory::Write_U8(OtherPerm, Addr); Addr += 1;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -34,7 +34,7 @@ struct Rpt : public std::vector<u8>
|
||||
|
||||
typedef std::vector<Rpt> NetWiimote;
|
||||
|
||||
#define NETPLAY_VERSION "Dolphin NetPlay r6423"
|
||||
#define NETPLAY_VERSION "Dolphin NetPlay 2013-03-03"
|
||||
|
||||
// messages
|
||||
enum
|
||||
|
||||
@@ -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"
|
||||
@@ -338,7 +338,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()
|
||||
@@ -357,7 +357,7 @@ void Jit64::WriteExternalExceptionExit()
|
||||
MOV(32, R(EAX), M(&PC));
|
||||
MOV(32, M(&NPC), R(EAX));
|
||||
ABI_CallFunction(reinterpret_cast<void *>(&PowerPC::CheckExternalExceptions));
|
||||
SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount));
|
||||
SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount));
|
||||
JMP(asm_routines.dispatcher, true);
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
if (js.memcheck) \
|
||||
SetJumpTarget(memException);
|
||||
|
||||
class Jit64 : public JitBase
|
||||
class Jit64 : public Jitx86Base
|
||||
{
|
||||
private:
|
||||
GPRRegCache gpr;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -1991,8 +1991,10 @@ void JitIL::WriteCode() {
|
||||
}
|
||||
|
||||
void ProfiledReJit() {
|
||||
jit->SetCodePtr(jit->js.rewriteStart);
|
||||
DoWriteCode(&((JitIL *)jit)->ibuild, (JitIL *)jit, true, false);
|
||||
jit->js.curBlock->codeSize = (int)(jit->GetCodePtr() - jit->js.rewriteStart);
|
||||
jit->GetBlockCache()->FinalizeBlock(jit->js.curBlock->blockNum, jit->jo.enableBlocklink, jit->js.curBlock->normalEntry);
|
||||
JitIL *jitil = (JitIL *)jit;
|
||||
jitil->SetCodePtr(jitil->js.rewriteStart);
|
||||
DoWriteCode(&jitil->ibuild, jitil, true, false);
|
||||
jitil->js.curBlock->codeSize = (int)(jitil->GetCodePtr() - jitil->js.rewriteStart);
|
||||
jitil->GetBlockCache()->FinalizeBlock(jitil->js.curBlock->blockNum, jitil->jo.enableBlocklink,
|
||||
jitil->js.curBlock->normalEntry);
|
||||
}
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#include "Common.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
#include "Thunk.h"
|
||||
#include "../../HLE/HLE.h"
|
||||
#include "../../Core.h"
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
#define DISABLE64
|
||||
#endif
|
||||
|
||||
class JitIL : public JitBase
|
||||
class JitIL : public Jitx86Base
|
||||
{
|
||||
private:
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
#include "x64Emitter.h"
|
||||
|
||||
#include "../../HW/Memmap.h"
|
||||
@@ -25,7 +25,7 @@
|
||||
#include "MemoryUtil.h"
|
||||
#include "CPUDetect.h"
|
||||
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
#include "Thunk.h"
|
||||
|
||||
#include "../../HW/GPFifo.h"
|
||||
@@ -211,14 +211,14 @@ void JitILAsmRoutineManager::Generate()
|
||||
doTiming = GetCodePtr();
|
||||
|
||||
ABI_CallFunction(reinterpret_cast<void *>(&CoreTiming::Advance));
|
||||
|
||||
|
||||
testExceptions = GetCodePtr();
|
||||
MOV(32, R(EAX), M(&PC));
|
||||
MOV(32, M(&NPC), R(EAX));
|
||||
ABI_CallFunction(reinterpret_cast<void *>(&PowerPC::CheckExceptions));
|
||||
MOV(32, R(EAX), M(&NPC));
|
||||
MOV(32, M(&PC), R(EAX));
|
||||
|
||||
|
||||
TEST(32, M((void*)PowerPC::GetStatePtr()), Imm32(0xFFFFFFFF));
|
||||
J_CC(CC_Z, outer_loop, true);
|
||||
//Landing pad for drec space
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "../../HW/Memmap.h"
|
||||
#include "../PPCTables.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
|
||||
#include "JitIL.h"
|
||||
#include "JitILAsm.h"
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "../PPCTables.h"
|
||||
#include "CPUDetect.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
|
||||
#include "JitIL.h"
|
||||
#include "JitILAsm.h"
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#include "../PPCTables.h"
|
||||
#include "CPUDetect.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
|
||||
#include "JitIL.h"
|
||||
#include "JitILAsm.h"
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "../PowerPC.h"
|
||||
#include "../PPCTables.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
#include "Thunk.h"
|
||||
|
||||
#include "JitIL.h"
|
||||
|
||||
@@ -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,497 @@
|
||||
// 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, PPCSTATE_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, PPCSTATE_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, PPCSTATE_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, PPCSTATE_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, PPCSTATE_OFF(PowerPC::ppcState, msr));
|
||||
TST(A, Shift);
|
||||
FixupBranch b1 = B_CC(CC_NEQ);
|
||||
STR(R9, C, PPCSTATE_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;}
|
||||
#define PPCSTATE_OFF(str, elem) ((s32)STRUCT_OFF(PowerPC::ppcState, elem) - (s32)STRUCT_OFF(PowerPC::ppcState, spr[0]))
|
||||
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,45 @@
|
||||
// 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(R11, address);
|
||||
emit.MOVI2R(R12, (u32)jit->GetAsmRoutines()->dispatcher);
|
||||
emit.STR(R9, R11, PPCSTATE_OFF(PowerPC::ppcState, pc));
|
||||
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,355 @@
|
||||
// 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, PPCSTATE_OFF(PowerPC::ppcState, Exceptions));
|
||||
ORR(rA, rA, EXCEPTION_SYSCALL);
|
||||
STR(R9, rA, PPCSTATE_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(rB, (~mask) & clearMSR13);
|
||||
MOVI2R(rC, mask & clearMSR13);
|
||||
|
||||
LDR(rD, R9, PPCSTATE_OFF(PowerPC::ppcState, msr));
|
||||
|
||||
AND(rD, rD, rB); // rD = Masked MSR
|
||||
STR(R9, rD, PPCSTATE_OFF(PowerPC::ppcState, msr));
|
||||
|
||||
LDR(rB, R9, PPCSTATE_OFF(PowerPC::ppcState, spr[SPR_SRR1])); // rB contains SRR1 here
|
||||
|
||||
AND(rB, rB, rC); // rB contains masked SRR1 here
|
||||
ORR(rB, rD, rB); // rB = Masked MSR OR masked SRR1
|
||||
|
||||
STR(R9, rB, PPCSTATE_OFF(PowerPC::ppcState, msr)); // STR rB in to rA
|
||||
|
||||
LDR(rA, R9, PPCSTATE_OFF(PowerPC::ppcState, spr[SPR_SRR0]));
|
||||
|
||||
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)
|
||||
{
|
||||
ARMReg rA = gpr.GetReg(false);
|
||||
u32 Jumpto = js.compilerPC + 4;
|
||||
MOVI2R(rA, Jumpto);
|
||||
STR(R9, rA, PPCSTATE_OFF(PowerPC::ppcState, spr[SPR_LR]));
|
||||
//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
|
||||
{
|
||||
LDR(rB, R9, PPCSTATE_OFF(PowerPC::ppcState, spr[SPR_CTR]));
|
||||
SUBS(rB, rB, 1);
|
||||
STR(R9, rB, PPCSTATE_OFF(PowerPC::ppcState, spr[SPR_CTR]));
|
||||
|
||||
//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, PPCSTATE_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
|
||||
}
|
||||
if (inst.LK)
|
||||
{
|
||||
u32 Jumpto = js.compilerPC + 4;
|
||||
MOVI2R(rB, Jumpto);
|
||||
STR(R9, rB, PPCSTATE_OFF(PowerPC::ppcState, spr[SPR_LR]));
|
||||
//ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4); // Careful, destroys R14, R12
|
||||
}
|
||||
gpr.Unlock(rA, rB);
|
||||
|
||||
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;
|
||||
ARMReg rA = gpr.GetReg();
|
||||
ARMReg rB = gpr.GetReg();
|
||||
|
||||
if(inst.LK_3)
|
||||
{
|
||||
u32 Jumpto = js.compilerPC + 4;
|
||||
MOVI2R(rA, Jumpto);
|
||||
STR(R9, rA, PPCSTATE_OFF(PowerPC::ppcState, spr[SPR_LR]));
|
||||
// ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4);
|
||||
}
|
||||
MVN(rB, 0x3); // 0xFFFFFFFC
|
||||
LDR(rA, R9, PPCSTATE_OFF(PowerPC::ppcState, spr[SPR_CTR]));
|
||||
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, PPCSTATE_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);
|
||||
|
||||
LDR(rA, R9, PPCSTATE_OFF(PowerPC::ppcState, spr[SPR_CTR]));
|
||||
MVN(rB, 0x3); // 0xFFFFFFFC
|
||||
AND(rA, rA, rB);
|
||||
|
||||
if (inst.LK_3){
|
||||
u32 Jumpto = js.compilerPC + 4;
|
||||
MOVI2R(rB, Jumpto);
|
||||
STR(R9, rB, PPCSTATE_OFF(PowerPC::ppcState, spr[SPR_LR]));
|
||||
//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)
|
||||
{
|
||||
ARMReg rA = gpr.GetReg(false);
|
||||
u32 Jumpto = js.compilerPC + 4;
|
||||
MOVI2R(rA, Jumpto);
|
||||
STR(R9, rA, PPCSTATE_OFF(PowerPC::ppcState, spr[SPR_LR]));
|
||||
// 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
|
||||
{
|
||||
LDR(rB, R9, PPCSTATE_OFF(PowerPC::ppcState, spr[SPR_CTR]));
|
||||
SUBS(rB, rB, 1);
|
||||
STR(R9, rB, PPCSTATE_OFF(PowerPC::ppcState, spr[SPR_CTR]));
|
||||
|
||||
//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, PPCSTATE_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));
|
||||
MVN(rB, 0x3); // 0xFFFFFFFC
|
||||
LDR(rA, R9, PPCSTATE_OFF(PowerPC::ppcState, spr[SPR_LR]));
|
||||
AND(rA, rA, rB);
|
||||
if (inst.LK){
|
||||
u32 Jumpto = js.compilerPC + 4;
|
||||
MOVI2R(rB, Jumpto);
|
||||
STR(R9, rB, PPCSTATE_OFF(PowerPC::ppcState, spr[SPR_LR]));
|
||||
//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, PPCSTATE_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, PPCSTATE_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, PPCSTATE_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, PPCSTATE_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, PPCSTATE_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, PPCSTATE_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, PPCSTATE_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, PPCSTATE_OFF(PowerPC::ppcState, Exceptions));
|
||||
CMP(rA, EXCEPTION_DSI);
|
||||
FixupBranch DoNotLoad = B_CC(CC_EQ);
|
||||
#if FASTMEM
|
||||
// Backpatch route
|
||||
// Gets loaded in to RD
|
||||
// Address is in R10
|
||||
gpr.Unlock(rA, rB);
|
||||
if (inst.RA)
|
||||
{
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ADD(R10, RA, RB); // - 4
|
||||
}
|
||||
else
|
||||
MOV(R10, RB); // -4
|
||||
LoadToReg(RD, R10, 32, 0);
|
||||
#else
|
||||
if (inst.RA)
|
||||
{
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ADD(rB, RA, RB);
|
||||
}
|
||||
else
|
||||
MOV(rB, RB);
|
||||
|
||||
MOVI2R(rA, (u32)&Memory::Read_U32);
|
||||
PUSH(4, R0, R1, R2, R3);
|
||||
MOV(R0, rB);
|
||||
BL(rA);
|
||||
MOV(rA, R0);
|
||||
POP(4, R0, R1, R2, R3);
|
||||
MOV(RD, rA);
|
||||
gpr.Unlock(rA, rB);
|
||||
#endif
|
||||
SetJumpTarget(DoNotLoad);
|
||||
//// u32 temp = Memory::Read_U32(_inst.RA ? (m_GPR[_inst.RA] + m_GPR[_inst.RB]) : m_GPR[_inst.RB]);
|
||||
}
|
||||
void JitArm::icbi(UGeckoInstruction inst)
|
||||
{
|
||||
Default(inst);
|
||||
WriteExit(js.compilerPC + 4, 0);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "Common.h"
|
||||
#include "Thunk.h"
|
||||
|
||||
#include "../../Core.h"
|
||||
#include "../PowerPC.h"
|
||||
#include "../../ConfigManager.h"
|
||||
#include "../../CoreTiming.h"
|
||||
#include "../PPCTables.h"
|
||||
#include "ArmEmitter.h"
|
||||
#include "../../HW/Memmap.h"
|
||||
|
||||
|
||||
#include "Jit.h"
|
||||
#include "JitRegCache.h"
|
||||
#include "JitFPRCache.h"
|
||||
#include "JitAsm.h"
|
||||
|
||||
void JitArm::lfs(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(LoadStoreFloating)
|
||||
Default(inst); return;
|
||||
|
||||
ARMReg rA = gpr.GetReg();
|
||||
ARMReg rB = gpr.GetReg();
|
||||
LDR(rA, R9, PPCSTATE_OFF(PowerPC::ppcState, Exceptions));
|
||||
CMP(rA, EXCEPTION_DSI);
|
||||
FixupBranch DoNotLoad = B_CC(CC_EQ);
|
||||
|
||||
if (inst.RA)
|
||||
{
|
||||
MOVI2R(rB, inst.SIMM_16);
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ADD(rB, rB, RA);
|
||||
}
|
||||
else
|
||||
MOVI2R(rB, (u32)inst.SIMM_16);
|
||||
|
||||
MOVI2R(rA, (u32)&Memory::Read_U32);
|
||||
PUSH(4, R0, R1, R2, R3);
|
||||
MOV(R0, rB);
|
||||
BL(rA);
|
||||
MOV(rA, R0);
|
||||
POP(4, R0, R1, R2, R3);
|
||||
|
||||
ARMReg v0 = fpr.R0(inst.FD, false);
|
||||
ARMReg v1 = fpr.R1(inst.FD, false);
|
||||
|
||||
VMOV(v0, rA, false);
|
||||
VMOV(v1, rA, false);
|
||||
|
||||
gpr.Unlock(rA, rB);
|
||||
SetJumpTarget(DoNotLoad);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
#include "Common.h"
|
||||
#include "Thunk.h"
|
||||
|
||||
#include "../../Core.h"
|
||||
#include "../PowerPC.h"
|
||||
#include "../../CoreTiming.h"
|
||||
#include "../PPCTables.h"
|
||||
#include "ArmEmitter.h"
|
||||
|
||||
#include "Jit.h"
|
||||
#include "JitRegCache.h"
|
||||
#include "JitAsm.h"
|
||||
|
||||
void JitArm::mtspr(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(SystemRegisters)
|
||||
|
||||
u32 iIndex = (inst.SPRU << 5) | (inst.SPRL & 0x1F);
|
||||
ARMReg RD = gpr.R(inst.RD);
|
||||
|
||||
switch (iIndex)
|
||||
{
|
||||
case SPR_LR:
|
||||
case SPR_CTR:
|
||||
case SPR_XER:
|
||||
// These are safe to do the easy way, see the bottom of this function.
|
||||
break;
|
||||
|
||||
case SPR_GQR0:
|
||||
case SPR_GQR0 + 1:
|
||||
case SPR_GQR0 + 2:
|
||||
case SPR_GQR0 + 3:
|
||||
case SPR_GQR0 + 4:
|
||||
case SPR_GQR0 + 5:
|
||||
case SPR_GQR0 + 6:
|
||||
case SPR_GQR0 + 7:
|
||||
// Prevent recompiler from compiling in old quantizer values.
|
||||
// If the value changed, destroy all blocks using this quantizer
|
||||
// This will create a little bit of block churn, but hopefully not too bad.
|
||||
{
|
||||
/*
|
||||
MOV(32, R(EAX), M(&PowerPC::ppcState.spr[iIndex])); // Load old value
|
||||
CMP(32, R(EAX), gpr.R(inst.RD));
|
||||
FixupBranch skip_destroy = J_CC(CC_E, false);
|
||||
int gqr = iIndex - SPR_GQR0;
|
||||
ABI_CallFunctionC(ProtectFunction(&Jit64::DestroyBlocksWithFlag, 1), (u32)BLOCK_USE_GQR0 << gqr);
|
||||
SetJumpTarget(skip_destroy);*/
|
||||
}
|
||||
// TODO - break block if quantizers are written to.
|
||||
default:
|
||||
Default(inst);
|
||||
return;
|
||||
}
|
||||
|
||||
// OK, this is easy.
|
||||
STR(R9, RD, PPCSTATE_OFF(PowerPC::ppcState, spr) + iIndex * 4);
|
||||
}
|
||||
|
||||
void JitArm::mfspr(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(SystemRegisters)
|
||||
|
||||
u32 iIndex = (inst.SPRU << 5) | (inst.SPRL & 0x1F);
|
||||
ARMReg RD = gpr.R(inst.RD);
|
||||
switch (iIndex)
|
||||
{
|
||||
case SPR_WPAR:
|
||||
case SPR_DEC:
|
||||
case SPR_TL:
|
||||
case SPR_TU:
|
||||
Default(inst);
|
||||
return;
|
||||
default:
|
||||
LDR(RD, R9, PPCSTATE_OFF(PowerPC::ppcState, spr) + iIndex * 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
void JitArm::mtmsr(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
// Don't interpret this, if we do we get thrown out
|
||||
//JITDISABLE(SystemRegisters)
|
||||
|
||||
STR(R9, gpr.R(inst.RS), PPCSTATE_OFF(PowerPC::ppcState, msr));
|
||||
WriteExit(js.compilerPC + 4, 0);
|
||||
}
|
||||
@@ -0,0 +1,502 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "Jit.h"
|
||||
#include "../JitInterface.h"
|
||||
#include "JitArm_Tables.h"
|
||||
|
||||
// Should be moved in to the Jit class
|
||||
typedef void (JitArm::*_Instruction) (UGeckoInstruction instCode);
|
||||
|
||||
static _Instruction dynaOpTable[64];
|
||||
static _Instruction dynaOpTable4[1024];
|
||||
static _Instruction dynaOpTable19[1024];
|
||||
static _Instruction dynaOpTable31[1024];
|
||||
static _Instruction dynaOpTable59[32];
|
||||
static _Instruction dynaOpTable63[1024];
|
||||
|
||||
void JitArm::DynaRunTable4(UGeckoInstruction _inst) {(this->*dynaOpTable4 [_inst.SUBOP10])(_inst);}
|
||||
void JitArm::DynaRunTable19(UGeckoInstruction _inst) {(this->*dynaOpTable19[_inst.SUBOP10])(_inst);}
|
||||
void JitArm::DynaRunTable31(UGeckoInstruction _inst) {(this->*dynaOpTable31[_inst.SUBOP10])(_inst);}
|
||||
void JitArm::DynaRunTable59(UGeckoInstruction _inst) {(this->*dynaOpTable59[_inst.SUBOP5 ])(_inst);}
|
||||
void JitArm::DynaRunTable63(UGeckoInstruction _inst) {(this->*dynaOpTable63[_inst.SUBOP10])(_inst);}
|
||||
|
||||
struct GekkoOPTemplate
|
||||
{
|
||||
int opcode;
|
||||
_Instruction Inst;
|
||||
//GekkoOPInfo opinfo; // Doesn't need opinfo, Interpreter fills it out
|
||||
};
|
||||
|
||||
static GekkoOPTemplate primarytable[] =
|
||||
{
|
||||
{4, &JitArm::DynaRunTable4}, //"RunTable4", OPTYPE_SUBTABLE | (4<<24), 0}},
|
||||
{19, &JitArm::DynaRunTable19}, //"RunTable19", OPTYPE_SUBTABLE | (19<<24), 0}},
|
||||
{31, &JitArm::DynaRunTable31}, //"RunTable31", OPTYPE_SUBTABLE | (31<<24), 0}},
|
||||
{59, &JitArm::DynaRunTable59}, //"RunTable59", OPTYPE_SUBTABLE | (59<<24), 0}},
|
||||
{63, &JitArm::DynaRunTable63}, //"RunTable63", OPTYPE_SUBTABLE | (63<<24), 0}},
|
||||
|
||||
{16, &JitArm::bcx}, //"bcx", OPTYPE_SYSTEM, FL_ENDBLOCK}},
|
||||
{18, &JitArm::bx}, //"bx", OPTYPE_SYSTEM, FL_ENDBLOCK}},
|
||||
|
||||
{1, &JitArm::HLEFunction}, //"HLEFunction", OPTYPE_SYSTEM, FL_ENDBLOCK}},
|
||||
{2, &JitArm::Default}, //"DynaBlock", OPTYPE_SYSTEM, 0}},
|
||||
{3, &JitArm::Break}, //"twi", OPTYPE_SYSTEM, FL_ENDBLOCK}},
|
||||
{17, &JitArm::sc}, //"sc", OPTYPE_SYSTEM, FL_ENDBLOCK, 1}},
|
||||
|
||||
{7, &JitArm::mulli}, //"mulli", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_RC_BIT, 2}},
|
||||
{8, &JitArm::Default}, //"subfic", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_SET_CA}},
|
||||
{10, &JitArm::cmpli}, //"cmpli", OPTYPE_INTEGER, FL_IN_A | FL_SET_CRn}},
|
||||
{11, &JitArm::cmpi}, //"cmpi", OPTYPE_INTEGER, FL_IN_A | FL_SET_CRn}},
|
||||
{12, &JitArm::Default}, //"addic", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_SET_CA}},
|
||||
{13, &JitArm::Default}, //"addic_rc", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_SET_CR0}},
|
||||
{14, &JitArm::addi}, //"addi", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A0}},
|
||||
{15, &JitArm::addis}, //"addis", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A0}},
|
||||
|
||||
{20, &JitArm::rlwimix}, //"rlwimix", OPTYPE_INTEGER, FL_OUT_A | FL_IN_A | FL_IN_S | FL_RC_BIT}},
|
||||
{21, &JitArm::rlwinmx}, //"rlwinmx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_RC_BIT}},
|
||||
{23, &JitArm::Default}, //"rlwnmx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_IN_B | FL_RC_BIT}},
|
||||
|
||||
{24, &JitArm::ori}, //"ori", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S}},
|
||||
{25, &JitArm::oris}, //"oris", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S}},
|
||||
{26, &JitArm::Default}, //"xori", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S}},
|
||||
{27, &JitArm::Default}, //"xoris", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S}},
|
||||
{28, &JitArm::Default}, //"andi_rc", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_SET_CR0}},
|
||||
{29, &JitArm::Default}, //"andis_rc", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_SET_CR0}},
|
||||
|
||||
{32, &JitArm::lwz}, //"lwz", OPTYPE_LOAD, FL_OUT_D | FL_IN_A}},
|
||||
{33, &JitArm::Default}, //"lwzu", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A}},
|
||||
{34, &JitArm::lbz}, //"lbz", OPTYPE_LOAD, FL_OUT_D | FL_IN_A}},
|
||||
{35, &JitArm::Default}, //"lbzu", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A}},
|
||||
{40, &JitArm::lhz}, //"lhz", OPTYPE_LOAD, FL_OUT_D | FL_IN_A}},
|
||||
{41, &JitArm::Default}, //"lhzu", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A}},
|
||||
{42, &JitArm::Default}, //"lha", OPTYPE_LOAD, FL_OUT_D | FL_IN_A}},
|
||||
{43, &JitArm::Default}, //"lhau", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A}},
|
||||
|
||||
{44, &JitArm::Default}, //"sth", OPTYPE_STORE, FL_IN_A | FL_IN_S}},
|
||||
{45, &JitArm::Default}, //"sthu", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_S}},
|
||||
{36, &JitArm::stw}, //"stw", OPTYPE_STORE, FL_IN_A | FL_IN_S}},
|
||||
{37, &JitArm::stwu}, //"stwu", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_S}},
|
||||
{38, &JitArm::Default}, //"stb", OPTYPE_STORE, FL_IN_A | FL_IN_S}},
|
||||
{39, &JitArm::Default}, //"stbu", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_S}},
|
||||
|
||||
{46, &JitArm::Default}, //"lmw", OPTYPE_SYSTEM, FL_EVIL, 10}},
|
||||
{47, &JitArm::Default}, //"stmw", OPTYPE_SYSTEM, FL_EVIL, 10}},
|
||||
|
||||
{48, &JitArm::lfs}, //"lfs", OPTYPE_LOADFP, FL_IN_A}},
|
||||
{49, &JitArm::Default}, //"lfsu", OPTYPE_LOADFP, FL_OUT_A | FL_IN_A}},
|
||||
{50, &JitArm::Default}, //"lfd", OPTYPE_LOADFP, FL_IN_A}},
|
||||
{51, &JitArm::Default}, //"lfdu", OPTYPE_LOADFP, FL_OUT_A | FL_IN_A}},
|
||||
|
||||
{52, &JitArm::Default}, //"stfs", OPTYPE_STOREFP, FL_IN_A}},
|
||||
{53, &JitArm::Default}, //"stfsu", OPTYPE_STOREFP, FL_OUT_A | FL_IN_A}},
|
||||
{54, &JitArm::Default}, //"stfd", OPTYPE_STOREFP, FL_IN_A}},
|
||||
{55, &JitArm::Default}, //"stfdu", OPTYPE_STOREFP, FL_OUT_A | FL_IN_A}},
|
||||
|
||||
{56, &JitArm::Default}, //"psq_l", OPTYPE_PS, FL_IN_A}},
|
||||
{57, &JitArm::Default}, //"psq_lu", OPTYPE_PS, FL_OUT_A | FL_IN_A}},
|
||||
{60, &JitArm::Default}, //"psq_st", OPTYPE_PS, FL_IN_A}},
|
||||
{61, &JitArm::Default}, //"psq_stu", OPTYPE_PS, FL_OUT_A | FL_IN_A}},
|
||||
|
||||
//missing: 0, 5, 6, 9, 22, 30, 62, 58
|
||||
{0, &JitArm::Default}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}},
|
||||
{5, &JitArm::Default}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}},
|
||||
{6, &JitArm::Default}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}},
|
||||
{9, &JitArm::Default}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}},
|
||||
{22, &JitArm::Default}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}},
|
||||
{30, &JitArm::Default}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}},
|
||||
{62, &JitArm::Default}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}},
|
||||
{58, &JitArm::Default}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}},
|
||||
};
|
||||
|
||||
static GekkoOPTemplate table4[] =
|
||||
{ //SUBOP10
|
||||
{0, &JitArm::Default}, //"ps_cmpu0", OPTYPE_PS, FL_SET_CRn}},
|
||||
{32, &JitArm::Default}, //"ps_cmpo0", OPTYPE_PS, FL_SET_CRn}},
|
||||
{40, &JitArm::Default}, //"ps_neg", OPTYPE_PS, FL_RC_BIT}},
|
||||
{136, &JitArm::Default}, //"ps_nabs", OPTYPE_PS, FL_RC_BIT}},
|
||||
{264, &JitArm::Default}, //"ps_abs", OPTYPE_PS, FL_RC_BIT}},
|
||||
{64, &JitArm::Default}, //"ps_cmpu1", OPTYPE_PS, FL_RC_BIT}},
|
||||
{72, &JitArm::Default}, //"ps_mr", OPTYPE_PS, FL_RC_BIT}},
|
||||
{96, &JitArm::Default}, //"ps_cmpo1", OPTYPE_PS, FL_RC_BIT}},
|
||||
{528, &JitArm::Default}, //"ps_merge00", OPTYPE_PS, FL_RC_BIT}},
|
||||
{560, &JitArm::Default}, //"ps_merge01", OPTYPE_PS, FL_RC_BIT}},
|
||||
{592, &JitArm::Default}, //"ps_merge10", OPTYPE_PS, FL_RC_BIT}},
|
||||
{624, &JitArm::Default}, //"ps_merge11", OPTYPE_PS, FL_RC_BIT}},
|
||||
|
||||
{1014, &JitArm::Default}, //"dcbz_l", OPTYPE_SYSTEM, 0}},
|
||||
};
|
||||
|
||||
static GekkoOPTemplate table4_2[] =
|
||||
{
|
||||
{10, &JitArm::Default}, //"ps_sum0", OPTYPE_PS, 0}},
|
||||
{11, &JitArm::Default}, //"ps_sum1", OPTYPE_PS, 0}},
|
||||
{12, &JitArm::Default}, //"ps_muls0", OPTYPE_PS, 0}},
|
||||
{13, &JitArm::Default}, //"ps_muls1", OPTYPE_PS, 0}},
|
||||
{14, &JitArm::Default}, //"ps_madds0", OPTYPE_PS, 0}},
|
||||
{15, &JitArm::Default}, //"ps_madds1", OPTYPE_PS, 0}},
|
||||
{18, &JitArm::Default}, //"ps_div", OPTYPE_PS, 0, 16}},
|
||||
{20, &JitArm::Default}, //"ps_sub", OPTYPE_PS, 0}},
|
||||
{21, &JitArm::Default}, //"ps_add", OPTYPE_PS, 0}},
|
||||
{23, &JitArm::Default}, //"ps_sel", OPTYPE_PS, 0}},
|
||||
{24, &JitArm::Default}, //"ps_res", OPTYPE_PS, 0}},
|
||||
{25, &JitArm::Default}, //"ps_mul", OPTYPE_PS, 0}},
|
||||
{26, &JitArm::Default}, //"ps_rsqrte", OPTYPE_PS, 0, 1}},
|
||||
{28, &JitArm::Default}, //"ps_msub", OPTYPE_PS, 0}},
|
||||
{29, &JitArm::Default}, //"ps_madd", OPTYPE_PS, 0}},
|
||||
{30, &JitArm::Default}, //"ps_nmsub", OPTYPE_PS, 0}},
|
||||
{31, &JitArm::Default}, //"ps_nmadd", OPTYPE_PS, 0}},
|
||||
};
|
||||
|
||||
|
||||
static GekkoOPTemplate table4_3[] =
|
||||
{
|
||||
{6, &JitArm::Default}, //"psq_lx", OPTYPE_PS, 0}},
|
||||
{7, &JitArm::Default}, //"psq_stx", OPTYPE_PS, 0}},
|
||||
{38, &JitArm::Default}, //"psq_lux", OPTYPE_PS, 0}},
|
||||
{39, &JitArm::Default}, //"psq_stux", OPTYPE_PS, 0}},
|
||||
};
|
||||
|
||||
static GekkoOPTemplate table19[] =
|
||||
{
|
||||
{528, &JitArm::bcctrx}, //"bcctrx", OPTYPE_BRANCH, FL_ENDBLOCK}},
|
||||
{16, &JitArm::bclrx}, //"bclrx", OPTYPE_BRANCH, FL_ENDBLOCK}},
|
||||
{257, &JitArm::Default}, //"crand", OPTYPE_CR, FL_EVIL}},
|
||||
{129, &JitArm::Default}, //"crandc", OPTYPE_CR, FL_EVIL}},
|
||||
{289, &JitArm::Default}, //"creqv", OPTYPE_CR, FL_EVIL}},
|
||||
{225, &JitArm::Default}, //"crnand", OPTYPE_CR, FL_EVIL}},
|
||||
{33, &JitArm::Default}, //"crnor", OPTYPE_CR, FL_EVIL}},
|
||||
{449, &JitArm::Default}, //"cror", OPTYPE_CR, FL_EVIL}},
|
||||
{417, &JitArm::Default}, //"crorc", OPTYPE_CR, FL_EVIL}},
|
||||
{193, &JitArm::Default}, //"crxor", OPTYPE_CR, FL_EVIL}},
|
||||
|
||||
{150, &JitArm::DoNothing}, //"isync", OPTYPE_ICACHE, FL_EVIL}},
|
||||
{0, &JitArm::Default}, //"mcrf", OPTYPE_SYSTEM, FL_EVIL}},
|
||||
|
||||
{50, &JitArm::rfi}, //"rfi", OPTYPE_SYSTEM, FL_ENDBLOCK | FL_CHECKEXCEPTIONS, 1}},
|
||||
{18, &JitArm::Break}, //"rfid", OPTYPE_SYSTEM, FL_ENDBLOCK | FL_CHECKEXCEPTIONS}}
|
||||
};
|
||||
|
||||
|
||||
static GekkoOPTemplate table31[] =
|
||||
{
|
||||
{28, &JitArm::Default}, //"andx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
|
||||
{60, &JitArm::Default}, //"andcx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
|
||||
{444, &JitArm::orx}, //"orx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
|
||||
{124, &JitArm::Default}, //"norx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
|
||||
{316, &JitArm::Default}, //"xorx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
|
||||
{412, &JitArm::Default}, //"orcx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
|
||||
{476, &JitArm::Default}, //"nandx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
|
||||
{284, &JitArm::Default}, //"eqvx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
|
||||
{0, &JitArm::cmp}, //"cmp", OPTYPE_INTEGER, FL_IN_AB | FL_SET_CRn}},
|
||||
{32, &JitArm::Default}, //"cmpl", OPTYPE_INTEGER, FL_IN_AB | FL_SET_CRn}},
|
||||
{26, &JitArm::Default}, //"cntlzwx",OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_RC_BIT}},
|
||||
{922, &JitArm::extshx}, //"extshx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_RC_BIT}},
|
||||
{954, &JitArm::extsbx}, //"extsbx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_RC_BIT}},
|
||||
{536, &JitArm::Default}, //"srwx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}},
|
||||
{792, &JitArm::Default}, //"srawx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}},
|
||||
{824, &JitArm::Default}, //"srawix", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}},
|
||||
{24, &JitArm::Default}, //"slwx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}},
|
||||
|
||||
{54, &JitArm::Default}, //"dcbst", OPTYPE_DCACHE, 0, 4}},
|
||||
{86, &JitArm::Default}, //"dcbf", OPTYPE_DCACHE, 0, 4}},
|
||||
{246, &JitArm::Default}, //"dcbtst", OPTYPE_DCACHE, 0, 1}},
|
||||
{278, &JitArm::Default}, //"dcbt", OPTYPE_DCACHE, 0, 1}},
|
||||
{470, &JitArm::Default}, //"dcbi", OPTYPE_DCACHE, 0, 4}},
|
||||
{758, &JitArm::Default}, //"dcba", OPTYPE_DCACHE, 0, 4}},
|
||||
{1014, &JitArm::Default}, //"dcbz", OPTYPE_DCACHE, 0, 4}},
|
||||
|
||||
//load word
|
||||
{23, &JitArm::lwzx}, //"lwzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
|
||||
{55, &JitArm::Default}, //"lwzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}},
|
||||
|
||||
//load halfword
|
||||
{279, &JitArm::Default}, //"lhzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
|
||||
{311, &JitArm::Default}, //"lhzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}},
|
||||
|
||||
//load halfword signextend
|
||||
{343, &JitArm::Default}, //"lhax", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
|
||||
{375, &JitArm::Default}, //"lhaux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}},
|
||||
|
||||
//load byte
|
||||
{87, &JitArm::Default}, //"lbzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
|
||||
{119, &JitArm::Default}, //"lbzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}},
|
||||
|
||||
//load byte reverse
|
||||
{534, &JitArm::Default}, //"lwbrx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
|
||||
{790, &JitArm::Default}, //"lhbrx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
|
||||
|
||||
// Conditional load/store (Wii SMP)
|
||||
{150, &JitArm::Default}, //"stwcxd", OPTYPE_STORE, FL_EVIL | FL_SET_CR0}},
|
||||
{20, &JitArm::Default}, //"lwarx", OPTYPE_LOAD, FL_EVIL | FL_OUT_D | FL_IN_A0B | FL_SET_CR0}},
|
||||
|
||||
//load string (interpret these)
|
||||
{533, &JitArm::Default}, //"lswx", OPTYPE_LOAD, FL_EVIL | FL_IN_A | FL_OUT_D}},
|
||||
{597, &JitArm::Default}, //"lswi", OPTYPE_LOAD, FL_EVIL | FL_IN_AB | FL_OUT_D}},
|
||||
|
||||
//store word
|
||||
{151, &JitArm::Default}, //"stwx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}},
|
||||
{183, &JitArm::Default}, //"stwux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B}},
|
||||
|
||||
//store halfword
|
||||
{407, &JitArm::Default}, //"sthx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}},
|
||||
{439, &JitArm::Default}, //"sthux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B}},
|
||||
|
||||
//store byte
|
||||
{215, &JitArm::Default}, //"stbx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}},
|
||||
{247, &JitArm::Default}, //"stbux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B}},
|
||||
|
||||
//store bytereverse
|
||||
{662, &JitArm::Default}, //"stwbrx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}},
|
||||
{918, &JitArm::Default}, //"sthbrx", OPTYPE_STORE, FL_IN_A | FL_IN_B}},
|
||||
|
||||
{661, &JitArm::Default}, //"stswx", OPTYPE_STORE, FL_EVIL}},
|
||||
{725, &JitArm::Default}, //"stswi", OPTYPE_STORE, FL_EVIL}},
|
||||
|
||||
// fp load/store
|
||||
{535, &JitArm::Default}, //"lfsx", OPTYPE_LOADFP, FL_IN_A0 | FL_IN_B}},
|
||||
{567, &JitArm::Default}, //"lfsux", OPTYPE_LOADFP, FL_IN_A | FL_IN_B}},
|
||||
{599, &JitArm::Default}, //"lfdx", OPTYPE_LOADFP, FL_IN_A0 | FL_IN_B}},
|
||||
{631, &JitArm::Default}, //"lfdux", OPTYPE_LOADFP, FL_IN_A | FL_IN_B}},
|
||||
|
||||
{663, &JitArm::Default}, //"stfsx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B}},
|
||||
{695, &JitArm::Default}, //"stfsux", OPTYPE_STOREFP, FL_IN_A | FL_IN_B}},
|
||||
{727, &JitArm::Default}, //"stfdx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B}},
|
||||
{759, &JitArm::Default}, //"stfdux", OPTYPE_STOREFP, FL_IN_A | FL_IN_B}},
|
||||
{983, &JitArm::Default}, //"stfiwx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B}},
|
||||
|
||||
{19, &JitArm::Default}, //"mfcr", OPTYPE_SYSTEM, FL_OUT_D}},
|
||||
{83, &JitArm::Default}, //"mfmsr", OPTYPE_SYSTEM, FL_OUT_D}},
|
||||
{144, &JitArm::Default}, //"mtcrf", OPTYPE_SYSTEM, 0}},
|
||||
{146, &JitArm::mtmsr}, //"mtmsr", OPTYPE_SYSTEM, FL_ENDBLOCK}},
|
||||
{210, &JitArm::Default}, //"mtsr", OPTYPE_SYSTEM, 0}},
|
||||
{242, &JitArm::Default}, //"mtsrin", OPTYPE_SYSTEM, 0}},
|
||||
{339, &JitArm::mfspr}, //"mfspr", OPTYPE_SPR, FL_OUT_D}},
|
||||
{467, &JitArm::mtspr}, //"mtspr", OPTYPE_SPR, 0, 2}},
|
||||
{371, &JitArm::Default}, //"mftb", OPTYPE_SYSTEM, FL_OUT_D | FL_TIMER}},
|
||||
{512, &JitArm::Default}, //"mcrxr", OPTYPE_SYSTEM, 0}},
|
||||
{595, &JitArm::Default}, //"mfsr", OPTYPE_SYSTEM, FL_OUT_D, 2}},
|
||||
{659, &JitArm::Default}, //"mfsrin", OPTYPE_SYSTEM, FL_OUT_D, 2}},
|
||||
|
||||
{4, &JitArm::Break}, //"tw", OPTYPE_SYSTEM, FL_ENDBLOCK, 1}},
|
||||
{598, &JitArm::Default}, //"sync", OPTYPE_SYSTEM, 0, 2}},
|
||||
{982, &JitArm::icbi}, //"icbi", OPTYPE_SYSTEM, FL_ENDBLOCK, 3}},
|
||||
|
||||
// Unused instructions on GC
|
||||
{310, &JitArm::Default}, //"eciwx", OPTYPE_INTEGER, FL_RC_BIT}},
|
||||
{438, &JitArm::Default}, //"ecowx", OPTYPE_INTEGER, FL_RC_BIT}},
|
||||
{854, &JitArm::Default}, //"eieio", OPTYPE_INTEGER, FL_RC_BIT}},
|
||||
{306, &JitArm::Default}, //"tlbie", OPTYPE_SYSTEM, 0}},
|
||||
{370, &JitArm::Default}, //"tlbia", OPTYPE_SYSTEM, 0}},
|
||||
{566, &JitArm::Default}, //"tlbsync", OPTYPE_SYSTEM, 0}},
|
||||
};
|
||||
|
||||
static GekkoOPTemplate table31_2[] =
|
||||
{
|
||||
{266, &JitArm::addx}, //"addx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
|
||||
{778, &JitArm::addx}, //"addx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
|
||||
{10, &JitArm::Default}, //"addcx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT}},
|
||||
{138, &JitArm::Default}, //"addex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
|
||||
{234, &JitArm::Default}, //"addmex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
|
||||
{202, &JitArm::Default}, //"addzex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
|
||||
{491, &JitArm::Default}, //"divwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39}},
|
||||
{1003, &JitArm::Default}, //"divwox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39}},
|
||||
{459, &JitArm::Default}, //"divwux", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39}},
|
||||
{971, &JitArm::Default}, //"divwuox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39}},
|
||||
{75, &JitArm::Default}, //"mulhwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}},
|
||||
{11, &JitArm::Default}, //"mulhwux", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}},
|
||||
{235, &JitArm::Default}, //"mullwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}},
|
||||
{747, &JitArm::Default}, //"mullwox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}},
|
||||
{104, &JitArm::negx}, //"negx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
|
||||
{40, &JitArm::Default}, //"subfx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
|
||||
{552, &JitArm::Default}, //"subox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
|
||||
{8, &JitArm::Default}, //"subfcx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT}},
|
||||
{136, &JitArm::Default}, //"subfex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
|
||||
{232, &JitArm::Default}, //"subfmex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
|
||||
{200, &JitArm::Default}, //"subfzex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
|
||||
};
|
||||
|
||||
static GekkoOPTemplate table59[] =
|
||||
{
|
||||
{18, &JitArm::Default}, //{"fdivsx", OPTYPE_FPU, FL_RC_BIT_F, 16}},
|
||||
{20, &JitArm::Default}, //"fsubsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{21, &JitArm::Default}, //"faddsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
// {22, &JitArm::Default}, //"fsqrtsx", OPTYPE_FPU, FL_RC_BIT_F}}, // Not implemented on gekko
|
||||
{24, &JitArm::Default}, //"fresx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{25, &JitArm::Default}, //"fmulsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{28, &JitArm::Default}, //"fmsubsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{29, &JitArm::Default}, //"fmaddsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{30, &JitArm::Default}, //"fnmsubsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{31, &JitArm::Default}, //"fnmaddsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
};
|
||||
|
||||
static GekkoOPTemplate table63[] =
|
||||
{
|
||||
{264, &JitArm::fabsx}, //"fabsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{32, &JitArm::Default}, //"fcmpo", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{0, &JitArm::Default}, //"fcmpu", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{14, &JitArm::Default}, //"fctiwx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{15, &JitArm::Default}, //"fctiwzx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{72, &JitArm::fmrx}, //"fmrx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{136, &JitArm::Default}, //"fnabsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{40, &JitArm::Default}, //"fnegx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{12, &JitArm::Default}, //"frspx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
|
||||
{64, &JitArm::Default}, //"mcrfs", OPTYPE_SYSTEMFP, 0}},
|
||||
{583, &JitArm::Default}, //"mffsx", OPTYPE_SYSTEMFP, 0}},
|
||||
{70, &JitArm::Default}, //"mtfsb0x", OPTYPE_SYSTEMFP, 0, 2}},
|
||||
{38, &JitArm::Default}, //"mtfsb1x", OPTYPE_SYSTEMFP, 0, 2}},
|
||||
{134, &JitArm::Default}, //"mtfsfix", OPTYPE_SYSTEMFP, 0, 2}},
|
||||
{711, &JitArm::Default}, //"mtfsfx", OPTYPE_SYSTEMFP, 0, 2}},
|
||||
};
|
||||
|
||||
static GekkoOPTemplate table63_2[] =
|
||||
{
|
||||
{18, &JitArm::Default}, //"fdivx", OPTYPE_FPU, FL_RC_BIT_F, 30}},
|
||||
{20, &JitArm::Default}, //"fsubx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{21, &JitArm::faddx}, //"faddx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{22, &JitArm::Default}, //"fsqrtx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{23, &JitArm::Default}, //"fselx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{25, &JitArm::Default}, //"fmulx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{26, &JitArm::Default}, //"frsqrtex", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{28, &JitArm::Default}, //"fmsubx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{29, &JitArm::Default}, //"fmaddx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{30, &JitArm::Default}, //"fnmsubx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{31, &JitArm::Default}, //"fnmaddx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
};
|
||||
|
||||
|
||||
namespace JitArmTables
|
||||
{
|
||||
|
||||
void CompileInstruction(PPCAnalyst::CodeOp & op)
|
||||
{
|
||||
JitArm *jitarm = (JitArm *)jit;
|
||||
(jitarm->*dynaOpTable[op.inst.OPCD])(op.inst);
|
||||
GekkoOPInfo *info = op.opinfo;
|
||||
if (info) {
|
||||
#ifdef OPLOG
|
||||
if (!strcmp(info->opname, OP_TO_LOG)){ ///"mcrfs"
|
||||
rsplocations.push_back(jit.js.compilerPC);
|
||||
}
|
||||
#endif
|
||||
info->compileCount++;
|
||||
info->lastUse = jit->js.compilerPC;
|
||||
}
|
||||
}
|
||||
|
||||
void InitTables()
|
||||
{
|
||||
// once initialized, tables are read-only
|
||||
static bool initialized = false;
|
||||
if (initialized)
|
||||
return;
|
||||
|
||||
//clear
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
dynaOpTable59[i] = &JitArm::unknown_instruction;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 1024; i++)
|
||||
{
|
||||
dynaOpTable4 [i] = &JitArm::unknown_instruction;
|
||||
dynaOpTable19[i] = &JitArm::unknown_instruction;
|
||||
dynaOpTable31[i] = &JitArm::unknown_instruction;
|
||||
dynaOpTable63[i] = &JitArm::unknown_instruction;
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)(sizeof(primarytable) / sizeof(GekkoOPTemplate)); i++)
|
||||
{
|
||||
dynaOpTable[primarytable[i].opcode] = primarytable[i].Inst;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
int fill = i << 5;
|
||||
for (int j = 0; j < (int)(sizeof(table4_2) / sizeof(GekkoOPTemplate)); j++)
|
||||
{
|
||||
int op = fill+table4_2[j].opcode;
|
||||
dynaOpTable4[op] = table4_2[j].Inst;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
int fill = i << 6;
|
||||
for (int j = 0; j < (int)(sizeof(table4_3) / sizeof(GekkoOPTemplate)); j++)
|
||||
{
|
||||
int op = fill+table4_3[j].opcode;
|
||||
dynaOpTable4[op] = table4_3[j].Inst;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)(sizeof(table4) / sizeof(GekkoOPTemplate)); i++)
|
||||
{
|
||||
int op = table4[i].opcode;
|
||||
dynaOpTable4[op] = table4[i].Inst;
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)(sizeof(table31) / sizeof(GekkoOPTemplate)); i++)
|
||||
{
|
||||
int op = table31[i].opcode;
|
||||
dynaOpTable31[op] = table31[i].Inst;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 1; i++)
|
||||
{
|
||||
int fill = i << 9;
|
||||
for (int j = 0; j < (int)(sizeof(table31_2) / sizeof(GekkoOPTemplate)); j++)
|
||||
{
|
||||
int op = fill + table31_2[j].opcode;
|
||||
dynaOpTable31[op] = table31_2[j].Inst;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)(sizeof(table19) / sizeof(GekkoOPTemplate)); i++)
|
||||
{
|
||||
int op = table19[i].opcode;
|
||||
dynaOpTable19[op] = table19[i].Inst;
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)(sizeof(table59) / sizeof(GekkoOPTemplate)); i++)
|
||||
{
|
||||
int op = table59[i].opcode;
|
||||
dynaOpTable59[op] = table59[i].Inst;
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)(sizeof(table63) / sizeof(GekkoOPTemplate)); i++)
|
||||
{
|
||||
int op = table63[i].opcode;
|
||||
dynaOpTable63[op] = table63[i].Inst;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
int fill = i << 5;
|
||||
for (int j = 0; j < (int)(sizeof(table63_2) / sizeof(GekkoOPTemplate)); j++)
|
||||
{
|
||||
int op = fill + table63_2[j].opcode;
|
||||
dynaOpTable63[op] = table63_2[j].Inst;
|
||||
}
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef JITARM_TABLES_H
|
||||
#define JITARM_TABLES_H
|
||||
|
||||
#include "../Gekko.h"
|
||||
#include "../PPCTables.h"
|
||||
|
||||
namespace JitArmTables
|
||||
{
|
||||
void CompileInstruction(PPCAnalyst::CodeOp & op);
|
||||
void InitTables();
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,174 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
|
||||
#include "../../HW/Memmap.h"
|
||||
|
||||
#include "../PowerPC.h"
|
||||
#include "../../CoreTiming.h"
|
||||
#include "MemoryUtil.h"
|
||||
|
||||
#include "Jit.h"
|
||||
#include "../JitCommon/JitCache.h"
|
||||
|
||||
#include "../../HW/GPFifo.h"
|
||||
#include "../../Core.h"
|
||||
#include "JitAsm.h"
|
||||
#include "ArmEmitter.h"
|
||||
|
||||
using namespace ArmGen;
|
||||
|
||||
//TODO - make an option
|
||||
//#if _DEBUG
|
||||
// bool enableDebug = false;
|
||||
//#else
|
||||
// bool enableDebug = false;
|
||||
//#endif
|
||||
|
||||
JitArmAsmRoutineManager asm_routines;
|
||||
|
||||
void JitArmAsmRoutineManager::Generate()
|
||||
{
|
||||
enterCode = GetCodePtr();
|
||||
PUSH(2, R11, _LR); // R11 is frame pointer in Debug.
|
||||
|
||||
MOVI2R(R0, (u32)&CoreTiming::downcount);
|
||||
MOVI2R(R9, (u32)&PowerPC::ppcState.spr[0]);
|
||||
|
||||
FixupBranch skipToRealDispatcher = B();
|
||||
dispatcher = GetCodePtr();
|
||||
printf("Dispatcher is %p\n", dispatcher);
|
||||
|
||||
// Downcount Check
|
||||
// The result of slice decrementation should be in flags if somebody jumped here
|
||||
// IMPORTANT - We jump on negative, not carry!!!
|
||||
FixupBranch bail = B_CC(CC_MI);
|
||||
|
||||
SetJumpTarget(skipToRealDispatcher);
|
||||
dispatcherNoCheck = GetCodePtr();
|
||||
|
||||
// This block of code gets the address of the compiled block of code
|
||||
// It runs though to the compiling portion if it isn't found
|
||||
LDR(R12, R9, PPCSTATE_OFF(PowerPC::ppcState, pc));// Load the current PC into R12
|
||||
|
||||
MOVI2R(R14, JIT_ICACHE_MASK); // Potential for optimization
|
||||
AND(R12, R12, R14); // R12 contains PC & JIT_ICACHE_MASK here.
|
||||
// Confirmed good to this point 08-03-12
|
||||
|
||||
MOVI2R(R14, (u32)jit->GetBlockCache()->GetICache());
|
||||
// Confirmed That this loads the base iCache Location correctly 08-04-12
|
||||
|
||||
LDR(R12, R14, R12, true, true); // R12 contains iCache[PC & JIT_ICACHE_MASK] here
|
||||
// R12 Confirmed this is the correct iCache Location loaded.
|
||||
TST(R12, 0xFC); // Test to see if it is a JIT block.
|
||||
|
||||
SetCC(CC_EQ); // Only run next part if R12 is zero
|
||||
// Success, it is our Jitblock.
|
||||
MOVI2R(R14, (u32)jit->GetBlockCache()->GetCodePointers());
|
||||
// LDR R14 right here to get CodePointers()[0] pointer.
|
||||
REV(R12, R12); // Reversing this gives us our JITblock.
|
||||
LSL(R12, R12, 2); // Multiply by four because address locations are u32 in size
|
||||
LDR(R14, R14, R12, true, true); // Load the block address in to R14
|
||||
|
||||
B(R14);
|
||||
|
||||
FixupBranch NextBlock = B(); // Jump to end so we can start a new block
|
||||
SetCC(); // Return to always executing codes
|
||||
|
||||
// If we get to this point, that means that we don't have the block cached to execute
|
||||
// So call ArmJit to compile the block and then execute it.
|
||||
MOVI2R(R14, (u32)&Jit);
|
||||
LDR(R0, R9, PPCSTATE_OFF(PowerPC::ppcState, pc));
|
||||
BL(R14);
|
||||
|
||||
B(dispatcherNoCheck);
|
||||
|
||||
// fpException()
|
||||
// Floating Point Exception Check, Jumped to if false
|
||||
fpException = GetCodePtr();
|
||||
LDR(R0, R9, PPCSTATE_OFF(PowerPC::ppcState, Exceptions));
|
||||
ORR(R0, R0, EXCEPTION_FPU_UNAVAILABLE);
|
||||
STR(R9, R0, PPCSTATE_OFF(PowerPC::ppcState, Exceptions));
|
||||
QuickCallFunction(R14, (void*)&PowerPC::CheckExceptions);
|
||||
LDR(R0, R9, PPCSTATE_OFF(PowerPC::ppcState, npc));
|
||||
STR(R9, R0, PPCSTATE_OFF(PowerPC::ppcState, pc));
|
||||
B(dispatcher);
|
||||
|
||||
SetJumpTarget(bail);
|
||||
doTiming = GetCodePtr();
|
||||
// XXX: In JIT64, Advance() gets called /after/ the exception checking
|
||||
// once it jumps back to the start of outerLoop
|
||||
QuickCallFunction(R14, (void*)&CoreTiming::Advance);
|
||||
|
||||
// Does exception checking
|
||||
testExceptions = GetCodePtr();
|
||||
LDR(R0, R9, PPCSTATE_OFF(PowerPC::ppcState, pc));
|
||||
STR(R9, R0, PPCSTATE_OFF(PowerPC::ppcState, npc));
|
||||
QuickCallFunction(R14, (void*)&PowerPC::CheckExceptions);
|
||||
LDR(R0, R9, PPCSTATE_OFF(PowerPC::ppcState, npc));
|
||||
STR(R9, R0, PPCSTATE_OFF(PowerPC::ppcState, pc));
|
||||
// Check the state pointer to see if we are exiting
|
||||
// Gets checked on every exception check
|
||||
MOVI2R(R0, (u32)PowerPC::GetStatePtr());
|
||||
MVN(R1, 0);
|
||||
LDR(R0, R0);
|
||||
TST(R0, R1);
|
||||
FixupBranch Exit = B_CC(CC_NEQ);
|
||||
|
||||
SetJumpTarget(NextBlock);
|
||||
B(dispatcher);
|
||||
|
||||
SetJumpTarget(Exit);
|
||||
|
||||
POP(2, R11, _PC);
|
||||
FlushIcache();
|
||||
}
|
||||
|
||||
void JitArmAsmRoutineManager::GenerateCommon()
|
||||
{
|
||||
/* fifoDirectWrite8 = AlignCode4();
|
||||
GenFifoWrite(8);
|
||||
fifoDirectWrite16 = AlignCode4();
|
||||
GenFifoWrite(16);
|
||||
fifoDirectWrite32 = AlignCode4();
|
||||
GenFifoWrite(32);
|
||||
fifoDirectWriteFloat = AlignCode4();
|
||||
GenFifoFloatWrite();
|
||||
fifoDirectWriteXmm64 = AlignCode4();
|
||||
GenFifoXmm64Write();
|
||||
|
||||
GenQuantizedLoads();
|
||||
GenQuantizedStores();
|
||||
GenQuantizedSingleStores();
|
||||
*/
|
||||
//CMPSD(R(XMM0), M(&zero),
|
||||
// TODO
|
||||
|
||||
// Fast write routines - special case the most common hardware write
|
||||
// TODO: use this.
|
||||
// Even in x86, the param values will be in the right registers.
|
||||
/*
|
||||
const u8 *fastMemWrite8 = AlignCode16();
|
||||
CMP(32, R(ABI_PARAM2), Imm32(0xCC008000));
|
||||
FixupBranch skip_fast_write = J_CC(CC_NE, false);
|
||||
MOV(32, EAX, M(&m_gatherPipeCount));
|
||||
MOV(8, MDisp(EAX, (u32)&m_gatherPipe), ABI_PARAM1);
|
||||
ADD(32, 1, M(&m_gatherPipeCount));
|
||||
RET();
|
||||
SetJumpTarget(skip_fast_write);
|
||||
CALL((void *)&Memory::Write_U8);*/
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _JITARMASM_H
|
||||
#define _JITARMASM_H
|
||||
#include "ArmEmitter.h"
|
||||
#include "../JitCommon/JitAsmCommon.h"
|
||||
using namespace ArmGen;
|
||||
class JitArmAsmRoutineManager : public CommonAsmRoutinesBase, public ARMXCodeBlock
|
||||
{
|
||||
private:
|
||||
void Generate();
|
||||
void GenerateCommon();
|
||||
|
||||
public:
|
||||
void Init() {
|
||||
AllocCodeSpace(8192);
|
||||
Generate();
|
||||
WriteProtect();
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
FreeCodeSpace();
|
||||
}
|
||||
};
|
||||
|
||||
extern JitArmAsmRoutineManager asm_routines;
|
||||
|
||||
#endif // _JIT64ASM_H
|
||||
@@ -0,0 +1,169 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "Jit.h"
|
||||
#include "JitFPRCache.h"
|
||||
|
||||
ArmFPRCache::ArmFPRCache()
|
||||
{
|
||||
emit = 0;
|
||||
}
|
||||
|
||||
void ArmFPRCache::Init(ARMXEmitter *emitter)
|
||||
{
|
||||
emit = emitter;
|
||||
ARMReg *PPCRegs = GetPPCAllocationOrder(NUMPPCREG);
|
||||
ARMReg *Regs = GetAllocationOrder(NUMARMREG);
|
||||
|
||||
for(u8 a = 0; a < NUMPPCREG; ++a)
|
||||
{
|
||||
ArmCRegs[a].PPCReg = 33;
|
||||
ArmCRegs[a].Reg = PPCRegs[a];
|
||||
ArmCRegs[a].LastLoad = 0;
|
||||
ArmCRegs[a].PS1 = false;
|
||||
}
|
||||
for(u8 a = 0; a < NUMARMREG; ++a)
|
||||
{
|
||||
ArmRegs[a].Reg = Regs[a];
|
||||
ArmRegs[a].free = true;
|
||||
}
|
||||
}
|
||||
void ArmFPRCache::Start(PPCAnalyst::BlockRegStats &stats)
|
||||
{
|
||||
for(u8 a = 0; a < NUMPPCREG; ++a)
|
||||
{
|
||||
ArmCRegs[a].PPCReg = 33;
|
||||
ArmCRegs[a].LastLoad = 0;
|
||||
}
|
||||
}
|
||||
ARMReg *ArmFPRCache::GetPPCAllocationOrder(int &count)
|
||||
{
|
||||
// This will return us the allocation order of the registers we can use on
|
||||
// the ppc side.
|
||||
static ARMReg allocationOrder[] =
|
||||
{
|
||||
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
|
||||
};
|
||||
count = sizeof(allocationOrder) / sizeof(const int);
|
||||
return allocationOrder;
|
||||
}
|
||||
ARMReg *ArmFPRCache::GetAllocationOrder(int &count)
|
||||
{
|
||||
// This will return us the allocation order of the registers we can use on
|
||||
// the host side.
|
||||
static ARMReg allocationOrder[] =
|
||||
{
|
||||
D31, D30, D29, D28
|
||||
};
|
||||
count = sizeof(allocationOrder) / sizeof(const int);
|
||||
return allocationOrder;
|
||||
}
|
||||
|
||||
ARMReg ArmFPRCache::GetReg(bool AutoLock)
|
||||
{
|
||||
for(u8 a = 0; a < NUMARMREG; ++a)
|
||||
if(ArmRegs[a].free)
|
||||
{
|
||||
// Alright, this one is free
|
||||
if (AutoLock)
|
||||
ArmRegs[a].free = false;
|
||||
return ArmRegs[a].Reg;
|
||||
}
|
||||
// Uh Oh, we have all them locked....
|
||||
_assert_msg_(_DYNA_REC_, false, "All available registers are locked dumb dumb");
|
||||
return D31;
|
||||
}
|
||||
void ArmFPRCache::Unlock(ARMReg V0)
|
||||
{
|
||||
for(u8 RegNum = 0; RegNum < NUMARMREG; ++RegNum)
|
||||
{
|
||||
if(ArmRegs[RegNum].Reg == V0)
|
||||
{
|
||||
_assert_msg_(_DYNA_REC, !ArmRegs[RegNum].free, "This register is already unlocked");
|
||||
ArmRegs[RegNum].free = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
ARMReg ArmFPRCache::GetPPCReg(u32 preg, bool PS1, bool preLoad)
|
||||
{
|
||||
u32 HighestUsed = 0;
|
||||
u8 Num = 0;
|
||||
for(u8 a = 0; a < NUMPPCREG; ++a){
|
||||
++ArmCRegs[a].LastLoad;
|
||||
if (ArmCRegs[a].LastLoad > HighestUsed)
|
||||
{
|
||||
HighestUsed = ArmCRegs[a].LastLoad;
|
||||
Num = a;
|
||||
}
|
||||
}
|
||||
// Check if already Loaded
|
||||
for(u8 a = 0; a < NUMPPCREG; ++a)
|
||||
if (ArmCRegs[a].PPCReg == preg && ArmCRegs[a].PS1 == PS1)
|
||||
{
|
||||
ArmCRegs[a].LastLoad = 0;
|
||||
return ArmCRegs[a].Reg;
|
||||
}
|
||||
// Check if we have a free register
|
||||
for (u8 a = 0; a < NUMPPCREG; ++a)
|
||||
if (ArmCRegs[a].PPCReg == 33)
|
||||
{
|
||||
u16 offset = PPCSTATE_OFF(PowerPC::ppcState, ps) + (preg * 16) + (PS1 ? 8 : 0);
|
||||
if (preLoad)
|
||||
emit->VLDR(ArmCRegs[a].Reg, R9, offset);
|
||||
ArmCRegs[a].PPCReg = preg;
|
||||
ArmCRegs[a].LastLoad = 0;
|
||||
ArmCRegs[a].PS1 = PS1;
|
||||
return ArmCRegs[a].Reg;
|
||||
}
|
||||
// Alright, we couldn't get a free space, dump that least used register
|
||||
u16 offsetOld = PPCSTATE_OFF(PowerPC::ppcState, ps) + (ArmCRegs[Num].PPCReg * 16) + (ArmCRegs[Num].PS1 ? 8 : 0);
|
||||
emit->VSTR(ArmCRegs[Num].Reg, R9, offsetOld);
|
||||
|
||||
u16 offsetNew = PPCSTATE_OFF(PowerPC::ppcState, ps) + (preg * 16) + (PS1 ? 8 : 0);
|
||||
if (preLoad)
|
||||
emit->VLDR(ArmCRegs[Num].Reg, R9, offsetNew);
|
||||
ArmCRegs[Num].PPCReg = preg;
|
||||
ArmCRegs[Num].LastLoad = 0;
|
||||
ArmCRegs[Num].PS1 = PS1;
|
||||
return ArmCRegs[Num].Reg;
|
||||
|
||||
}
|
||||
|
||||
ARMReg ArmFPRCache::R0(u32 preg, bool preLoad)
|
||||
{
|
||||
return GetPPCReg(preg, false, preLoad);
|
||||
}
|
||||
|
||||
ARMReg ArmFPRCache::R1(u32 preg, bool preLoad)
|
||||
{
|
||||
return GetPPCReg(preg, true, preLoad);
|
||||
}
|
||||
|
||||
void ArmFPRCache::Flush()
|
||||
{
|
||||
for(u8 a = 0; a < NUMPPCREG; ++a)
|
||||
if (ArmCRegs[a].PPCReg != 33)
|
||||
{
|
||||
u16 offset = PPCSTATE_OFF(PowerPC::ppcState, ps) + (ArmCRegs[a].PPCReg * 16) + (ArmCRegs[a].PS1 ? 8 : 0);
|
||||
emit->VSTR(ArmCRegs[a].Reg, R9, offset);
|
||||
ArmCRegs[a].PPCReg = 33;
|
||||
ArmCRegs[a].LastLoad = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
// 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 _JITARMFPRCACHE_H
|
||||
#define _JITARMFPRCACHE_H
|
||||
|
||||
#include "ArmEmitter.h"
|
||||
#include "../Gekko.h"
|
||||
#include "../PPCAnalyst.h"
|
||||
#include "JitRegCache.h"
|
||||
|
||||
#define ARMFPUREGS 32
|
||||
using namespace ArmGen;
|
||||
|
||||
class ArmFPRCache
|
||||
{
|
||||
private:
|
||||
PPCCachedReg regs[32];
|
||||
JRCPPC ArmCRegs[ARMFPUREGS];
|
||||
JRCReg ArmRegs[ARMFPUREGS];
|
||||
|
||||
int NUMPPCREG;
|
||||
int NUMARMREG;
|
||||
|
||||
ARMReg *GetAllocationOrder(int &count);
|
||||
ARMReg *GetPPCAllocationOrder(int &count);
|
||||
|
||||
ARMReg GetPPCReg(u32 preg, bool PS1, bool preLoad);
|
||||
|
||||
protected:
|
||||
ARMXEmitter *emit;
|
||||
|
||||
public:
|
||||
ArmFPRCache();
|
||||
~ArmFPRCache() {}
|
||||
|
||||
void Init(ARMXEmitter *emitter);
|
||||
void Start(PPCAnalyst::BlockRegStats &stats);
|
||||
|
||||
void SetEmitter(ARMXEmitter *emitter) {emit = emitter;}
|
||||
|
||||
ARMReg GetReg(bool AutoLock = true); // Return a ARM register we can use.
|
||||
void Unlock(ARMReg V0);
|
||||
void Flush();
|
||||
ARMReg R0(u32 preg, bool preLoad = true); // Returns a cached register
|
||||
ARMReg R1(u32 preg, bool preLoad = true);
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,168 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "Jit.h"
|
||||
#include "JitRegCache.h"
|
||||
|
||||
ArmRegCache::ArmRegCache()
|
||||
{
|
||||
emit = 0;
|
||||
}
|
||||
|
||||
void ArmRegCache::Init(ARMXEmitter *emitter)
|
||||
{
|
||||
emit = emitter;
|
||||
ARMReg *PPCRegs = GetPPCAllocationOrder(NUMPPCREG);
|
||||
ARMReg *Regs = GetAllocationOrder(NUMARMREG);
|
||||
for(u8 a = 0; a < 32; ++a)
|
||||
{
|
||||
// This gives us the memory locations of the gpr registers so we can
|
||||
// load them.
|
||||
regs[a].location = (u8*)&PowerPC::ppcState.gpr[a];
|
||||
regs[a].UsesLeft = 0;
|
||||
}
|
||||
for(u8 a = 0; a < NUMPPCREG; ++a)
|
||||
{
|
||||
ArmCRegs[a].PPCReg = 33;
|
||||
ArmCRegs[a].Reg = PPCRegs[a];
|
||||
ArmCRegs[a].LastLoad = 0;
|
||||
}
|
||||
for(u8 a = 0; a < NUMARMREG; ++a)
|
||||
{
|
||||
ArmRegs[a].Reg = Regs[a];
|
||||
ArmRegs[a].free = true;
|
||||
}
|
||||
}
|
||||
void ArmRegCache::Start(PPCAnalyst::BlockRegStats &stats)
|
||||
{
|
||||
for(u8 a = 0; a < NUMPPCREG; ++a)
|
||||
{
|
||||
ArmCRegs[a].PPCReg = 33;
|
||||
ArmCRegs[a].LastLoad = 0;
|
||||
}
|
||||
for(u8 a = 0; a < 32; ++a)
|
||||
regs[a].UsesLeft = stats.GetTotalNumAccesses(a);
|
||||
}
|
||||
ARMReg *ArmRegCache::GetPPCAllocationOrder(int &count)
|
||||
{
|
||||
// This will return us the allocation order of the registers we can use on
|
||||
// the ppc side.
|
||||
static ARMReg allocationOrder[] =
|
||||
{
|
||||
R0, R1, R2, R3, R4, R5, R6, R7, R8
|
||||
};
|
||||
count = sizeof(allocationOrder) / sizeof(const int);
|
||||
return allocationOrder;
|
||||
}
|
||||
ARMReg *ArmRegCache::GetAllocationOrder(int &count)
|
||||
{
|
||||
// This will return us the allocation order of the registers we can use on
|
||||
// the host side.
|
||||
static ARMReg allocationOrder[] =
|
||||
{
|
||||
R14, R12, R11, R10
|
||||
};
|
||||
count = sizeof(allocationOrder) / sizeof(const int);
|
||||
return allocationOrder;
|
||||
}
|
||||
|
||||
ARMReg ArmRegCache::GetReg(bool AutoLock)
|
||||
{
|
||||
for(u8 a = 0; a < NUMARMREG; ++a)
|
||||
if(ArmRegs[a].free)
|
||||
{
|
||||
// Alright, this one is free
|
||||
if (AutoLock)
|
||||
ArmRegs[a].free = false;
|
||||
return ArmRegs[a].Reg;
|
||||
}
|
||||
// Uh Oh, we have all them locked....
|
||||
_assert_msg_(_DYNA_REC_, false, "All available registers are locked dumb dumb");
|
||||
return R0;
|
||||
}
|
||||
void ArmRegCache::Lock(ARMReg Reg)
|
||||
{
|
||||
for(u8 RegNum = 0; RegNum < NUMARMREG; ++RegNum)
|
||||
if(ArmRegs[RegNum].Reg == Reg)
|
||||
{
|
||||
_assert_msg_(_DYNA_REC, ArmRegs[RegNum].free, "This register is already locked");
|
||||
ArmRegs[RegNum].free = false;
|
||||
}
|
||||
_assert_msg_(_DYNA_REC, false, "Register %d can't be used with lock", Reg);
|
||||
}
|
||||
void ArmRegCache::Unlock(ARMReg R0, ARMReg R1, ARMReg R2, ARMReg R3)
|
||||
{
|
||||
for(u8 RegNum = 0; RegNum < NUMARMREG; ++RegNum)
|
||||
{
|
||||
if(ArmRegs[RegNum].Reg == R0)
|
||||
{
|
||||
_assert_msg_(_DYNA_REC, !ArmRegs[RegNum].free, "This register is already unlocked");
|
||||
ArmRegs[RegNum].free = true;
|
||||
}
|
||||
if( R1 != INVALID_REG && ArmRegs[RegNum].Reg == R1) ArmRegs[RegNum].free = true;
|
||||
if( R2 != INVALID_REG && ArmRegs[RegNum].Reg == R2) ArmRegs[RegNum].free = true;
|
||||
if( R3 != INVALID_REG && ArmRegs[RegNum].Reg == R3) ArmRegs[RegNum].free = true;
|
||||
}
|
||||
}
|
||||
|
||||
ARMReg ArmRegCache::R(u32 preg)
|
||||
{
|
||||
u32 HighestUsed = 0;
|
||||
u8 Num = 0;
|
||||
for(u8 a = 0; a < NUMPPCREG; ++a){
|
||||
++ArmCRegs[a].LastLoad;
|
||||
if (ArmCRegs[a].LastLoad > HighestUsed)
|
||||
{
|
||||
HighestUsed = ArmCRegs[a].LastLoad;
|
||||
Num = a;
|
||||
}
|
||||
}
|
||||
// Check if already Loaded
|
||||
for(u8 a = 0; a < NUMPPCREG; ++a)
|
||||
if (ArmCRegs[a].PPCReg == preg)
|
||||
{
|
||||
ArmCRegs[a].LastLoad = 0;
|
||||
return ArmCRegs[a].Reg;
|
||||
}
|
||||
// Check if we have a free register
|
||||
for (u8 a = 0; a < NUMPPCREG; ++a)
|
||||
if (ArmCRegs[a].PPCReg == 33)
|
||||
{
|
||||
emit->LDR(ArmCRegs[a].Reg, R9, PPCSTATE_OFF(PowerPC::ppcState, gpr) + preg * 4);
|
||||
ArmCRegs[a].PPCReg = preg;
|
||||
ArmCRegs[a].LastLoad = 0;
|
||||
return ArmCRegs[a].Reg;
|
||||
}
|
||||
// Alright, we couldn't get a free space, dump that least used register
|
||||
emit->STR(R9, ArmCRegs[Num].Reg, PPCSTATE_OFF(PowerPC::ppcState, gpr) + ArmCRegs[Num].PPCReg * 4);
|
||||
emit->LDR(ArmCRegs[Num].Reg, R9, PPCSTATE_OFF(PowerPC::ppcState, gpr) + preg * 4);
|
||||
ArmCRegs[Num].PPCReg = preg;
|
||||
ArmCRegs[Num].LastLoad = 0;
|
||||
return ArmCRegs[Num].Reg;
|
||||
}
|
||||
|
||||
void ArmRegCache::Flush()
|
||||
{
|
||||
for(u8 a = 0; a < NUMPPCREG; ++a)
|
||||
if (ArmCRegs[a].PPCReg != 33)
|
||||
{
|
||||
emit->STR(R9, ArmCRegs[a].Reg, PPCSTATE_OFF(PowerPC::ppcState, gpr) + ArmCRegs[a].PPCReg * 4);
|
||||
ArmCRegs[a].PPCReg = 33;
|
||||
ArmCRegs[a].LastLoad = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _JITARMREGCACHE_H
|
||||
#define _JITARMREGCACHE_H
|
||||
|
||||
#include "ArmEmitter.h"
|
||||
#include "../Gekko.h"
|
||||
#include "../PPCAnalyst.h"
|
||||
|
||||
using namespace ArmGen;
|
||||
// This ARM Register cache actually pre loads the most used registers before
|
||||
// the block to increase speed since every memory load requires two
|
||||
// instructions to load it. We are going to use R0-RMAX as registers for the
|
||||
// use of PPC Registers.
|
||||
// Allocation order as follows
|
||||
#define ARMREGS 16
|
||||
// Allocate R0 to R9 for PPC first.
|
||||
// For General registers on the host side, start with R14 and go down as we go
|
||||
// R13 is reserved for our stack pointer, don't ever use that. Unless you save
|
||||
// it
|
||||
// So we have R14, R12, R11, R10 to work with instructions
|
||||
|
||||
struct PPCCachedReg
|
||||
{
|
||||
const u8 *location;
|
||||
u32 UsesLeft;
|
||||
};
|
||||
struct JRCPPC
|
||||
{
|
||||
u32 PPCReg; // Tied to which PPC Register
|
||||
bool PS1;
|
||||
ARMReg Reg; // Tied to which ARM Register
|
||||
u32 LastLoad;
|
||||
};
|
||||
struct JRCReg
|
||||
{
|
||||
ARMReg Reg; // Which reg this is.
|
||||
bool free;
|
||||
};
|
||||
class ArmRegCache
|
||||
{
|
||||
private:
|
||||
PPCCachedReg regs[32];
|
||||
JRCPPC ArmCRegs[ARMREGS];
|
||||
JRCReg ArmRegs[ARMREGS]; // Four registers remaining
|
||||
|
||||
int NUMPPCREG;
|
||||
int NUMARMREG;
|
||||
|
||||
ARMReg *GetAllocationOrder(int &count);
|
||||
ARMReg *GetPPCAllocationOrder(int &count);
|
||||
|
||||
protected:
|
||||
ARMXEmitter *emit;
|
||||
|
||||
public:
|
||||
ArmRegCache();
|
||||
~ArmRegCache() {}
|
||||
|
||||
void Init(ARMXEmitter *emitter);
|
||||
void Start(PPCAnalyst::BlockRegStats &stats);
|
||||
|
||||
void SetEmitter(ARMXEmitter *emitter) {emit = emitter;}
|
||||
|
||||
ARMReg GetReg(bool AutoLock = true); // Return a ARM register we can use.
|
||||
void Lock(ARMReg reg);
|
||||
void Unlock(ARMReg R0, ARMReg R1 = INVALID_REG, ARMReg R2 = INVALID_REG, ARMReg R3 =
|
||||
INVALID_REG);
|
||||
void Flush();
|
||||
ARMReg R(u32 preg); // Returns a cached register
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -15,7 +15,7 @@
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
#include "Thunk.h"
|
||||
#include "CPUDetect.h"
|
||||
#include "x64Emitter.h"
|
||||
@@ -26,7 +26,7 @@
|
||||
#include "../../CoreTiming.h"
|
||||
#include "MemoryUtil.h"
|
||||
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
#include "../JitCommon/JitCache.h"
|
||||
|
||||
#include "../../HW/GPFifo.h"
|
||||
|
||||
@@ -21,16 +21,8 @@
|
||||
#include "../JitCommon/Jit_Util.h"
|
||||
#include "Thunk.h"
|
||||
|
||||
class CommonAsmRoutines : public EmuCodeBlock {
|
||||
protected:
|
||||
void GenQuantizedLoads();
|
||||
void GenQuantizedStores();
|
||||
void GenQuantizedSingleStores();
|
||||
|
||||
class CommonAsmRoutinesBase {
|
||||
public:
|
||||
void GenFifoWrite(int size);
|
||||
void GenFifoXmm64Write();
|
||||
void GenFifoFloatWrite();
|
||||
|
||||
const u8 *fifoDirectWrite8;
|
||||
const u8 *fifoDirectWrite16;
|
||||
@@ -72,8 +64,23 @@ public:
|
||||
// In: XMM0: Bottom 32-bit slot holds the float to be written.
|
||||
const u8 **singleStoreQuantized;
|
||||
|
||||
};
|
||||
|
||||
class CommonAsmRoutines : public CommonAsmRoutinesBase, public EmuCodeBlock
|
||||
{
|
||||
protected:
|
||||
void GenQuantizedLoads();
|
||||
void GenQuantizedStores();
|
||||
void GenQuantizedSingleStores();
|
||||
|
||||
public:
|
||||
void GenFifoWrite(int size);
|
||||
void GenFifoXmm64Write();
|
||||
void GenFifoFloatWrite();
|
||||
|
||||
private:
|
||||
ThunkManager thunks;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -25,7 +25,7 @@
|
||||
#include "../../HW/Memmap.h"
|
||||
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
#include "Thunk.h"
|
||||
#include "x64Analyzer.h"
|
||||
|
||||
@@ -160,7 +160,7 @@ const u8 *TrampolineCache::GetWriteTrampoline(const InstructionInfo &info)
|
||||
// 1) It's really necessary. We don't know anything about the context.
|
||||
// 2) It doesn't really hurt. Only instructions that access I/O will get these, and there won't be
|
||||
// that many of them in a typical program/game.
|
||||
const u8 *JitBase::BackPatch(u8 *codePtr, int accessType, u32 emAddress, void *ctx_void)
|
||||
const u8 *Jitx86Base::BackPatch(u8 *codePtr, int accessType, u32 emAddress, void *ctx_void)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
CONTEXT *ctx = (CONTEXT *)ctx_void;
|
||||
|
||||
@@ -34,6 +34,9 @@
|
||||
// from the real context.
|
||||
struct CONTEXT
|
||||
{
|
||||
#ifdef _M_ARM
|
||||
u32 reg_pc;
|
||||
#else
|
||||
#ifdef _M_X64
|
||||
u64 Rip;
|
||||
u64 Rax;
|
||||
@@ -41,6 +44,7 @@
|
||||
u32 Eip;
|
||||
u32 Eax;
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -32,12 +32,9 @@
|
||||
|
||||
#define JIT_OPCODE 0
|
||||
|
||||
class JitBase : public CPUCoreBase, public EmuCodeBlock
|
||||
class JitBase : public CPUCoreBase
|
||||
{
|
||||
protected:
|
||||
JitBlockCache blocks;
|
||||
TrampolineCache trampolines;
|
||||
|
||||
struct JitOptions
|
||||
{
|
||||
bool optimizeStack;
|
||||
@@ -85,14 +82,29 @@ public:
|
||||
// This should probably be removed from public:
|
||||
JitOptions jo;
|
||||
JitState js;
|
||||
|
||||
JitBlockCache *GetBlockCache() { return &blocks; }
|
||||
|
||||
virtual JitBaseBlockCache *GetBlockCache() = 0;
|
||||
|
||||
virtual void Jit(u32 em_address) = 0;
|
||||
|
||||
virtual const u8 *BackPatch(u8 *codePtr, int accessType, u32 em_address, void *ctx) = 0;
|
||||
|
||||
virtual const CommonAsmRoutinesBase *GetAsmRoutines() = 0;
|
||||
|
||||
virtual bool IsInCodeSpace(u8 *ptr) = 0;
|
||||
};
|
||||
|
||||
class Jitx86Base : public JitBase, public EmuCodeBlock
|
||||
{
|
||||
protected:
|
||||
JitBlockCache blocks;
|
||||
TrampolineCache trampolines;
|
||||
public:
|
||||
JitBlockCache *GetBlockCache() { return &blocks; }
|
||||
|
||||
const u8 *BackPatch(u8 *codePtr, int accessType, u32 em_address, void *ctx);
|
||||
|
||||
virtual const CommonAsmRoutines *GetAsmRoutines() = 0;
|
||||
bool IsInCodeSpace(u8 *ptr) { return IsInSpace(ptr); }
|
||||
};
|
||||
|
||||
extern JitBase *jit;
|
||||
|
||||
@@ -32,15 +32,13 @@
|
||||
#include "MemoryUtil.h"
|
||||
|
||||
#include "../../HW/Memmap.h"
|
||||
#include "../JitInterface.h"
|
||||
#include "../../CoreTiming.h"
|
||||
|
||||
#include "../PowerPC.h"
|
||||
#include "../PPCTables.h"
|
||||
#include "../PPCAnalyst.h"
|
||||
|
||||
#include "x64Emitter.h"
|
||||
#include "x64Analyzer.h"
|
||||
|
||||
#include "JitCache.h"
|
||||
#include "JitBase.h"
|
||||
|
||||
@@ -68,12 +66,12 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
||||
return (em_address >= originalAddress && em_address < originalAddress + 4 * originalSize);
|
||||
}
|
||||
|
||||
bool JitBlockCache::IsFull() const
|
||||
bool JitBaseBlockCache::IsFull() const
|
||||
{
|
||||
return GetNumBlocks() >= MAX_NUM_BLOCKS - 1;
|
||||
}
|
||||
|
||||
void JitBlockCache::Init()
|
||||
void JitBaseBlockCache::Init()
|
||||
{
|
||||
MAX_NUM_BLOCKS = 65536*2;
|
||||
|
||||
@@ -91,11 +89,11 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
||||
}
|
||||
else
|
||||
{
|
||||
PanicAlert("JitBlockCache::Init() - iCache is already initialized");
|
||||
PanicAlert("JitBaseBlockCache::Init() - iCache is already initialized");
|
||||
}
|
||||
if (iCache == 0 || iCacheEx == 0 || iCacheVMEM == 0)
|
||||
{
|
||||
PanicAlert("JitBlockCache::Init() - unable to allocate iCache");
|
||||
PanicAlert("JitBaseBlockCache::Init() - unable to allocate iCache");
|
||||
}
|
||||
memset(iCache, JIT_ICACHE_INVALID_BYTE, JIT_ICACHE_SIZE);
|
||||
memset(iCacheEx, JIT_ICACHE_INVALID_BYTE, JIT_ICACHEEX_SIZE);
|
||||
@@ -104,7 +102,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
||||
Clear();
|
||||
}
|
||||
|
||||
void JitBlockCache::Shutdown()
|
||||
void JitBaseBlockCache::Shutdown()
|
||||
{
|
||||
delete[] blocks;
|
||||
delete[] blockCodePointers;
|
||||
@@ -133,7 +131,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
||||
|
||||
// This clears the JIT cache. It's called from JitCache.cpp when the JIT cache
|
||||
// is full and when saving and loading states.
|
||||
void JitBlockCache::Clear()
|
||||
void JitBaseBlockCache::Clear()
|
||||
{
|
||||
if (IsFull())
|
||||
Core::DisplayMessage("Clearing block cache.", 3000);
|
||||
@@ -151,7 +149,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
||||
memset(blockCodePointers, 0, sizeof(u8*)*MAX_NUM_BLOCKS);
|
||||
}
|
||||
|
||||
void JitBlockCache::ClearSafe()
|
||||
void JitBaseBlockCache::ClearSafe()
|
||||
{
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
memset(iCache, JIT_ICACHE_INVALID_BYTE, JIT_ICACHE_SIZE);
|
||||
@@ -160,7 +158,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
||||
#endif
|
||||
}
|
||||
|
||||
/*void JitBlockCache::DestroyBlocksWithFlag(BlockFlag death_flag)
|
||||
/*void JitBaseBlockCache::DestroyBlocksWithFlag(BlockFlag death_flag)
|
||||
{
|
||||
for (int i = 0; i < num_blocks; i++)
|
||||
{
|
||||
@@ -171,23 +169,23 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
||||
}
|
||||
}*/
|
||||
|
||||
void JitBlockCache::Reset()
|
||||
void JitBaseBlockCache::Reset()
|
||||
{
|
||||
Shutdown();
|
||||
Init();
|
||||
}
|
||||
|
||||
JitBlock *JitBlockCache::GetBlock(int no)
|
||||
JitBlock *JitBaseBlockCache::GetBlock(int no)
|
||||
{
|
||||
return &blocks[no];
|
||||
}
|
||||
|
||||
int JitBlockCache::GetNumBlocks() const
|
||||
int JitBaseBlockCache::GetNumBlocks() const
|
||||
{
|
||||
return num_blocks;
|
||||
}
|
||||
|
||||
bool JitBlockCache::RangeIntersect(int s1, int e1, int s2, int e2) const
|
||||
bool JitBaseBlockCache::RangeIntersect(int s1, int e1, int s2, int e2) const
|
||||
{
|
||||
// check if any endpoint is inside the other range
|
||||
if ((s1 >= s2 && s1 <= e2) ||
|
||||
@@ -199,7 +197,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
||||
return false;
|
||||
}
|
||||
|
||||
int JitBlockCache::AllocateBlock(u32 em_address)
|
||||
int JitBaseBlockCache::AllocateBlock(u32 em_address)
|
||||
{
|
||||
JitBlock &b = blocks[num_blocks];
|
||||
b.invalid = false;
|
||||
@@ -215,12 +213,12 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
||||
return num_blocks - 1;
|
||||
}
|
||||
|
||||
void JitBlockCache::FinalizeBlock(int block_num, bool block_link, const u8 *code_ptr)
|
||||
void JitBaseBlockCache::FinalizeBlock(int block_num, bool block_link, const u8 *code_ptr)
|
||||
{
|
||||
blockCodePointers[block_num] = code_ptr;
|
||||
JitBlock &b = blocks[block_num];
|
||||
b.originalFirstOpcode = Memory::Read_Opcode_JIT(b.originalAddress);
|
||||
Memory::Write_Opcode_JIT(b.originalAddress, (JIT_OPCODE << 26) | block_num);
|
||||
b.originalFirstOpcode = JitInterface::Read_Opcode_JIT(b.originalAddress);
|
||||
JitInterface::Write_Opcode_JIT(b.originalAddress, (JIT_OPCODE << 26) | block_num);
|
||||
|
||||
// Convert the logical address to a physical address for the block map
|
||||
u32 pAddr = b.originalAddress & 0x1FFFFFFF;
|
||||
@@ -264,29 +262,29 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
||||
#endif
|
||||
}
|
||||
|
||||
const u8 **JitBlockCache::GetCodePointers()
|
||||
const u8 **JitBaseBlockCache::GetCodePointers()
|
||||
{
|
||||
return blockCodePointers;
|
||||
}
|
||||
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
u8* JitBlockCache::GetICache()
|
||||
u8* JitBaseBlockCache::GetICache()
|
||||
{
|
||||
return iCache;
|
||||
}
|
||||
|
||||
u8* JitBlockCache::GetICacheEx()
|
||||
u8* JitBaseBlockCache::GetICacheEx()
|
||||
{
|
||||
return iCacheEx;
|
||||
}
|
||||
|
||||
u8* JitBlockCache::GetICacheVMEM()
|
||||
u8* JitBaseBlockCache::GetICacheVMEM()
|
||||
{
|
||||
return iCacheVMEM;
|
||||
}
|
||||
#endif
|
||||
|
||||
int JitBlockCache::GetBlockNumberFromStartAddress(u32 addr)
|
||||
int JitBaseBlockCache::GetBlockNumberFromStartAddress(u32 addr)
|
||||
{
|
||||
if (!blocks)
|
||||
return -1;
|
||||
@@ -317,24 +315,24 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
||||
return inst;
|
||||
}
|
||||
|
||||
void JitBlockCache::GetBlockNumbersFromAddress(u32 em_address, std::vector<int> *block_numbers)
|
||||
void JitBaseBlockCache::GetBlockNumbersFromAddress(u32 em_address, std::vector<int> *block_numbers)
|
||||
{
|
||||
for (int i = 0; i < num_blocks; i++)
|
||||
if (blocks[i].ContainsAddress(em_address))
|
||||
block_numbers->push_back(i);
|
||||
}
|
||||
|
||||
u32 JitBlockCache::GetOriginalFirstOp(int block_num)
|
||||
u32 JitBaseBlockCache::GetOriginalFirstOp(int block_num)
|
||||
{
|
||||
if (block_num >= num_blocks)
|
||||
{
|
||||
//PanicAlert("JitBlockCache::GetOriginalFirstOp - block_num = %u is out of range", block_num);
|
||||
//PanicAlert("JitBaseBlockCache::GetOriginalFirstOp - block_num = %u is out of range", block_num);
|
||||
return block_num;
|
||||
}
|
||||
return blocks[block_num].originalFirstOpcode;
|
||||
}
|
||||
|
||||
CompiledCode JitBlockCache::GetCompiledCodeFromBlock(int block_num)
|
||||
CompiledCode JitBaseBlockCache::GetCompiledCodeFromBlock(int block_num)
|
||||
{
|
||||
return (CompiledCode)blockCodePointers[block_num];
|
||||
}
|
||||
@@ -345,7 +343,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
||||
//Can be faster by doing a queue for blocks to link up, and only process those
|
||||
//Should probably be done
|
||||
|
||||
void JitBlockCache::LinkBlockExits(int i)
|
||||
void JitBaseBlockCache::LinkBlockExits(int i)
|
||||
{
|
||||
JitBlock &b = blocks[i];
|
||||
if (b.invalid)
|
||||
@@ -360,8 +358,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
||||
int destinationBlock = GetBlockNumberFromStartAddress(b.exitAddress[e]);
|
||||
if (destinationBlock != -1)
|
||||
{
|
||||
XEmitter emit(b.exitPtrs[e]);
|
||||
emit.JMP(blocks[destinationBlock].checkedEntry, true);
|
||||
WriteLinkBlock(b.exitPtrs[e], blocks[destinationBlock].checkedEntry);
|
||||
b.linkStatus[e] = true;
|
||||
}
|
||||
}
|
||||
@@ -370,7 +367,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
||||
|
||||
using namespace std;
|
||||
|
||||
void JitBlockCache::LinkBlock(int i)
|
||||
void JitBaseBlockCache::LinkBlock(int i)
|
||||
{
|
||||
LinkBlockExits(i);
|
||||
JitBlock &b = blocks[i];
|
||||
@@ -386,7 +383,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
||||
}
|
||||
}
|
||||
|
||||
void JitBlockCache::UnlinkBlock(int i)
|
||||
void JitBaseBlockCache::UnlinkBlock(int i)
|
||||
{
|
||||
JitBlock &b = blocks[i];
|
||||
pair<multimap<u32, int>::iterator, multimap<u32, int>::iterator> ppp;
|
||||
@@ -403,7 +400,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
||||
}
|
||||
}
|
||||
|
||||
void JitBlockCache::DestroyBlock(int block_num, bool invalidate)
|
||||
void JitBaseBlockCache::DestroyBlock(int block_num, bool invalidate)
|
||||
{
|
||||
if (block_num < 0 || block_num >= num_blocks)
|
||||
{
|
||||
@@ -419,7 +416,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
||||
}
|
||||
b.invalid = true;
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
Memory::Write_Opcode_JIT(b.originalAddress, b.originalFirstOpcode?b.originalFirstOpcode:JIT_ICACHE_INVALID_WORD);
|
||||
JitInterface::Write_Opcode_JIT(b.originalAddress, b.originalFirstOpcode?b.originalFirstOpcode:JIT_ICACHE_INVALID_WORD);
|
||||
#else
|
||||
if (Memory::ReadFast32(b.originalAddress) == block_num)
|
||||
Memory::WriteUnchecked_U32(b.originalFirstOpcode, b.originalAddress);
|
||||
@@ -430,18 +427,10 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
||||
// Send anyone who tries to run this block back to the dispatcher.
|
||||
// Not entirely ideal, but .. pretty good.
|
||||
// Spurious entrances from previously linked blocks can only come through checkedEntry
|
||||
XEmitter emit((u8 *)b.checkedEntry);
|
||||
emit.MOV(32, M(&PC), Imm32(b.originalAddress));
|
||||
emit.JMP(jit->GetAsmRoutines()->dispatcher, true);
|
||||
// this is not needed really
|
||||
/*
|
||||
emit.SetCodePtr((u8 *)blockCodePointers[blocknum]);
|
||||
emit.MOV(32, M(&PC), Imm32(b.originalAddress));
|
||||
emit.JMP(asm_routines.dispatcher, true);
|
||||
*/
|
||||
WriteDestroyBlock(b.checkedEntry, b.originalAddress);
|
||||
}
|
||||
|
||||
void JitBlockCache::InvalidateICache(u32 address, const u32 length)
|
||||
void JitBaseBlockCache::InvalidateICache(u32 address, const u32 length)
|
||||
{
|
||||
// Convert the logical address to a physical address for the block map
|
||||
u32 pAddr = address & 0x1FFFFFFF;
|
||||
@@ -449,7 +438,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
||||
// Optimize the common case of length == 32 which is used by Interpreter::dcb*
|
||||
bool destroy_block = true;
|
||||
if (length == 32)
|
||||
{
|
||||
{
|
||||
if (!valid_block[pAddr / 32])
|
||||
destroy_block = false;
|
||||
else
|
||||
@@ -462,7 +451,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
||||
{
|
||||
std::map<pair<u32,u32>, u32>::iterator it1 = block_map.lower_bound(std::make_pair(pAddr, 0)), it2 = it1;
|
||||
while (it2 != block_map.end() && it2->first.second < pAddr + length)
|
||||
{
|
||||
{
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
JitBlock &b = blocks[it2->second];
|
||||
if (b.originalAddress & JIT_ICACHE_VMEM_BIT)
|
||||
@@ -516,3 +505,14 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
void JitBlockCache::WriteLinkBlock(u8* location, const u8* address)
|
||||
{
|
||||
XEmitter emit(location);
|
||||
emit.JMP(address, true);
|
||||
}
|
||||
void JitBlockCache::WriteDestroyBlock(const u8* location, u32 address)
|
||||
{
|
||||
XEmitter emit((u8 *)location);
|
||||
emit.MOV(32, M(&PC), Imm32(address));
|
||||
emit.JMP(jit->GetAsmRoutines()->dispatcher, true);
|
||||
}
|
||||
@@ -78,7 +78,8 @@ struct JitBlock
|
||||
|
||||
typedef void (*CompiledCode)();
|
||||
|
||||
class JitBlockCache
|
||||
|
||||
class JitBaseBlockCache
|
||||
{
|
||||
const u8 **blockCodePointers;
|
||||
JitBlock *blocks;
|
||||
@@ -97,9 +98,13 @@ class JitBlockCache
|
||||
void LinkBlockExits(int i);
|
||||
void LinkBlock(int i);
|
||||
void UnlinkBlock(int i);
|
||||
|
||||
// Virtual for overloaded
|
||||
virtual void WriteLinkBlock(u8* location, const u8* address) = 0;
|
||||
virtual void WriteDestroyBlock(const u8* location, u32 address) = 0;
|
||||
|
||||
public:
|
||||
JitBlockCache() :
|
||||
JitBaseBlockCache() :
|
||||
blockCodePointers(0), blocks(0), num_blocks(0),
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
iCache(0), iCacheEx(0), iCacheVMEM(0),
|
||||
@@ -146,4 +151,11 @@ public:
|
||||
//void DestroyBlocksWithFlag(BlockFlag death_flag);
|
||||
};
|
||||
|
||||
// x86 BlockCache
|
||||
class JitBlockCache : public JitBaseBlockCache
|
||||
{
|
||||
private:
|
||||
void WriteLinkBlock(u8* location, const u8* address);
|
||||
void WriteDestroyBlock(const u8* location, u32 address);
|
||||
};
|
||||
#endif
|
||||
@@ -25,7 +25,7 @@
|
||||
#include "../../HW/Memmap.h"
|
||||
#include "../PPCTables.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "ABI.h"
|
||||
#include "x64ABI.h"
|
||||
#include "JitBase.h"
|
||||
#include "Jit_Util.h"
|
||||
|
||||
|
||||
@@ -0,0 +1,350 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "JitInterface.h"
|
||||
#include "JitCommon/JitBase.h"
|
||||
|
||||
#ifndef _M_GENERIC
|
||||
#include "Jit64IL/JitIL.h"
|
||||
#include "Jit64/Jit.h"
|
||||
#include "Jit64/Jit64_Tables.h"
|
||||
#include "Jit64IL/JitIL_Tables.h"
|
||||
#endif
|
||||
|
||||
#ifdef _M_ARM
|
||||
#include "JitArm32/Jit.h"
|
||||
#include "JitArm32/JitArm_Tables.h"
|
||||
#endif
|
||||
|
||||
#include "Profiler.h"
|
||||
#include "PPCSymbolDB.h"
|
||||
#include "HW/Memmap.h"
|
||||
#include "ConfigManager.h"
|
||||
|
||||
bool bFakeVMEM = false;
|
||||
bool bMMU = false;
|
||||
|
||||
namespace JitInterface
|
||||
{
|
||||
void DoState(PointerWrap &p)
|
||||
{
|
||||
if (jit && p.GetMode() == PointerWrap::MODE_READ)
|
||||
jit->GetBlockCache()->ClearSafe();
|
||||
}
|
||||
CPUCoreBase *InitJitCore(int core)
|
||||
{
|
||||
bFakeVMEM = SConfig::GetInstance().m_LocalCoreStartupParameter.iTLBHack == 1;
|
||||
bMMU = SConfig::GetInstance().m_LocalCoreStartupParameter.bMMU;
|
||||
|
||||
CPUCoreBase *ptr = NULL;
|
||||
switch(core)
|
||||
{
|
||||
#ifndef _M_GENERIC
|
||||
case 1:
|
||||
{
|
||||
ptr = new Jit64();
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
ptr = new JitIL();
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef _M_ARM
|
||||
case 3:
|
||||
{
|
||||
ptr = new JitArm();
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
{
|
||||
PanicAlert("Unrecognizable cpu_core: %d", core);
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
jit = static_cast<JitBase*>(ptr);
|
||||
jit->Init();
|
||||
return ptr;
|
||||
}
|
||||
void InitTables(int core)
|
||||
{
|
||||
switch(core)
|
||||
{
|
||||
#ifndef _M_GENERIC
|
||||
case 1:
|
||||
{
|
||||
Jit64Tables::InitTables();
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
JitILTables::InitTables();
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef _M_ARM
|
||||
case 3:
|
||||
{
|
||||
JitArmTables::InitTables();
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
{
|
||||
PanicAlert("Unrecognizable cpu_core: %d", core);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
CPUCoreBase *GetCore()
|
||||
{
|
||||
return jit;
|
||||
}
|
||||
|
||||
void WriteProfileResults(const char *filename)
|
||||
{
|
||||
// Can't really do this with no jit core available
|
||||
#ifndef _M_GENERIC
|
||||
|
||||
std::vector<BlockStat> stats;
|
||||
stats.reserve(jit->GetBlockCache()->GetNumBlocks());
|
||||
u64 cost_sum = 0;
|
||||
#ifdef _WIN32
|
||||
u64 timecost_sum = 0;
|
||||
u64 countsPerSec;
|
||||
QueryPerformanceFrequency((LARGE_INTEGER *)&countsPerSec);
|
||||
#endif
|
||||
for (int i = 0; i < jit->GetBlockCache()->GetNumBlocks(); i++)
|
||||
{
|
||||
const JitBlock *block = jit->GetBlockCache()->GetBlock(i);
|
||||
// Rough heuristic. Mem instructions should cost more.
|
||||
u64 cost = block->originalSize * (block->runCount / 4);
|
||||
#ifdef _WIN32
|
||||
u64 timecost = block->ticCounter;
|
||||
#endif
|
||||
// Todo: tweak.
|
||||
if (block->runCount >= 1)
|
||||
stats.push_back(BlockStat(i, cost));
|
||||
cost_sum += cost;
|
||||
#ifdef _WIN32
|
||||
timecost_sum += timecost;
|
||||
#endif
|
||||
}
|
||||
|
||||
sort(stats.begin(), stats.end());
|
||||
File::IOFile f(filename, "w");
|
||||
if (!f)
|
||||
{
|
||||
PanicAlert("failed to open %s", filename);
|
||||
return;
|
||||
}
|
||||
fprintf(f.GetHandle(), "origAddr\tblkName\tcost\ttimeCost\tpercent\ttimePercent\tOvAllinBlkTime(ms)\tblkCodeSize\n");
|
||||
for (unsigned int i = 0; i < stats.size(); i++)
|
||||
{
|
||||
const JitBlock *block = jit->GetBlockCache()->GetBlock(stats[i].blockNum);
|
||||
if (block)
|
||||
{
|
||||
std::string name = g_symbolDB.GetDescription(block->originalAddress);
|
||||
double percent = 100.0 * (double)stats[i].cost / (double)cost_sum;
|
||||
#ifdef _WIN32
|
||||
double timePercent = 100.0 * (double)block->ticCounter / (double)timecost_sum;
|
||||
fprintf(f.GetHandle(), "%08x\t%s\t%llu\t%llu\t%.2lf\t%llf\t%lf\t%i\n",
|
||||
block->originalAddress, name.c_str(), stats[i].cost,
|
||||
block->ticCounter, percent, timePercent,
|
||||
(double)block->ticCounter*1000.0/(double)countsPerSec, block->codeSize);
|
||||
#else
|
||||
fprintf(f.GetHandle(), "%08x\t%s\t%llu\t???\t%.2lf\t???\t???\t%i\n",
|
||||
block->originalAddress, name.c_str(), stats[i].cost, percent, block->codeSize);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
bool IsInCodeSpace(u8 *ptr)
|
||||
{
|
||||
return jit->IsInCodeSpace(ptr);
|
||||
}
|
||||
const u8 *BackPatch(u8 *codePtr, int accessType, u32 em_address, void *ctx)
|
||||
{
|
||||
return jit->BackPatch(codePtr, accessType, em_address, ctx);
|
||||
}
|
||||
|
||||
void ClearCache()
|
||||
{
|
||||
if (jit)
|
||||
jit->ClearCache();
|
||||
}
|
||||
void ClearSafe()
|
||||
{
|
||||
if (jit)
|
||||
jit->GetBlockCache()->ClearSafe();
|
||||
}
|
||||
|
||||
void InvalidateICache(u32 address, u32 size)
|
||||
{
|
||||
if (jit)
|
||||
jit->GetBlockCache()->InvalidateICache(address, size);
|
||||
}
|
||||
|
||||
|
||||
// Memory functions
|
||||
u32 Read_Opcode_JIT_Uncached(const u32 _Address)
|
||||
{
|
||||
u8* iCache;
|
||||
u32 addr;
|
||||
if (_Address & JIT_ICACHE_VMEM_BIT)
|
||||
{
|
||||
iCache = jit->GetBlockCache()->GetICacheVMEM();
|
||||
addr = _Address & JIT_ICACHE_MASK;
|
||||
}
|
||||
else if (_Address & JIT_ICACHE_EXRAM_BIT)
|
||||
{
|
||||
iCache = jit->GetBlockCache()->GetICacheEx();
|
||||
addr = _Address & JIT_ICACHEEX_MASK;
|
||||
}
|
||||
else
|
||||
{
|
||||
iCache = jit->GetBlockCache()->GetICache();
|
||||
addr = _Address & JIT_ICACHE_MASK;
|
||||
}
|
||||
u32 inst = *(u32*)(iCache + addr);
|
||||
if (inst == JIT_ICACHE_INVALID_WORD)
|
||||
{
|
||||
u32 cache_block_start = addr & ~0x1f;
|
||||
u32 mem_block_start = _Address & ~0x1f;
|
||||
u8 *pMem = Memory::GetPointer(mem_block_start);
|
||||
memcpy(iCache + cache_block_start, pMem, 32);
|
||||
inst = *(u32*)(iCache + addr);
|
||||
}
|
||||
inst = Common::swap32(inst);
|
||||
|
||||
if ((inst & 0xfc000000) == 0)
|
||||
{
|
||||
inst = jit->GetBlockCache()->GetOriginalFirstOp(inst);
|
||||
}
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
u32 Read_Opcode_JIT(u32 _Address)
|
||||
{
|
||||
#ifdef FAST_ICACHE
|
||||
if (bMMU && !bFakeVMEM && (_Address & Memory::ADDR_MASK_MEM1))
|
||||
{
|
||||
_Address = Memory::TranslateAddress(_Address, Memory::FLAG_OPCODE);
|
||||
if (_Address == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
u32 inst = 0;
|
||||
|
||||
// Bypass the icache for the external interrupt exception handler
|
||||
if ( (_Address & 0x0FFFFF00) == 0x00000500 )
|
||||
inst = Read_Opcode_JIT_Uncached(_Address);
|
||||
else
|
||||
inst = PowerPC::ppcState.iCache.ReadInstruction(_Address);
|
||||
#else
|
||||
u32 inst = Memory::ReadUnchecked_U32(_Address);
|
||||
#endif
|
||||
return inst;
|
||||
}
|
||||
|
||||
// The following function is deprecated in favour of FAST_ICACHE
|
||||
u32 Read_Opcode_JIT_LC(const u32 _Address)
|
||||
{
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
if ((_Address & ~JIT_ICACHE_MASK) != 0x80000000 && (_Address & ~JIT_ICACHE_MASK) != 0x00000000 &&
|
||||
(_Address & ~JIT_ICACHE_MASK) != 0x7e000000 && // TLB area
|
||||
(_Address & ~JIT_ICACHEEX_MASK) != 0x90000000 && (_Address & ~JIT_ICACHEEX_MASK) != 0x10000000)
|
||||
{
|
||||
PanicAlertT("iCacheJIT: Reading Opcode from %x. Please report.", _Address);
|
||||
ERROR_LOG(MEMMAP, "iCacheJIT: Reading Opcode from %x. Please report.", _Address);
|
||||
return 0;
|
||||
}
|
||||
u8* iCache;
|
||||
u32 addr;
|
||||
if (_Address & JIT_ICACHE_VMEM_BIT)
|
||||
{
|
||||
iCache = jit->GetBlockCache()->GetICacheVMEM();
|
||||
addr = _Address & JIT_ICACHE_MASK;
|
||||
}
|
||||
else if (_Address & JIT_ICACHE_EXRAM_BIT)
|
||||
{
|
||||
iCache = jit->GetBlockCache()->GetICacheEx();
|
||||
addr = _Address & JIT_ICACHEEX_MASK;
|
||||
}
|
||||
else
|
||||
{
|
||||
iCache = jit->GetBlockCache()->GetICache();
|
||||
addr = _Address & JIT_ICACHE_MASK;
|
||||
}
|
||||
u32 inst = *(u32*)(iCache + addr);
|
||||
if (inst == JIT_ICACHE_INVALID_WORD)
|
||||
inst = Memory::ReadUnchecked_U32(_Address);
|
||||
else
|
||||
inst = Common::swap32(inst);
|
||||
#else
|
||||
u32 inst = Memory::ReadUnchecked_U32(_Address);
|
||||
#endif
|
||||
if ((inst & 0xfc000000) == 0)
|
||||
{
|
||||
inst = jit->GetBlockCache()->GetOriginalFirstOp(inst);
|
||||
}
|
||||
return inst;
|
||||
}
|
||||
|
||||
// WARNING! No checks!
|
||||
// We assume that _Address is cached
|
||||
void Write_Opcode_JIT(const u32 _Address, const u32 _Value)
|
||||
{
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
if (_Address & JIT_ICACHE_VMEM_BIT)
|
||||
{
|
||||
*(u32*)(jit->GetBlockCache()->GetICacheVMEM() + (_Address & JIT_ICACHE_MASK)) = Common::swap32(_Value);
|
||||
}
|
||||
else if (_Address & JIT_ICACHE_EXRAM_BIT)
|
||||
{
|
||||
*(u32*)(jit->GetBlockCache()->GetICacheEx() + (_Address & JIT_ICACHEEX_MASK)) = Common::swap32(_Value);
|
||||
}
|
||||
else
|
||||
*(u32*)(jit->GetBlockCache()->GetICache() + (_Address & JIT_ICACHE_MASK)) = Common::swap32(_Value);
|
||||
#else
|
||||
Memory::WriteUnchecked_U32(_Value, _Address);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void Shutdown()
|
||||
{
|
||||
if (jit)
|
||||
{
|
||||
jit->Shutdown();
|
||||
delete jit;
|
||||
jit = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// 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 "ChunkFile.h"
|
||||
#include "CPUCoreBase.h"
|
||||
|
||||
namespace JitInterface
|
||||
{
|
||||
void DoState(PointerWrap &p);
|
||||
|
||||
CPUCoreBase *InitJitCore(int core);
|
||||
void InitTables(int core);
|
||||
CPUCoreBase *GetCore();
|
||||
|
||||
// Debugging
|
||||
void WriteProfileResults(const char *filename);
|
||||
|
||||
// Memory Utilities
|
||||
bool IsInCodeSpace(u8 *ptr);
|
||||
const u8 *BackPatch(u8 *codePtr, int accessType, u32 em_address, void *ctx);
|
||||
|
||||
// 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);
|
||||
|
||||
// Clearing CodeCache
|
||||
void ClearCache();
|
||||
|
||||
void ClearSafe();
|
||||
|
||||
void InvalidateICache(u32 address, u32 size);
|
||||
|
||||
void Shutdown();
|
||||
}
|
||||
extern bool bFakeVMEM;
|
||||
extern bool bMMU;
|
||||
|
||||
#ifdef _M_ARM
|
||||
#include "JitArm32/Jit.h"
|
||||
#endif
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "StringUtil.h"
|
||||
#include "Interpreter/Interpreter.h"
|
||||
#include "../HW/Memmap.h"
|
||||
#include "JitInterface.h"
|
||||
#include "PPCTables.h"
|
||||
#include "PPCSymbolDB.h"
|
||||
#include "SignatureDB.h"
|
||||
@@ -368,7 +369,7 @@ u32 Flatten(u32 address, int *realsize, BlockStats *st, BlockRegStats *gpa,
|
||||
}
|
||||
else
|
||||
{
|
||||
inst = Memory::Read_Opcode_JIT(address);
|
||||
inst = JitInterface::Read_Opcode_JIT(address);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "PowerPC.h"
|
||||
#include "JitCommon/JitBase.h"
|
||||
#include "JitCommon/JitCache.h"
|
||||
#include "JitInterface.h"
|
||||
|
||||
namespace PowerPC
|
||||
{
|
||||
@@ -76,8 +77,7 @@ namespace PowerPC
|
||||
memset(lookup_table_ex, 0xff, sizeof(lookup_table_ex));
|
||||
memset(lookup_table_vmem, 0xff, sizeof(lookup_table_vmem));
|
||||
#endif
|
||||
if (jit)
|
||||
jit->GetBlockCache()->ClearSafe();
|
||||
JitInterface::ClearSafe();
|
||||
}
|
||||
|
||||
void InstructionCache::Init()
|
||||
@@ -109,8 +109,7 @@ namespace PowerPC
|
||||
}
|
||||
#endif
|
||||
valid[set] = 0;
|
||||
if (jit)
|
||||
jit->GetBlockCache()->InvalidateICache(addr & ~0x1f, 32);
|
||||
JitInterface::InvalidateICache(addr & ~0x1f, 32);
|
||||
}
|
||||
|
||||
u32 InstructionCache::ReadInstruction(u32 addr)
|
||||
|
||||
@@ -24,11 +24,7 @@
|
||||
#include "FileUtil.h"
|
||||
#include "Interpreter/Interpreter.h"
|
||||
#include "Interpreter/Interpreter_Tables.h"
|
||||
#include "Jit64IL/JitIL_Tables.h"
|
||||
#include "Jit64/Jit64_Tables.h"
|
||||
|
||||
#include "Jit64IL/JitIL.h"
|
||||
#include "Jit64/Jit.h"
|
||||
#include "JitInterface.h"
|
||||
|
||||
struct op_inf
|
||||
{
|
||||
@@ -165,19 +161,9 @@ void InitTables(int cpu_core)
|
||||
// Interpreter
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
Jit64Tables::InitTables();
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
JitILTables::InitTables();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
PanicAlert("Unrecognizable cpu_core: %d", cpu_core);
|
||||
JitInterface::InitTables(cpu_core);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Loaded 100 of 443 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user