mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-09-06 09:35:53 +00:00
Merge branch 'master' into wii-network
Conflicts: Source/Core/Core/Core.vcxproj Source/Core/Core/Core.vcxproj.filters Source/Core/Core/Src/CoreParameter.cpp Source/Core/DolphinWX/Dolphin.vcxproj Source/Core/DolphinWX/Dolphin.vcxproj.filters
This commit is contained in:
commit
b6e054a2be
1697 changed files with 64354 additions and 72384 deletions
|
@ -21,7 +21,6 @@ set(SRCS Src/BreakPoints.cpp
|
|||
Src/Thread.cpp
|
||||
Src/Timer.cpp
|
||||
Src/Version.cpp
|
||||
Src/VideoBackendBase.cpp
|
||||
Src/x64ABI.cpp
|
||||
Src/x64Analyzer.cpp
|
||||
Src/x64Emitter.cpp
|
||||
|
|
|
@ -212,7 +212,6 @@
|
|||
<ClCompile Include="Src\Thread.cpp" />
|
||||
<ClCompile Include="Src\Timer.cpp" />
|
||||
<ClCompile Include="Src\Version.cpp" />
|
||||
<ClCompile Include="Src\VideoBackendBase.cpp" />
|
||||
<ClCompile Include="Src\x64ABI.cpp" />
|
||||
<ClCompile Include="Src\x64Analyzer.cpp" />
|
||||
<ClCompile Include="Src\x64CPUDetect.cpp" />
|
||||
|
@ -266,7 +265,6 @@
|
|||
<ClInclude Include="Src\Thread.h" />
|
||||
<ClInclude Include="Src\Thunk.h" />
|
||||
<ClInclude Include="Src\Timer.h" />
|
||||
<ClInclude Include="Src\VideoBackendBase.h" />
|
||||
<ClInclude Include="Src\x64ABI.h" />
|
||||
<ClInclude Include="Src\x64Analyzer.h" />
|
||||
<ClInclude Include="Src\x64Emitter.h" />
|
||||
|
@ -277,4 +275,4 @@
|
|||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
<ClCompile Include="Src\Thread.cpp" />
|
||||
<ClCompile Include="Src\Timer.cpp" />
|
||||
<ClCompile Include="Src\Version.cpp" />
|
||||
<ClCompile Include="Src\VideoBackendBase.cpp" />
|
||||
<ClCompile Include="Src\x64Analyzer.cpp" />
|
||||
<ClCompile Include="Src\x64Emitter.cpp" />
|
||||
<ClCompile Include="Src\LogManager.cpp">
|
||||
|
@ -87,7 +86,6 @@
|
|||
<ClInclude Include="Src\Thread.h" />
|
||||
<ClInclude Include="Src\Thunk.h" />
|
||||
<ClInclude Include="Src\Timer.h" />
|
||||
<ClInclude Include="Src\VideoBackendBase.h" />
|
||||
<ClInclude Include="Src\x64Analyzer.h" />
|
||||
<ClInclude Include="Src\x64Emitter.h" />
|
||||
<ClInclude Include="Src\Log.h">
|
||||
|
@ -124,4 +122,4 @@
|
|||
<UniqueIdentifier>{f078f36e-a0ff-4cd0-95f8-476100d68e68}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -17,9 +17,10 @@
|
|||
|
||||
#include "Common.h"
|
||||
#include "CPUDetect.h"
|
||||
#include "StringUtil.h"
|
||||
#include "FileUtil.h"
|
||||
|
||||
// Only Linux platforms have /proc/cpuinfo
|
||||
#if !defined(BLACKBERRY) && !defined(IOS) && !defined(__SYMBIAN32__)
|
||||
const char procfile[] = "/proc/cpuinfo";
|
||||
|
||||
char *GetCPUString()
|
||||
|
@ -33,7 +34,7 @@ char *GetCPUString()
|
|||
auto const fp = file.GetHandle();
|
||||
if (!fp)
|
||||
return 0;
|
||||
|
||||
|
||||
while (fgets(buf, sizeof(buf), fp))
|
||||
{
|
||||
if (strncmp(buf, marker, sizeof(marker) - 1))
|
||||
|
@ -42,6 +43,7 @@ char *GetCPUString()
|
|||
cpu_string = strndup(cpu_string, strlen(cpu_string) - 1); // Strip the newline
|
||||
break;
|
||||
}
|
||||
|
||||
return cpu_string;
|
||||
}
|
||||
|
||||
|
@ -66,6 +68,9 @@ unsigned char GetCPUImplementer()
|
|||
sscanf(implementer_string, "0x%02hhx", &implementer);
|
||||
break;
|
||||
}
|
||||
|
||||
free(implementer_string);
|
||||
|
||||
return implementer;
|
||||
}
|
||||
|
||||
|
@ -90,15 +95,17 @@ unsigned short GetCPUPart()
|
|||
sscanf(part_string, "0x%03hx", &part);
|
||||
break;
|
||||
}
|
||||
return part;
|
||||
|
||||
free(part_string);
|
||||
|
||||
return part;
|
||||
}
|
||||
|
||||
bool CheckCPUFeature(const char *feature)
|
||||
{
|
||||
const char marker[] = "Features\t: ";
|
||||
char buf[1024];
|
||||
|
||||
|
||||
File::IOFile file(procfile, "r");
|
||||
auto const fp = file.GetHandle();
|
||||
if (!fp)
|
||||
|
@ -117,10 +124,18 @@ bool CheckCPUFeature(const char *feature)
|
|||
token = strtok(NULL, " ");
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
int GetCoreCount()
|
||||
{
|
||||
#ifdef __SYMBIAN32__
|
||||
return 1;
|
||||
#elif defined(BLACKBERRY) || defined(IOS)
|
||||
return 2;
|
||||
#else
|
||||
const char marker[] = "processor\t: ";
|
||||
int cores = 0;
|
||||
char buf[1024];
|
||||
|
@ -129,14 +144,16 @@ int GetCoreCount()
|
|||
auto const fp = file.GetHandle();
|
||||
if (!fp)
|
||||
return 0;
|
||||
|
||||
|
||||
while (fgets(buf, sizeof(buf), fp))
|
||||
{
|
||||
if (strncmp(buf, marker, sizeof(marker) - 1))
|
||||
continue;
|
||||
++cores;
|
||||
}
|
||||
|
||||
return cores;
|
||||
#endif
|
||||
}
|
||||
|
||||
CPUInfo cpu_info;
|
||||
|
@ -153,12 +170,58 @@ void CPUInfo::Detect()
|
|||
HTT = false;
|
||||
OS64bit = false;
|
||||
CPU64bit = false;
|
||||
Mode64bit = false;
|
||||
Mode64bit = false;
|
||||
vendor = VENDOR_ARM;
|
||||
|
||||
|
||||
// Get the information about the CPU
|
||||
strncpy(cpu_string, GetCPUString(), sizeof(cpu_string));
|
||||
num_cores = GetCoreCount();
|
||||
#if defined(__SYMBIAN32__) || defined(BLACKBERRY) || defined(IOS)
|
||||
bool isVFP3 = false;
|
||||
bool isVFP4 = false;
|
||||
#ifdef IOS
|
||||
isVFP3 = true;
|
||||
// Check for swift arch (VFP4`)
|
||||
#ifdef __ARM_ARCH_7S__
|
||||
isVFP4 = true;
|
||||
#endif // #ifdef __ARM_ARCH_7S__
|
||||
#elif defined(BLACKBERRY)
|
||||
isVFP3 = true;
|
||||
const char cpuInfoPath[] = "/pps/services/hw_info/inventory";
|
||||
const char marker[] = "Processor_Name::";
|
||||
const char qcCPU[] = "MSM";
|
||||
char buf[1024];
|
||||
FILE* fp;
|
||||
if (fp = fopen(cpuInfoPath, "r"))
|
||||
{
|
||||
while (fgets(buf, sizeof(buf), fp))
|
||||
{
|
||||
if (strncmp(buf, marker, sizeof(marker) - 1))
|
||||
continue;
|
||||
if (strncmp(buf + sizeof(marker) - 1, qcCPU, sizeof(qcCPU) - 1) == 0)
|
||||
isVFP4 = true;
|
||||
break;
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
#endif
|
||||
// Hardcode this for now
|
||||
bSwp = true;
|
||||
bHalf = true;
|
||||
bThumb = false;
|
||||
bFastMult = true;
|
||||
bVFP = true;
|
||||
bEDSP = true;
|
||||
bThumbEE = isVFP3;
|
||||
bNEON = isVFP3;
|
||||
bVFPv3 = isVFP3;
|
||||
bTLS = true;
|
||||
bVFPv4 = isVFP4;
|
||||
bIDIVa = isVFP4;
|
||||
bIDIVt = isVFP4;
|
||||
bFP = false;
|
||||
bASIMD = false;
|
||||
#else
|
||||
strncpy(cpu_string, GetCPUString(), sizeof(cpu_string));
|
||||
bSwp = CheckCPUFeature("swp");
|
||||
bHalf = CheckCPUFeature("half");
|
||||
bThumb = CheckCPUFeature("thumb");
|
||||
|
@ -172,16 +235,15 @@ void CPUInfo::Detect()
|
|||
bVFPv4 = CheckCPUFeature("vfpv4");
|
||||
bIDIVa = CheckCPUFeature("idiva");
|
||||
bIDIVt = CheckCPUFeature("idivt");
|
||||
|
||||
// Qualcomm Krait supports IDIVA but it doesn't report it. Check for krait.
|
||||
if (GetCPUImplementer() == 0x51 && GetCPUPart() == 0x6F) // Krait(300) is 0x6F, Scorpion is 0x4D
|
||||
bIDIVa = bIDIVt = true;
|
||||
|
||||
// These two are ARMv8 specific.
|
||||
bIDIVa = bIDIVt = true;
|
||||
// These two require ARMv8 or higher
|
||||
bFP = CheckCPUFeature("fp");
|
||||
bASIMD = CheckCPUFeature("asimd");
|
||||
|
||||
|
||||
#endif
|
||||
// On android, we build a separate library for ARMv7 so this is fine.
|
||||
// TODO: Check for ARMv7 on other platforms.
|
||||
#if defined(__ARM_ARCH_7A__)
|
||||
bArmV7 = true;
|
||||
#else
|
||||
|
@ -193,11 +255,14 @@ void CPUInfo::Detect()
|
|||
std::string CPUInfo::Summarize()
|
||||
{
|
||||
std::string sum;
|
||||
#if defined(BLACKBERRY) || defined(IOS) || defined(__SYMBIAN32__)
|
||||
sum = StringFromFormat("%i cores", num_cores);
|
||||
#else
|
||||
if (num_cores == 1)
|
||||
sum = StringFromFormat("%s, %i core", cpu_string, num_cores);
|
||||
else
|
||||
sum = StringFromFormat("%s, %i cores", cpu_string, num_cores);
|
||||
|
||||
#endif
|
||||
if (bSwp) sum += ", SWP";
|
||||
if (bHalf) sum += ", Half";
|
||||
if (bThumb) sum += ", Thumb";
|
||||
|
|
|
@ -86,7 +86,7 @@ bool TryMakeOperand2_AllowNegation(s32 imm, Operand2 &op2, bool *negated)
|
|||
Operand2 AssumeMakeOperand2(u32 imm) {
|
||||
Operand2 op2;
|
||||
bool result = TryMakeOperand2(imm, op2);
|
||||
_assert_msg_(DYNA_REC, result, "Could not make assumed Operand2.");
|
||||
_dbg_assert_msg_(DYNA_REC, result, "Could not make assumed Operand2.");
|
||||
return op2;
|
||||
}
|
||||
|
||||
|
@ -117,14 +117,33 @@ bool ARMXEmitter::TrySetValue_TwoOp(ARMReg reg, u32 val)
|
|||
return true;
|
||||
}
|
||||
|
||||
void ARMXEmitter::MOVI2F(ARMReg dest, float val, ARMReg tempReg)
|
||||
void ARMXEmitter::MOVI2F(ARMReg dest, float val, ARMReg tempReg, bool negate)
|
||||
{
|
||||
union {float f; u32 u;} conv;
|
||||
conv.f = val;
|
||||
conv.f = negate ? -val : val;
|
||||
// Try moving directly first if mantisse is empty
|
||||
if (cpu_info.bVFPv3 && ((conv.u & 0x7FFFF) == 0))
|
||||
{
|
||||
// VFP Encoding for Imms: <7> Not(<6>) Repeat(<6>,5) <5:0> Zeros(19)
|
||||
bool bit6 = (conv.u & 0x40000000) == 0x40000000;
|
||||
bool canEncode = true;
|
||||
for (u32 mask = 0x20000000; mask >= 0x2000000; mask >>= 1)
|
||||
{
|
||||
if (((conv.u & mask) == mask) == bit6)
|
||||
canEncode = false;
|
||||
}
|
||||
if (canEncode)
|
||||
{
|
||||
u32 imm8 = (conv.u & 0x80000000) >> 24; // sign bit
|
||||
imm8 |= (!bit6 << 6);
|
||||
imm8 |= (conv.u & 0x1F80000) >> 19;
|
||||
VMOV(dest, IMM(imm8));
|
||||
return;
|
||||
}
|
||||
}
|
||||
MOVI2R(tempReg, conv.u);
|
||||
VMOV(dest, tempReg);
|
||||
// TODO: VMOV an IMM directly if possible
|
||||
// Otherwise, use a literal pool and VLDR directly (+- 1020)
|
||||
// Otherwise, possible to use a literal pool and VLDR directly (+- 1020)
|
||||
}
|
||||
|
||||
void ARMXEmitter::ADDI2R(ARMReg rd, ARMReg rs, u32 val, ARMReg scratch)
|
||||
|
@ -246,8 +265,12 @@ void ARMXEmitter::MOVI2R(ARMReg reg, u32 val, bool optimize)
|
|||
}
|
||||
|
||||
void ARMXEmitter::QuickCallFunction(ARMReg reg, void *func) {
|
||||
MOVI2R(reg, (u32)(func));
|
||||
BL(reg);
|
||||
if (BLInRange(func)) {
|
||||
BL(func);
|
||||
} else {
|
||||
MOVI2R(reg, (u32)(func));
|
||||
BL(reg);
|
||||
}
|
||||
}
|
||||
|
||||
void ARMXEmitter::SetCodePtr(u8 *ptr)
|
||||
|
@ -369,7 +392,7 @@ FixupBranch ARMXEmitter::B_CC(CCFlags Cond)
|
|||
void ARMXEmitter::B_CC(CCFlags Cond, const void *fnptr)
|
||||
{
|
||||
s32 distance = (s32)fnptr - (s32(code) + 8);
|
||||
_assert_msg_(DYNA_REC, distance > -33554432
|
||||
_dbg_assert_msg_(DYNA_REC, distance > -33554432
|
||||
&& distance <= 33554432,
|
||||
"B_CC out of range (%p calls %p)", code, fnptr);
|
||||
|
||||
|
@ -388,7 +411,7 @@ FixupBranch ARMXEmitter::BL_CC(CCFlags Cond)
|
|||
void ARMXEmitter::SetJumpTarget(FixupBranch const &branch)
|
||||
{
|
||||
s32 distance = (s32(code) - 8) - (s32)branch.ptr;
|
||||
_assert_msg_(DYNA_REC, distance > -33554432
|
||||
_dbg_assert_msg_(DYNA_REC, distance > -33554432
|
||||
&& distance <= 33554432,
|
||||
"SetJumpTarget out of range (%p calls %p)", code,
|
||||
branch.ptr);
|
||||
|
@ -402,7 +425,7 @@ void ARMXEmitter::SetJumpTarget(FixupBranch const &branch)
|
|||
void ARMXEmitter::B (const void *fnptr)
|
||||
{
|
||||
s32 distance = (s32)fnptr - (s32(code) + 8);
|
||||
_assert_msg_(DYNA_REC, distance > -33554432
|
||||
_dbg_assert_msg_(DYNA_REC, distance > -33554432
|
||||
&& distance <= 33554432,
|
||||
"B out of range (%p calls %p)", code, fnptr);
|
||||
|
||||
|
@ -414,10 +437,18 @@ void ARMXEmitter::B(ARMReg src)
|
|||
Write32(condition | 0x12FFF10 | src);
|
||||
}
|
||||
|
||||
bool ARMXEmitter::BLInRange(const void *fnptr) {
|
||||
s32 distance = (s32)fnptr - (s32(code) + 8);
|
||||
if (distance <= -33554432 || distance > 33554432)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
void ARMXEmitter::BL(const void *fnptr)
|
||||
{
|
||||
s32 distance = (s32)fnptr - (s32(code) + 8);
|
||||
_assert_msg_(DYNA_REC, distance > -33554432
|
||||
_dbg_assert_msg_(DYNA_REC, distance > -33554432
|
||||
&& distance <= 33554432,
|
||||
"BL out of range (%p calls %p)", code, fnptr);
|
||||
Write32(condition | 0x0B000000 | ((distance >> 2) & 0x00FFFFFF));
|
||||
|
@ -555,7 +586,7 @@ void ARMXEmitter::WriteInstruction (u32 Op, ARMReg Rd, ARMReg Rn, Operand2 Rm, b
|
|||
}
|
||||
}
|
||||
if (op == -1)
|
||||
_assert_msg_(DYNA_REC, false, "%s not yet support %d", InstNames[Op], Rm.GetType());
|
||||
_dbg_assert_msg_(DYNA_REC, false, "%s not yet support %d", InstNames[Op], Rm.GetType());
|
||||
Write32(condition | (op << 21) | (SetFlags ? (1 << 20) : 0) | Rn << 16 | Rd << 12 | Data);
|
||||
}
|
||||
|
||||
|
@ -581,6 +612,8 @@ void ARMXEmitter::LSLS(ARMReg dest, ARMReg src, Operand2 op2) { WriteShiftedData
|
|||
void ARMXEmitter::LSL (ARMReg dest, ARMReg src, ARMReg op2) { WriteShiftedDataOp(1, false, dest, src, op2);}
|
||||
void ARMXEmitter::LSLS(ARMReg dest, ARMReg src, ARMReg op2) { WriteShiftedDataOp(1, true, dest, src, op2);}
|
||||
void ARMXEmitter::LSR (ARMReg dest, ARMReg src, Operand2 op2) { WriteShiftedDataOp(3, false, dest, src, op2);}
|
||||
void ARMXEmitter::ASR (ARMReg dest, ARMReg src, Operand2 op2) { WriteShiftedDataOp(4, false, dest, src, op2);}
|
||||
void ARMXEmitter::ASRS(ARMReg dest, ARMReg src, Operand2 op2) { WriteShiftedDataOp(4, true, dest, src, op2);}
|
||||
void ARMXEmitter::MUL (ARMReg dest, ARMReg src, ARMReg op2)
|
||||
{
|
||||
Write32(condition | (dest << 16) | (src << 8) | (9 << 4) | op2);
|
||||
|
@ -599,6 +632,11 @@ void ARMXEmitter::UMULL(ARMReg destLo, ARMReg destHi, ARMReg rm, ARMReg rn)
|
|||
Write4OpMultiply(0x8, destLo, destHi, rn, rm);
|
||||
}
|
||||
|
||||
void ARMXEmitter::UMULLS(ARMReg destLo, ARMReg destHi, ARMReg rm, ARMReg rn)
|
||||
{
|
||||
Write4OpMultiply(0x9, destLo, destHi, rn, rm);
|
||||
}
|
||||
|
||||
void ARMXEmitter::SMULL(ARMReg destLo, ARMReg destHi, ARMReg rm, ARMReg rn)
|
||||
{
|
||||
Write4OpMultiply(0xC, destLo, destHi, rn, rm);
|
||||
|
@ -652,11 +690,11 @@ void ARMXEmitter::RBIT(ARMReg dest, ARMReg src)
|
|||
}
|
||||
void ARMXEmitter::REV (ARMReg dest, ARMReg src)
|
||||
{
|
||||
Write32(condition | (0x6B << 20) | (0xF << 16) | (dest << 12) | (0xF3 << 4) | src);
|
||||
Write32(condition | (0x6BF << 16) | (dest << 12) | (0xF3 << 4) | src);
|
||||
}
|
||||
void ARMXEmitter::REV16(ARMReg dest, ARMReg src)
|
||||
{
|
||||
Write32(condition | (0x3DF << 16) | (dest << 12) | (0xFD << 4) | src);
|
||||
Write32(condition | (0x6BF << 16) | (dest << 12) | (0xFB << 4) | src);
|
||||
}
|
||||
|
||||
void ARMXEmitter::_MSR (bool write_nzcvq, bool write_g, Operand2 op2)
|
||||
|
@ -677,7 +715,7 @@ void ARMXEmitter::LDREX(ARMReg dest, ARMReg base)
|
|||
}
|
||||
void ARMXEmitter::STREX(ARMReg result, ARMReg base, ARMReg op)
|
||||
{
|
||||
_assert_msg_(DYNA_REC, (result != base && result != op), "STREX dest can't be other two registers");
|
||||
_dbg_assert_msg_(DYNA_REC, (result != base && result != op), "STREX dest can't be other two registers");
|
||||
Write32(condition | (24 << 20) | (base << 16) | (result << 12) | (0xF9 << 4) | op);
|
||||
}
|
||||
void ARMXEmitter::DMB ()
|
||||
|
@ -730,7 +768,7 @@ void ARMXEmitter::WriteStoreOp(u32 Op, ARMReg Rt, ARMReg Rn, Operand2 Rm, bool R
|
|||
bool SignedLoad = false;
|
||||
|
||||
if (op == -1)
|
||||
_assert_msg_(DYNA_REC, false, "%s does not support %d", LoadStoreNames[Op], Rm.GetType());
|
||||
_dbg_assert_msg_(DYNA_REC, false, "%s does not support %d", LoadStoreNames[Op], Rm.GetType());
|
||||
|
||||
switch (Op)
|
||||
{
|
||||
|
@ -854,10 +892,25 @@ ARMReg ARMXEmitter::SubBase(ARMReg Reg)
|
|||
}
|
||||
|
||||
// NEON Specific
|
||||
void ARMXEmitter::VABD(IntegerSize Size, ARMReg Vd, ARMReg Vn, ARMReg Vm)
|
||||
{
|
||||
_dbg_assert_msg_(DYNA_REC, Vd >= D0, "Pass invalid register to VABD(float)");
|
||||
_dbg_assert_msg_(DYNA_REC, cpu_info.bNEON, "Can't use VABD(float) when CPU doesn't support it");
|
||||
bool register_quad = Vd >= Q0;
|
||||
|
||||
// Gets encoded as a double register
|
||||
Vd = SubBase(Vd);
|
||||
Vn = SubBase(Vn);
|
||||
Vm = SubBase(Vm);
|
||||
|
||||
Write32((0xF3 << 24) | ((Vd & 0x10) << 18) | (Size << 20) | ((Vn & 0xF) << 16) \
|
||||
| ((Vd & 0xF) << 12) | (0xD << 8) | ((Vn & 0x10) << 3) | (register_quad << 6) \
|
||||
| ((Vm & 0x10) << 2) | (Vm & 0xF));
|
||||
}
|
||||
void ARMXEmitter::VADD(IntegerSize Size, ARMReg Vd, ARMReg Vn, ARMReg Vm)
|
||||
{
|
||||
_assert_msg_(DYNA_REC, Vd >= D0, "Pass invalid register to VADD(integer)");
|
||||
_assert_msg_(DYNA_REC, cpu_info.bNEON, "Can't use VADD(integer) when CPU doesn't support it");
|
||||
_dbg_assert_msg_(DYNA_REC, Vd >= D0, "Pass invalid register to VADD(integer)");
|
||||
_dbg_assert_msg_(DYNA_REC, cpu_info.bNEON, "Can't use VADD(integer) when CPU doesn't support it");
|
||||
|
||||
bool register_quad = Vd >= Q0;
|
||||
|
||||
|
@ -868,13 +921,13 @@ void ARMXEmitter::VADD(IntegerSize Size, ARMReg Vd, ARMReg Vn, ARMReg Vm)
|
|||
|
||||
Write32((0xF2 << 24) | ((Vd & 0x10) << 18) | (Size << 20) | ((Vn & 0xF) << 16) \
|
||||
| ((Vd & 0xF) << 12) | (0x8 << 8) | ((Vn & 0x10) << 3) | (register_quad << 6) \
|
||||
| ((Vm & 0x10) << 1) | (Vm & 0xF));
|
||||
| ((Vm & 0x10) << 1) | (Vm & 0xF));
|
||||
|
||||
}
|
||||
void ARMXEmitter::VSUB(IntegerSize Size, ARMReg Vd, ARMReg Vn, ARMReg Vm)
|
||||
{
|
||||
_assert_msg_(DYNA_REC, Vd >= Q0, "Pass invalid register to VSUB(integer)");
|
||||
_assert_msg_(DYNA_REC, cpu_info.bNEON, "Can't use VSUB(integer) when CPU doesn't support it");
|
||||
_dbg_assert_msg_(DYNA_REC, Vd >= Q0, "Pass invalid register to VSUB(integer)");
|
||||
_dbg_assert_msg_(DYNA_REC, cpu_info.bNEON, "Can't use VSUB(integer) when CPU doesn't support it");
|
||||
|
||||
// Gets encoded as a double register
|
||||
Vd = SubBase(Vd);
|
||||
|
@ -883,36 +936,37 @@ void ARMXEmitter::VSUB(IntegerSize Size, ARMReg Vd, ARMReg Vn, ARMReg Vm)
|
|||
|
||||
Write32((0xF3 << 24) | ((Vd & 0x10) << 18) | (Size << 20) | ((Vn & 0xF) << 16) \
|
||||
| ((Vd & 0xF) << 12) | (0x8 << 8) | ((Vn & 0x10) << 3) | (1 << 6) \
|
||||
| ((Vm & 0x10) << 2) | (Vm & 0xF));
|
||||
| ((Vm & 0x10) << 2) | (Vm & 0xF));
|
||||
}
|
||||
|
||||
// VFP Specific
|
||||
struct VFPEnc
|
||||
{
|
||||
s16 opc1;
|
||||
s16 opc2;
|
||||
};
|
||||
// Double/single, Neon
|
||||
const VFPEnc VFPOps[][2] = {
|
||||
extern const VFPEnc VFPOps[16][2] = {
|
||||
{{0xE0, 0xA0}, {0x20, 0xD1}}, // 0: VMLA
|
||||
{{0xE0, 0xA4}, {0x22, 0xD1}}, // 1: VMLS
|
||||
{{0xE3, 0xA0}, {0x20, 0xD0}}, // 2: VADD
|
||||
{{0xE3, 0xA4}, {0x22, 0xD0}}, // 3: VSUB
|
||||
{{0xE2, 0xA0}, {0x30, 0xD1}}, // 4: VMUL
|
||||
{{0xEB, 0xAC}, { -1 /* 0x3B */, -1 /* 0x70 */}}, // 5: VABS(Vn(0x0) used for encoding)
|
||||
{{0xE8, 0xA0}, { -1, -1}}, // 6: VDIV
|
||||
{{0xEB, 0xA4}, { -1 /* 0x3B */, -1 /* 0x78 */}}, // 7: VNEG(Vn(0x1) used for encoding)
|
||||
{{0xEB, 0xAC}, { -1, -1}}, // 8: VSQRT (Vn(0x1) used for encoding)
|
||||
{{0xEB, 0xA4}, { -1, -1}}, // 9: VCMP (Vn(0x4 | #0 ? 1 : 0) used for encoding)
|
||||
{{0xEB, 0xAC}, { -1, -1}}, // 10: VCMPE (Vn(0x4 | #0 ? 1 : 0) used for encoding)
|
||||
{{ -1, -1}, {0x3B, 0x30}}, // 11: VABSi
|
||||
{{0xE1, 0xA4}, { -1, -1}}, // 1: VNMLA
|
||||
{{0xE0, 0xA4}, {0x22, 0xD1}}, // 2: VMLS
|
||||
{{0xE1, 0xA0}, { -1, -1}}, // 3: VNMLS
|
||||
{{0xE3, 0xA0}, {0x20, 0xD0}}, // 4: VADD
|
||||
{{0xE3, 0xA4}, {0x22, 0xD0}}, // 5: VSUB
|
||||
{{0xE2, 0xA0}, {0x30, 0xD1}}, // 6: VMUL
|
||||
{{0xE2, 0xA4}, { -1, -1}}, // 7: VNMUL
|
||||
{{0xEB, 0xAC}, { -1 /* 0x3B */, -1 /* 0x70 */}}, // 8: VABS(Vn(0x0) used for encoding)
|
||||
{{0xE8, 0xA0}, { -1, -1}}, // 9: VDIV
|
||||
{{0xEB, 0xA4}, { -1 /* 0x3B */, -1 /* 0x78 */}}, // 10: VNEG(Vn(0x1) used for encoding)
|
||||
{{0xEB, 0xAC}, { -1, -1}}, // 11: VSQRT (Vn(0x1) used for encoding)
|
||||
{{0xEB, 0xA4}, { -1, -1}}, // 12: VCMP (Vn(0x4 | #0 ? 1 : 0) used for encoding)
|
||||
{{0xEB, 0xAC}, { -1, -1}}, // 13: VCMPE (Vn(0x4 | #0 ? 1 : 0) used for encoding)
|
||||
{{ -1, -1}, {0x3B, 0x30}}, // 14: VABSi
|
||||
};
|
||||
const char *VFPOpNames[] = {
|
||||
|
||||
extern const char *VFPOpNames[16] = {
|
||||
"VMLA",
|
||||
"VNMLA",
|
||||
"VMLS",
|
||||
"VNMLS",
|
||||
"VADD",
|
||||
"VSUB",
|
||||
"VMUL",
|
||||
"VNMUL",
|
||||
"VABS",
|
||||
"VDIV",
|
||||
"VNEG",
|
||||
|
@ -966,6 +1020,7 @@ u32 ARMXEmitter::EncodeVm(ARMReg Vm)
|
|||
else
|
||||
return ((Reg & 0x1) << 5) | (Reg >> 1);
|
||||
}
|
||||
|
||||
void ARMXEmitter::WriteVFPDataOp(u32 Op, ARMReg Vd, ARMReg Vn, ARMReg Vm)
|
||||
{
|
||||
bool quad_reg = Vd >= Q0;
|
||||
|
@ -973,39 +1028,42 @@ void ARMXEmitter::WriteVFPDataOp(u32 Op, ARMReg Vd, ARMReg Vn, ARMReg Vm)
|
|||
|
||||
VFPEnc enc = VFPOps[Op][quad_reg];
|
||||
if (enc.opc1 == -1 && enc.opc2 == -1)
|
||||
_assert_msg_(DYNA_REC, false, "%s does not support %s", VFPOpNames[Op], quad_reg ? "NEON" : "VFP");
|
||||
_dbg_assert_msg_(DYNA_REC, false, "%s does not support %s", VFPOpNames[Op], quad_reg ? "NEON" : "VFP");
|
||||
u32 VdEnc = EncodeVd(Vd);
|
||||
u32 VnEnc = EncodeVn(Vn);
|
||||
u32 VmEnc = EncodeVm(Vm);
|
||||
u32 cond = quad_reg ? (0xF << 28) : condition;
|
||||
|
||||
|
||||
Write32(cond | (enc.opc1 << 20) | VnEnc | VdEnc | (enc.opc2 << 4) | (quad_reg << 6) | (double_reg << 8) | VmEnc);
|
||||
}
|
||||
void ARMXEmitter::VMLA(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(0, Vd, Vn, Vm); }
|
||||
void ARMXEmitter::VMLS(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(1, Vd, Vn, Vm); }
|
||||
void ARMXEmitter::VADD(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(2, Vd, Vn, Vm); }
|
||||
void ARMXEmitter::VSUB(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(3, Vd, Vn, Vm); }
|
||||
void ARMXEmitter::VMUL(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(4, Vd, Vn, Vm); }
|
||||
void ARMXEmitter::VABS(ARMReg Vd, ARMReg Vm){ WriteVFPDataOp(5, Vd, D0, Vm); }
|
||||
void ARMXEmitter::VDIV(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(6, Vd, Vn, Vm); }
|
||||
void ARMXEmitter::VNEG(ARMReg Vd, ARMReg Vm){ WriteVFPDataOp(7, Vd, D1, Vm); }
|
||||
void ARMXEmitter::VSQRT(ARMReg Vd, ARMReg Vm){ WriteVFPDataOp(8, Vd, D1, Vm); }
|
||||
void ARMXEmitter::VCMP(ARMReg Vd, ARMReg Vm){ WriteVFPDataOp(9, Vd, D4, Vm); }
|
||||
void ARMXEmitter::VCMPE(ARMReg Vd, ARMReg Vm){ WriteVFPDataOp(10, Vd, D4, Vm); }
|
||||
void ARMXEmitter::VCMP(ARMReg Vd){ WriteVFPDataOp(9, Vd, D5, D0); }
|
||||
void ARMXEmitter::VCMPE(ARMReg Vd){ WriteVFPDataOp(10, Vd, D5, D0); }
|
||||
void ARMXEmitter::VNMLA(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(1, Vd, Vn, Vm); }
|
||||
void ARMXEmitter::VMLS(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(2, Vd, Vn, Vm); }
|
||||
void ARMXEmitter::VNMLS(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(3, Vd, Vn, Vm); }
|
||||
void ARMXEmitter::VADD(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(4, Vd, Vn, Vm); }
|
||||
void ARMXEmitter::VSUB(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(5, Vd, Vn, Vm); }
|
||||
void ARMXEmitter::VMUL(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(6, Vd, Vn, Vm); }
|
||||
void ARMXEmitter::VNMUL(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(7, Vd, Vn, Vm); }
|
||||
void ARMXEmitter::VABS(ARMReg Vd, ARMReg Vm){ WriteVFPDataOp(8, Vd, D0, Vm); }
|
||||
void ARMXEmitter::VDIV(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(9, Vd, Vn, Vm); }
|
||||
void ARMXEmitter::VNEG(ARMReg Vd, ARMReg Vm){ WriteVFPDataOp(10, Vd, D1, Vm); }
|
||||
void ARMXEmitter::VSQRT(ARMReg Vd, ARMReg Vm){ WriteVFPDataOp(11, Vd, D1, Vm); }
|
||||
void ARMXEmitter::VCMP(ARMReg Vd, ARMReg Vm){ WriteVFPDataOp(12, Vd, D4, Vm); }
|
||||
void ARMXEmitter::VCMPE(ARMReg Vd, ARMReg Vm){ WriteVFPDataOp(13, Vd, D4, Vm); }
|
||||
void ARMXEmitter::VCMP(ARMReg Vd){ WriteVFPDataOp(12, Vd, D5, D0); }
|
||||
void ARMXEmitter::VCMPE(ARMReg Vd){ WriteVFPDataOp(13, Vd, D5, D0); }
|
||||
|
||||
void ARMXEmitter::VLDR(ARMReg Dest, ARMReg Base, s16 offset)
|
||||
{
|
||||
_assert_msg_(DYNA_REC, Dest >= S0 && Dest <= D31, "Passed Invalid dest register to VLDR");
|
||||
_assert_msg_(DYNA_REC, Base <= R15, "Passed invalid Base register to VLDR");
|
||||
_dbg_assert_msg_(DYNA_REC, Dest >= S0 && Dest <= D31, "Passed Invalid dest register to VLDR");
|
||||
_dbg_assert_msg_(DYNA_REC, Base <= R15, "Passed invalid Base register to VLDR");
|
||||
|
||||
bool Add = offset >= 0 ? true : false;
|
||||
u32 imm = abs(offset);
|
||||
|
||||
_assert_msg_(DYNA_REC, (imm & 0xC03) == 0, "VLDR: Offset needs to be word aligned and small enough");
|
||||
_dbg_assert_msg_(DYNA_REC, (imm & 0xC03) == 0, "VLDR: Offset needs to be word aligned and small enough");
|
||||
|
||||
if (imm & 0xC03)
|
||||
if (imm & 0xC03)
|
||||
ERROR_LOG(DYNA_REC, "VLDR: Bad offset %08x", imm);
|
||||
|
||||
bool single_reg = Dest < D0;
|
||||
|
@ -1015,25 +1073,25 @@ void ARMXEmitter::VLDR(ARMReg Dest, ARMReg Base, s16 offset)
|
|||
if (single_reg)
|
||||
{
|
||||
Write32(condition | (0xD << 24) | (Add << 23) | ((Dest & 0x1) << 22) | (1 << 20) | (Base << 16) \
|
||||
| ((Dest & 0x1E) << 11) | (10 << 8) | (imm >> 2));
|
||||
| ((Dest & 0x1E) << 11) | (10 << 8) | (imm >> 2));
|
||||
}
|
||||
else
|
||||
{
|
||||
Write32(condition | (0xD << 24) | (Add << 23) | ((Dest & 0x10) << 18) | (1 << 20) | (Base << 16) \
|
||||
| ((Dest & 0xF) << 12) | (11 << 8) | (imm >> 2));
|
||||
| ((Dest & 0xF) << 12) | (11 << 8) | (imm >> 2));
|
||||
}
|
||||
}
|
||||
void ARMXEmitter::VSTR(ARMReg Src, ARMReg Base, s16 offset)
|
||||
{
|
||||
_assert_msg_(DYNA_REC, Src >= S0 && Src <= D31, "Passed invalid src register to VSTR");
|
||||
_assert_msg_(DYNA_REC, Base <= R15, "Passed invalid base register to VSTR");
|
||||
_dbg_assert_msg_(DYNA_REC, Src >= S0 && Src <= D31, "Passed invalid src register to VSTR");
|
||||
_dbg_assert_msg_(DYNA_REC, Base <= R15, "Passed invalid base register to VSTR");
|
||||
|
||||
bool Add = offset >= 0 ? true : false;
|
||||
u32 imm = abs(offset);
|
||||
|
||||
_assert_msg_(DYNA_REC, (imm & 0xC03) == 0, "VSTR: Offset needs to be word aligned and small enough");
|
||||
_dbg_assert_msg_(DYNA_REC, (imm & 0xC03) == 0, "VSTR: Offset needs to be word aligned and small enough");
|
||||
|
||||
if (imm & 0xC03)
|
||||
if (imm & 0xC03)
|
||||
ERROR_LOG(DYNA_REC, "VSTR: Bad offset %08x", imm);
|
||||
|
||||
bool single_reg = Src < D0;
|
||||
|
@ -1043,12 +1101,12 @@ void ARMXEmitter::VSTR(ARMReg Src, ARMReg Base, s16 offset)
|
|||
if (single_reg)
|
||||
{
|
||||
Write32(condition | (0xD << 24) | (Add << 23) | ((Src & 0x1) << 22) | (Base << 16) \
|
||||
| ((Src & 0x1E) << 11) | (10 << 8) | (imm >> 2));
|
||||
| ((Src & 0x1E) << 11) | (10 << 8) | (imm >> 2));
|
||||
}
|
||||
else
|
||||
{
|
||||
Write32(condition | (0xD << 24) | (Add << 23) | ((Src & 0x10) << 18) | (Base << 16) \
|
||||
| ((Src & 0xF) << 12) | (11 << 8) | (imm >> 2));
|
||||
| ((Src & 0xF) << 12) | (11 << 8) | (imm >> 2));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1063,15 +1121,20 @@ void ARMXEmitter::VMSR(ARMReg Rt) {
|
|||
}
|
||||
|
||||
// VFP and ASIMD
|
||||
void ARMXEmitter::VMOV(ARMReg Dest, Operand2 op2)
|
||||
{
|
||||
_dbg_assert_msg_(DYNA_REC, cpu_info.bVFPv3, "VMOV #imm requires VFPv3");
|
||||
Write32(condition | (0xEB << 20) | EncodeVd(Dest) | (0xA << 8) | op2.Imm8VFP());
|
||||
}
|
||||
void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src, bool high)
|
||||
{
|
||||
_assert_msg_(DYNA_REC, Src < S0, "This VMOV doesn't support SRC other than ARM Reg");
|
||||
_assert_msg_(DYNA_REC, Dest >= D0, "This VMOV doesn't support DEST other than VFP");
|
||||
_dbg_assert_msg_(DYNA_REC, Src < S0, "This VMOV doesn't support SRC other than ARM Reg");
|
||||
_dbg_assert_msg_(DYNA_REC, Dest >= D0, "This VMOV doesn't support DEST other than VFP");
|
||||
|
||||
Dest = SubBase(Dest);
|
||||
|
||||
Write32(condition | (0xE << 24) | (high << 21) | ((Dest & 0xF) << 16) | (Src << 12) \
|
||||
| (11 << 8) | ((Dest & 0x10) << 3) | (1 << 4));
|
||||
| (0xB << 8) | ((Dest & 0x10) << 3) | (1 << 4));
|
||||
}
|
||||
|
||||
void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src)
|
||||
|
@ -1091,7 +1154,7 @@ void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src)
|
|||
else
|
||||
{
|
||||
// Move 64bit from Arm reg
|
||||
_assert_msg_(DYNA_REC, false, "This VMOV doesn't support moving 64bit ARM to NEON");
|
||||
_dbg_assert_msg_(DYNA_REC, false, "This VMOV doesn't support moving 64bit ARM to NEON");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1111,14 +1174,14 @@ void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src)
|
|||
else
|
||||
{
|
||||
// Move 64bit To Arm reg
|
||||
_assert_msg_(DYNA_REC, false, "This VMOV doesn't support moving 64bit ARM From NEON");
|
||||
_dbg_assert_msg_(DYNA_REC, false, "This VMOV doesn't support moving 64bit ARM From NEON");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Move Arm reg to Arm reg
|
||||
_assert_msg_(DYNA_REC, false, "VMOV doesn't support moving ARM registers");
|
||||
_dbg_assert_msg_(DYNA_REC, false, "VMOV doesn't support moving ARM registers");
|
||||
}
|
||||
}
|
||||
// Moving NEON registers
|
||||
|
@ -1127,7 +1190,7 @@ void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src)
|
|||
bool Single = DestSize == 1;
|
||||
bool Quad = DestSize == 4;
|
||||
|
||||
_assert_msg_(DYNA_REC, SrcSize == DestSize, "VMOV doesn't support moving different register sizes");
|
||||
_dbg_assert_msg_(DYNA_REC, SrcSize == DestSize, "VMOV doesn't support moving different register sizes");
|
||||
|
||||
Dest = SubBase(Dest);
|
||||
Src = SubBase(Src);
|
||||
|
@ -1142,7 +1205,7 @@ void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src)
|
|||
// Double and quad
|
||||
if (Quad)
|
||||
{
|
||||
_assert_msg_(DYNA_REC, cpu_info.bNEON, "Trying to use quad registers when you don't support ASIMD.");
|
||||
_dbg_assert_msg_(DYNA_REC, cpu_info.bNEON, "Trying to use quad registers when you don't support ASIMD.");
|
||||
// Gets encoded as a Double register
|
||||
Write32((0xF2 << 24) | ((Dest & 0x10) << 18) | (2 << 20) | ((Src & 0xF) << 16) \
|
||||
| ((Dest & 0xF) << 12) | (1 << 8) | ((Src & 0x10) << 3) | (1 << 6) \
|
||||
|
@ -1160,13 +1223,39 @@ void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src)
|
|||
void ARMXEmitter::VCVT(ARMReg Dest, ARMReg Source, int flags)
|
||||
{
|
||||
bool single_reg = (Dest < D0) && (Source < D0);
|
||||
bool single_double = !single_reg && (Source < D0 || Dest < D0);
|
||||
bool single_to_double = Source < D0;
|
||||
int op = ((flags & TO_INT) ? (flags & ROUND_TO_ZERO) : (flags & IS_SIGNED)) ? 1 : 0;
|
||||
int op2 = ((flags & TO_INT) ? (flags & IS_SIGNED) : 0) ? 1 : 0;
|
||||
Dest = SubBase(Dest);
|
||||
Source = SubBase(Source);
|
||||
|
||||
if (single_reg)
|
||||
if (single_double)
|
||||
{
|
||||
// S32<->F64
|
||||
if ((flags & TO_INT) || (flags & TO_FLOAT))
|
||||
{
|
||||
if (single_to_double)
|
||||
{
|
||||
Write32(condition | (0x1D << 23) | ((Dest & 0x10) << 18) | (0x7 << 19) \
|
||||
| ((Dest & 0xF) << 12) | (op << 7) | (0x2D << 6) | ((Source & 0x1) << 5) | (Source >> 1));
|
||||
} else {
|
||||
Write32(condition | (0x1D << 23) | ((Dest & 0x1) << 22) | (0x7 << 19) | ((flags & TO_INT) << 18) | (op2 << 16) \
|
||||
| ((Dest & 0x1E) << 11) | (op << 7) | (0x2D << 6) | ((Source & 0x10) << 1) | (Source & 0xF));
|
||||
}
|
||||
}
|
||||
// F32<->F64
|
||||
else {
|
||||
if (single_to_double)
|
||||
{
|
||||
Write32(condition | (0x1D << 23) | ((Dest & 0x10) << 18) | (0x3 << 20) | (0x7 << 16) \
|
||||
| ((Dest & 0xF) << 12) | (0x2B << 6) | ((Source & 0x1) << 5) | (Source >> 1));
|
||||
} else {
|
||||
Write32(condition | (0x1D << 23) | ((Dest & 0x1) << 22) | (0x3 << 20) | (0x7 << 16) \
|
||||
| ((Dest & 0x1E) << 11) | (0x2F << 6) | ((Source & 0x10) << 1) | (Source & 0xF));
|
||||
}
|
||||
}
|
||||
} else if (single_reg) {
|
||||
Write32(condition | (0x1D << 23) | ((Dest & 0x1) << 22) | (0x7 << 19) | ((flags & TO_INT) << 18) | (op2 << 16) \
|
||||
| ((Dest & 0x1E) << 11) | (op << 7) | (0x29 << 6) | ((Source & 0x1) << 5) | (Source >> 1));
|
||||
} else {
|
||||
|
|
|
@ -365,7 +365,7 @@ private:
|
|||
u32 EncodeVn(ARMReg Vn);
|
||||
u32 EncodeVm(ARMReg Vm);
|
||||
void WriteVFPDataOp(u32 Op, ARMReg Vd, ARMReg Vn, ARMReg Vm);
|
||||
|
||||
|
||||
void Write4OpMultiply(u32 op, ARMReg destLo, ARMReg destHi, ARMReg rn, ARMReg rm);
|
||||
|
||||
// New Ops
|
||||
|
@ -431,6 +431,7 @@ public:
|
|||
void B (ARMReg src);
|
||||
void BL(const void *fnptr);
|
||||
void BL(ARMReg src);
|
||||
bool BLInRange(const void *fnptr);
|
||||
|
||||
void PUSH(const int num, ...);
|
||||
void POP(const int num, ...);
|
||||
|
@ -453,6 +454,8 @@ public:
|
|||
void LSLS(ARMReg dest, ARMReg src, Operand2 op2);
|
||||
void LSLS(ARMReg dest, ARMReg src, ARMReg op2);
|
||||
void LSR (ARMReg dest, ARMReg src, Operand2 op2);
|
||||
void ASR (ARMReg dest, ARMReg src, Operand2 op2);
|
||||
void ASRS(ARMReg dest, ARMReg src, Operand2 op2);
|
||||
void SBC (ARMReg dest, ARMReg src, Operand2 op2);
|
||||
void SBCS(ARMReg dest, ARMReg src, Operand2 op2);
|
||||
void RBIT(ARMReg dest, ARMReg src);
|
||||
|
@ -484,6 +487,7 @@ public:
|
|||
void MULS(ARMReg dest, ARMReg src, ARMReg op2);
|
||||
|
||||
void UMULL(ARMReg destLo, ARMReg destHi, ARMReg rn, ARMReg rm);
|
||||
void UMULLS(ARMReg destLo, ARMReg destHi, ARMReg rn, ARMReg rm);
|
||||
void SMULL(ARMReg destLo, ARMReg destHi, ARMReg rn, ARMReg rm);
|
||||
|
||||
void UMLAL(ARMReg destLo, ARMReg destHi, ARMReg rn, ARMReg rm);
|
||||
|
@ -530,6 +534,7 @@ public:
|
|||
// Subtracts the base from the register to give us the real one
|
||||
ARMReg SubBase(ARMReg Reg);
|
||||
// NEON Only
|
||||
void VABD(IntegerSize Size, ARMReg Vd, ARMReg Vn, ARMReg Vm);
|
||||
void VADD(IntegerSize Size, ARMReg Vd, ARMReg Vn, ARMReg Vm);
|
||||
void VSUB(IntegerSize Size, ARMReg Vd, ARMReg Vn, ARMReg Vm);
|
||||
|
||||
|
@ -541,6 +546,10 @@ public:
|
|||
// Compares against zero
|
||||
void VCMP(ARMReg Vd);
|
||||
void VCMPE(ARMReg Vd);
|
||||
|
||||
void VNMLA(ARMReg Vd, ARMReg Vn, ARMReg Vm);
|
||||
void VNMLS(ARMReg Vd, ARMReg Vn, ARMReg Vm);
|
||||
void VNMUL(ARMReg Vd, ARMReg Vn, ARMReg Vm);
|
||||
void VDIV(ARMReg Vd, ARMReg Vn, ARMReg Vm);
|
||||
void VSQRT(ARMReg Vd, ARMReg Vm);
|
||||
|
||||
|
@ -552,6 +561,7 @@ public:
|
|||
void VMUL(ARMReg Vd, ARMReg Vn, ARMReg Vm);
|
||||
void VMLA(ARMReg Vd, ARMReg Vn, ARMReg Vm);
|
||||
void VMLS(ARMReg Vd, ARMReg Vn, ARMReg Vm);
|
||||
void VMOV(ARMReg Dest, Operand2 op2);
|
||||
void VMOV(ARMReg Dest, ARMReg Src, bool high);
|
||||
void VMOV(ARMReg Dest, ARMReg Src);
|
||||
void VCVT(ARMReg Dest, ARMReg Src, int flags);
|
||||
|
@ -564,7 +574,7 @@ public:
|
|||
|
||||
// Wrapper around MOVT/MOVW with fallbacks.
|
||||
void MOVI2R(ARMReg reg, u32 val, bool optimize = true);
|
||||
void MOVI2F(ARMReg dest, float val, ARMReg tempReg);
|
||||
void MOVI2F(ARMReg dest, float val, ARMReg tempReg, bool negate = false);
|
||||
|
||||
void ADDI2R(ARMReg rd, ARMReg rs, u32 val, ARMReg scratch);
|
||||
void ANDI2R(ARMReg rd, ARMReg rs, u32 val, ARMReg scratch);
|
||||
|
@ -624,7 +634,11 @@ public:
|
|||
// Start over if you need to change the code (call FreeCodeSpace(), AllocCodeSpace()).
|
||||
void WriteProtect()
|
||||
{
|
||||
WriteProtectMemory(region, region_size, true);
|
||||
WriteProtectMemory(region, region_size, true);
|
||||
}
|
||||
void UnWriteProtect()
|
||||
{
|
||||
UnWriteProtectMemory(region, region_size, false);
|
||||
}
|
||||
|
||||
void ResetCodePtr()
|
||||
|
@ -636,8 +650,24 @@ public:
|
|||
{
|
||||
return region_size - (GetCodePtr() - region);
|
||||
}
|
||||
|
||||
u8 *GetBasePtr() {
|
||||
return region;
|
||||
}
|
||||
|
||||
size_t GetOffset(u8 *ptr) {
|
||||
return ptr - region;
|
||||
}
|
||||
};
|
||||
|
||||
// VFP Specific
|
||||
struct VFPEnc {
|
||||
s16 opc1;
|
||||
s16 opc2;
|
||||
};
|
||||
extern const VFPEnc VFPOps[16][2];
|
||||
extern const char *VFPOpNames[16];
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif // _DOLPHIN_INTEL_CODEGEN_
|
||||
|
|
|
@ -136,7 +136,6 @@ static BOOL GetModuleNameFromAddress( UINT address, LPTSTR lpszModule )
|
|||
static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, LPTSTR lpszSymbol )
|
||||
{
|
||||
BOOL ret = FALSE;
|
||||
DWORD dwDisp = 0;
|
||||
DWORD dwSymSize = 10000;
|
||||
TCHAR lpszUnDSymbol[BUFFERSIZE]=_T("?");
|
||||
CHAR lpszNonUnicodeUnDSymbol[BUFFERSIZE]="?";
|
||||
|
@ -153,9 +152,11 @@ static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, L
|
|||
|
||||
// Get symbol info for IP
|
||||
#ifndef _M_X64
|
||||
DWORD dwDisp = 0;
|
||||
if ( SymGetSymFromAddr( GetCurrentProcess(), (ULONG)fnAddress, &dwDisp, pSym ) )
|
||||
#else
|
||||
//makes it compile but hell im not sure if this works...
|
||||
DWORD64 dwDisp = 0;
|
||||
if ( SymGetSymFromAddr( GetCurrentProcess(), (ULONG)fnAddress, (PDWORD64)&dwDisp, pSym ) )
|
||||
#endif
|
||||
{
|
||||
|
|
|
@ -20,45 +20,30 @@
|
|||
|
||||
namespace {
|
||||
|
||||
static void ParseLine(const std::string& line, std::string* keyOut, std::string* valueOut, std::string* commentOut)
|
||||
static void ParseLine(const std::string& line, std::string* keyOut, std::string* valueOut)
|
||||
{
|
||||
if (line[0] == '#')
|
||||
return;
|
||||
|
||||
int FirstEquals = (int)line.find("=", 0);
|
||||
int FirstCommentChar = -1;
|
||||
|
||||
// Comments
|
||||
if (FirstCommentChar < 0)
|
||||
FirstCommentChar =
|
||||
(int)line.find("#", FirstEquals > 0 ? FirstEquals : 0);
|
||||
if (FirstCommentChar < 0 && line[0] == ';')
|
||||
FirstCommentChar = 0;
|
||||
|
||||
// Allow preservation of spacing before comment
|
||||
if (FirstCommentChar > 0)
|
||||
{
|
||||
while (line[FirstCommentChar - 1] == ' ' || line[FirstCommentChar - 1] == 9) // 9 == tab
|
||||
{
|
||||
FirstCommentChar--;
|
||||
}
|
||||
}
|
||||
|
||||
if ((FirstEquals >= 0) && ((FirstCommentChar < 0) || (FirstEquals < FirstCommentChar)))
|
||||
if (FirstEquals >= 0)
|
||||
{
|
||||
// Yes, a valid line!
|
||||
*keyOut = StripSpaces(line.substr(0, FirstEquals));
|
||||
if (commentOut) *commentOut = FirstCommentChar > 0 ? line.substr(FirstCommentChar) : std::string("");
|
||||
if (valueOut) *valueOut = StripQuotes(StripSpaces(line.substr(FirstEquals + 1, FirstCommentChar - FirstEquals - 1)));
|
||||
if (valueOut) *valueOut = StripQuotes(StripSpaces(line.substr(FirstEquals + 1, std::string::npos)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::string* IniFile::Section::GetLine(const char* key, std::string* valueOut, std::string* commentOut)
|
||||
std::string* IniFile::Section::GetLine(const char* key, std::string* valueOut)
|
||||
{
|
||||
for (std::vector<std::string>::iterator iter = lines.begin(); iter != lines.end(); ++iter)
|
||||
{
|
||||
std::string& line = *iter;
|
||||
std::string lineKey;
|
||||
ParseLine(line, &lineKey, valueOut, commentOut);
|
||||
ParseLine(line, &lineKey, valueOut);
|
||||
if (!strcasecmp(lineKey.c_str(), key))
|
||||
return &line;
|
||||
}
|
||||
|
@ -67,12 +52,12 @@ std::string* IniFile::Section::GetLine(const char* key, std::string* valueOut, s
|
|||
|
||||
void IniFile::Section::Set(const char* key, const char* newValue)
|
||||
{
|
||||
std::string value, commented;
|
||||
std::string* line = GetLine(key, &value, &commented);
|
||||
std::string value;
|
||||
std::string* line = GetLine(key, &value);
|
||||
if (line)
|
||||
{
|
||||
// Change the value - keep the key and comment
|
||||
*line = StripSpaces(key) + " = " + newValue + commented;
|
||||
*line = StripSpaces(key) + " = " + newValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -91,7 +76,7 @@ void IniFile::Section::Set(const char* key, const std::string& newValue, const s
|
|||
|
||||
bool IniFile::Section::Get(const char* key, std::string* value, const char* defaultValue)
|
||||
{
|
||||
std::string* line = GetLine(key, value, 0);
|
||||
std::string* line = GetLine(key, value);
|
||||
if (!line)
|
||||
{
|
||||
if (defaultValue)
|
||||
|
@ -224,7 +209,7 @@ bool IniFile::Section::Exists(const char *key) const
|
|||
for (std::vector<std::string>::const_iterator iter = lines.begin(); iter != lines.end(); ++iter)
|
||||
{
|
||||
std::string lineKey;
|
||||
ParseLine(*iter, &lineKey, NULL, NULL);
|
||||
ParseLine(*iter, &lineKey, NULL);
|
||||
if (!strcasecmp(lineKey.c_str(), key))
|
||||
return true;
|
||||
}
|
||||
|
@ -233,7 +218,7 @@ bool IniFile::Section::Exists(const char *key) const
|
|||
|
||||
bool IniFile::Section::Delete(const char *key)
|
||||
{
|
||||
std::string* line = GetLine(key, 0, 0);
|
||||
std::string* line = GetLine(key, 0);
|
||||
for (std::vector<std::string>::iterator liter = lines.begin(); liter != lines.end(); ++liter)
|
||||
{
|
||||
if (line == &*liter)
|
||||
|
@ -313,7 +298,7 @@ bool IniFile::DeleteKey(const char* sectionName, const char* key)
|
|||
Section* section = GetSection(sectionName);
|
||||
if (!section)
|
||||
return false;
|
||||
std::string* line = section->GetLine(key, 0, 0);
|
||||
std::string* line = section->GetLine(key, 0);
|
||||
for (std::vector<std::string>::iterator liter = section->lines.begin(); liter != section->lines.end(); ++liter)
|
||||
{
|
||||
if (line == &(*liter))
|
||||
|
@ -335,7 +320,7 @@ bool IniFile::GetKeys(const char* sectionName, std::vector<std::string>& keys) c
|
|||
for (std::vector<std::string>::const_iterator liter = section->lines.begin(); liter != section->lines.end(); ++liter)
|
||||
{
|
||||
std::string key;
|
||||
ParseLine(*liter, &key, 0, 0);
|
||||
ParseLine(*liter, &key, 0);
|
||||
keys.push_back(key);
|
||||
}
|
||||
return true;
|
||||
|
@ -421,11 +406,6 @@ bool IniFile::Load(const char* filename)
|
|||
// New section!
|
||||
std::string sub = line.substr(1, endpos - 1);
|
||||
sections.push_back(Section(sub));
|
||||
|
||||
if (endpos + 1 < line.size())
|
||||
{
|
||||
sections[sections.size() - 1].comment = line.substr(endpos + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -459,7 +439,7 @@ bool IniFile::Save(const char* filename)
|
|||
|
||||
if (section.name != "")
|
||||
{
|
||||
out << "[" << section.name << "]" << section.comment << std::endl;
|
||||
out << "[" << section.name << "]" << std::endl;
|
||||
}
|
||||
|
||||
for (std::vector<std::string>::const_iterator liter = section.lines.begin(); liter != section.lines.end(); ++liter)
|
||||
|
|
|
@ -25,7 +25,7 @@ public:
|
|||
bool Exists(const char *key) const;
|
||||
bool Delete(const char *key);
|
||||
|
||||
std::string* GetLine(const char* key, std::string* valueOut, std::string* commentOut);
|
||||
std::string* GetLine(const char* key, std::string* valueOut);
|
||||
void Set(const char* key, const char* newValue);
|
||||
void Set(const char* key, const std::string& newValue, const std::string& defaultValue);
|
||||
|
||||
|
@ -70,7 +70,6 @@ public:
|
|||
protected:
|
||||
std::vector<std::string> lines;
|
||||
std::string name;
|
||||
std::string comment;
|
||||
};
|
||||
|
||||
bool Load(const char* filename);
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <linux/ashmem.h>
|
||||
#endif
|
||||
#endif
|
||||
#include <set>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
static const char* ram_temp_file = "/tmp/gc_mem.tmp";
|
||||
|
@ -151,7 +152,11 @@ u8* MemArena::Find4GBBase()
|
|||
PanicAlert("Failed to map 1 GB of memory space: %s", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
munmap(base, 0x31000000);
|
||||
#ifndef ANDROID
|
||||
// Android 4.3 changes how munmap works which causes crashes.
|
||||
// Keep the memory space after allocating it...
|
||||
munmap(base, MemSize);
|
||||
#endif
|
||||
return static_cast<u8*>(base);
|
||||
#endif
|
||||
#endif
|
||||
|
@ -214,27 +219,7 @@ static bool Memory_TryBase(u8 *base, const MemoryView *views, int num_views, u32
|
|||
|
||||
bail:
|
||||
// Argh! ERROR! Free what we grabbed so far so we can try again.
|
||||
for (int j = 0; j <= i; j++)
|
||||
{
|
||||
SKIP(flags, views[i].flags);
|
||||
if (views[j].out_ptr_low && *views[j].out_ptr_low)
|
||||
{
|
||||
arena->ReleaseView(*views[j].out_ptr_low, views[j].size);
|
||||
*views[j].out_ptr_low = NULL;
|
||||
}
|
||||
if (*views[j].out_ptr)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
arena->ReleaseView(*views[j].out_ptr, views[j].size);
|
||||
#else
|
||||
if (!(views[j].flags & MV_MIRROR_PREVIOUS))
|
||||
{
|
||||
arena->ReleaseView(*views[j].out_ptr, views[j].size);
|
||||
}
|
||||
#endif
|
||||
*views[j].out_ptr = NULL;
|
||||
}
|
||||
}
|
||||
MemoryMap_Shutdown(views, i+1, flags, arena);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -300,15 +285,20 @@ u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena
|
|||
|
||||
void MemoryMap_Shutdown(const MemoryView *views, int num_views, u32 flags, MemArena *arena)
|
||||
{
|
||||
std::set<void*> freeset;
|
||||
for (int i = 0; i < num_views; i++)
|
||||
{
|
||||
SKIP(flags, views[i].flags);
|
||||
if (views[i].out_ptr_low && *views[i].out_ptr_low)
|
||||
arena->ReleaseView(*views[i].out_ptr_low, views[i].size);
|
||||
if (*views[i].out_ptr && (views[i].out_ptr_low && *views[i].out_ptr != *views[i].out_ptr_low))
|
||||
arena->ReleaseView(*views[i].out_ptr, views[i].size);
|
||||
*views[i].out_ptr = NULL;
|
||||
if (views[i].out_ptr_low)
|
||||
*views[i].out_ptr_low = NULL;
|
||||
const MemoryView* view = &views[i];
|
||||
u8** outptrs[2] = {view->out_ptr_low, view->out_ptr};
|
||||
for (int j = 0; j < 2; j++)
|
||||
{
|
||||
u8** outptr = outptrs[j];
|
||||
if (outptr && *outptr && !freeset.count(*outptr))
|
||||
{
|
||||
arena->ReleaseView(*outptr, view->size);
|
||||
freeset.insert(*outptr);
|
||||
*outptr = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,39 @@ bool AsciiToHex(const char* _szValue, u32& result)
|
|||
|
||||
bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list args)
|
||||
{
|
||||
int writtenCount = vsnprintf(out, outsize, format, args);
|
||||
int writtenCount;
|
||||
|
||||
#ifdef _WIN32
|
||||
// You would think *printf are simple, right? Iterate on each character,
|
||||
// if it's a format specifier handle it properly, etc.
|
||||
//
|
||||
// Nooooo. Not according to the C standard.
|
||||
//
|
||||
// According to the C99 standard (7.19.6.1 "The fprintf function")
|
||||
// The format shall be a multibyte character sequence
|
||||
//
|
||||
// Because some character encodings might have '%' signs in the middle of
|
||||
// a multibyte sequence (SJIS for example only specifies that the first
|
||||
// byte of a 2 byte sequence is "high", the second byte can be anything),
|
||||
// printf functions have to decode the multibyte sequences and try their
|
||||
// best to not screw up.
|
||||
//
|
||||
// Unfortunately, on Windows, the locale for most languages is not UTF-8
|
||||
// as we would need. Notably, for zh_TW, Windows chooses EUC-CN as the
|
||||
// locale, and completely fails when trying to decode UTF-8 as EUC-CN.
|
||||
//
|
||||
// On the other hand, the fix is simple: because we use UTF-8, no such
|
||||
// multibyte handling is required as we can simply assume that no '%' char
|
||||
// will be present in the middle of a multibyte sequence.
|
||||
//
|
||||
// This is why we lookup an ANSI (cp1252) locale here and use _vsnprintf_l.
|
||||
static locale_t c_locale = NULL;
|
||||
if (!c_locale)
|
||||
c_locale = _create_locale(LC_ALL, ".1252");
|
||||
writtenCount = _vsnprintf_l(out, outsize, format, c_locale, args);
|
||||
#else
|
||||
writtenCount = vsnprintf(out, outsize, format, args);
|
||||
#endif
|
||||
|
||||
if (writtenCount > 0 && writtenCount < outsize)
|
||||
{
|
||||
|
@ -58,10 +90,9 @@ std::string StringFromFormat(const char* format, ...)
|
|||
va_start(args, format);
|
||||
required = _vscprintf(format, args);
|
||||
buf = new char[required + 1];
|
||||
vsnprintf(buf, required, format, args);
|
||||
CharArrayFromFormatV(buf, required + 1, format, args);
|
||||
va_end(args);
|
||||
|
||||
buf[required] = '\0';
|
||||
std::string temp = buf;
|
||||
delete[] buf;
|
||||
#else
|
||||
|
|
|
@ -72,6 +72,14 @@ static void __cpuid(int info[4], int x)
|
|||
#endif
|
||||
}
|
||||
|
||||
#define _XCR_XFEATURE_ENABLED_MASK 0
|
||||
static unsigned long long _xgetbv(unsigned int index)
|
||||
{
|
||||
unsigned int eax, edx;
|
||||
__asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(index));
|
||||
return ((unsigned long long)edx << 32) | eax;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#include "Common.h"
|
||||
|
@ -149,8 +157,17 @@ void CPUInfo::Detect()
|
|||
if ((cpu_id[2] >> 9) & 1) bSSSE3 = true;
|
||||
if ((cpu_id[2] >> 19) & 1) bSSE4_1 = true;
|
||||
if ((cpu_id[2] >> 20) & 1) bSSE4_2 = true;
|
||||
if ((cpu_id[2] >> 28) & 1) bAVX = true;
|
||||
if ((cpu_id[2] >> 25) & 1) bAES = true;
|
||||
|
||||
// AVX support requires 3 separate checks:
|
||||
// - Is the AVX bit set in CPUID?
|
||||
// - Is the XSAVE bit set in CPUID?
|
||||
// - XGETBV result has the XCR bit set.
|
||||
if (((cpu_id[2] >> 28) & 1) && ((cpu_id[2] >> 27) & 1))
|
||||
{
|
||||
if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6)
|
||||
bAVX = true;
|
||||
}
|
||||
}
|
||||
if (max_ex_fn >= 0x80000004) {
|
||||
// Extract brand string
|
||||
|
|
|
@ -58,8 +58,8 @@ var describe = GetFirstStdOutLine(gitexe + cmd_describe);
|
|||
var branch = GetFirstStdOutLine(gitexe + cmd_branch);
|
||||
var isMaster = +("master" == branch);
|
||||
|
||||
// remove hash from description
|
||||
describe = describe.replace(/-[^-]+(-dirty)?$/, '$1');
|
||||
// remove hash (and trailing "-0" if needed) from description
|
||||
describe = describe.replace(/(-0)?-[^-]+(-dirty)?$/, '$2');
|
||||
|
||||
var out_contents =
|
||||
"#define SCM_REV_STR \"" + revision + "\"\n" +
|
||||
|
|
|
@ -11,7 +11,6 @@ set(SRCS Src/ActionReplay.cpp
|
|||
Src/GeckoCodeConfig.cpp
|
||||
Src/GeckoCode.cpp
|
||||
Src/Movie.cpp
|
||||
Src/NetPlay.cpp
|
||||
Src/NetPlayClient.cpp
|
||||
Src/NetPlayServer.cpp
|
||||
Src/PatchEngine.cpp
|
||||
|
@ -215,6 +214,7 @@ if(_M_ARM)
|
|||
Src/PowerPC/JitArm32/JitArm_Integer.cpp
|
||||
Src/PowerPC/JitArm32/JitArm_LoadStore.cpp
|
||||
Src/PowerPC/JitArm32/JitArm_FloatingPoint.cpp
|
||||
Src/PowerPC/JitArm32/JitArm_Paired.cpp
|
||||
Src/PowerPC/JitArm32/JitArm_SystemRegisters.cpp
|
||||
Src/PowerPC/JitArm32/JitArm_LoadStoreFloating.cpp)
|
||||
endif()
|
||||
|
|
|
@ -338,7 +338,6 @@
|
|||
<ClCompile Include="Src\IPC_HLE\WII_Socket.cpp" />
|
||||
<ClCompile Include="Src\x64MemTools.cpp" />
|
||||
<ClCompile Include="Src\Movie.cpp" />
|
||||
<ClCompile Include="Src\NetPlay.cpp" />
|
||||
<ClCompile Include="Src\NetPlayClient.cpp" />
|
||||
<ClCompile Include="Src\NetPlayServer.cpp" />
|
||||
<ClCompile Include="Src\PatchEngine.cpp" />
|
||||
|
@ -550,7 +549,9 @@
|
|||
<ClInclude Include="Src\IPC_HLE\WII_Socket.h" />
|
||||
<ClInclude Include="Src\MemTools.h" />
|
||||
<ClInclude Include="Src\Movie.h" />
|
||||
<ClInclude Include="Src\NetPlay.h" />
|
||||
<ClInclude Include="Src\NetPlayClient.h" />
|
||||
<ClInclude Include="Src\NetPlayProto.h" />
|
||||
<ClInclude Include="Src\NetPlayServer.h" />
|
||||
<ClInclude Include="Src\ec_wii.h" />
|
||||
<ClInclude Include="Src\PatchEngine.h" />
|
||||
<ClInclude Include="Src\DSPEmulator.h" />
|
||||
|
|
|
@ -529,15 +529,6 @@
|
|||
<ClCompile Include="Src\HW\Wiimote.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Src\NetPlay.cpp">
|
||||
<Filter>NetPlay</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Src\NetPlayServer.cpp">
|
||||
<Filter>NetPlay</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Src\NetPlayClient.cpp">
|
||||
<Filter>NetPlay</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Src\BootManager.cpp" />
|
||||
<ClCompile Include="Src\FifoPlayer\FifoDataFile.cpp">
|
||||
<Filter>FifoPlayer</Filter>
|
||||
|
@ -567,6 +558,8 @@
|
|||
<ClCompile Include="Src\PowerPC\JitInterface.cpp">
|
||||
<Filter>PowerPC</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Src\NetPlayClient.cpp" />
|
||||
<ClCompile Include="Src\NetPlayServer.cpp" />
|
||||
<ClCompile Include="Src\IPC_HLE\ICMPWin.cpp">
|
||||
<Filter>IPC HLE %28IOS/Starlet%29\Net</Filter>
|
||||
</ClCompile>
|
||||
|
@ -1027,9 +1020,6 @@
|
|||
<ClInclude Include="Src\PowerPC\CPUCoreBase.h">
|
||||
<Filter>PowerPC</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Src\NetPlay.h">
|
||||
<Filter>NetPlay</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Src\BootManager.h" />
|
||||
<ClInclude Include="Src\FifoPlayer\FifoDataFile.h">
|
||||
<Filter>FifoPlayer</Filter>
|
||||
|
@ -1059,6 +1049,9 @@
|
|||
<ClInclude Include="Src\PowerPC\JitInterface.h">
|
||||
<Filter>PowerPC</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Src\NetPlayClient.h" />
|
||||
<ClInclude Include="Src\NetPlayProto.h" />
|
||||
<ClInclude Include="Src\NetPlayServer.h" />
|
||||
<ClInclude Include="Src\IPC_HLE\WII_IPC_HLE_Device_net_ssl.h">
|
||||
<Filter>IPC HLE %28IOS/Starlet%29\Net</Filter>
|
||||
</ClInclude>
|
||||
|
@ -1206,9 +1199,6 @@
|
|||
<Filter Include="HW %28Flipper/Hollywood%29\Wiimote\Real">
|
||||
<UniqueIdentifier>{1c21a3e1-b791-4a23-b0d5-ed2b2c34007f}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="NetPlay">
|
||||
<UniqueIdentifier>{231ceb02-1122-402a-87a8-094a9ed768c2}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="FifoPlayer">
|
||||
<UniqueIdentifier>{ca7d56f7-4e84-4d15-9aea-7ae6fa7d6586}</UniqueIdentifier>
|
||||
</Filter>
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
#include "Host.h"
|
||||
#include "VideoBackendBase.h"
|
||||
#include "Movie.h"
|
||||
#include "NetPlay.h"
|
||||
#include "NetPlayClient.h"
|
||||
|
||||
namespace BootManager
|
||||
{
|
||||
|
@ -43,7 +43,7 @@ namespace BootManager
|
|||
// Apply fire liberally
|
||||
struct ConfigCache
|
||||
{
|
||||
bool valid, bCPUThread, bSkipIdle, bEnableFPRF, bMMU, bDCBZOFF, m_EnableJIT,
|
||||
bool valid, bCPUThread, bSkipIdle, bEnableFPRF, bMMU, bDCBZOFF, m_EnableJIT, bDSPThread,
|
||||
bVBeamSpeedHack, bSyncGPU, bFastDiscSpeed, bMergeBlocks, bDSPHLE, bHLE_BS2;
|
||||
int iTLBHack, iCPUCore;
|
||||
std::string strBackend;
|
||||
|
@ -93,6 +93,7 @@ bool BootCore(const std::string& _rFilename)
|
|||
config_cache.strBackend = StartUp.m_strVideoBackend;
|
||||
config_cache.bHLE_BS2 = StartUp.bHLE_BS2;
|
||||
config_cache.m_EnableJIT = SConfig::GetInstance().m_EnableJIT;
|
||||
config_cache.bDSPThread = StartUp.bDSPThread;
|
||||
|
||||
// General settings
|
||||
game_ini.Get("Core", "CPUThread", &StartUp.bCPUThread, StartUp.bCPUThread);
|
||||
|
@ -106,6 +107,7 @@ bool BootCore(const std::string& _rFilename)
|
|||
game_ini.Get("Core", "FastDiscSpeed", &StartUp.bFastDiscSpeed, StartUp.bFastDiscSpeed);
|
||||
game_ini.Get("Core", "BlockMerging", &StartUp.bMergeBlocks, StartUp.bMergeBlocks);
|
||||
game_ini.Get("Core", "DSPHLE", &StartUp.bDSPHLE, StartUp.bDSPHLE);
|
||||
game_ini.Get("Core", "DSPThread", &StartUp.bDSPThread, StartUp.bDSPThread);
|
||||
game_ini.Get("Core", "GFXBackend", &StartUp.m_strVideoBackend, StartUp.m_strVideoBackend.c_str());
|
||||
game_ini.Get("Core", "CPUCore", &StartUp.iCPUCore, StartUp.iCPUCore);
|
||||
game_ini.Get("Core", "HLE_BS2", &StartUp.bHLE_BS2, StartUp.bHLE_BS2);
|
||||
|
@ -136,8 +138,9 @@ bool BootCore(const std::string& _rFilename)
|
|||
}
|
||||
}
|
||||
|
||||
if (NetPlay::GetNetPlayPtr())
|
||||
if (NetPlay::IsNetPlayRunning())
|
||||
{
|
||||
StartUp.bCPUThread = g_NetPlaySettings.m_CPUthread;
|
||||
StartUp.bDSPHLE = g_NetPlaySettings.m_DSPHLE;
|
||||
StartUp.bEnableMemcardSaving = g_NetPlaySettings.m_WriteToMemcard;
|
||||
SConfig::GetInstance().m_EnableJIT = g_NetPlaySettings.m_DSPEnableJIT;
|
||||
|
@ -176,6 +179,7 @@ void Stop()
|
|||
StartUp.bFastDiscSpeed = config_cache.bFastDiscSpeed;
|
||||
StartUp.bMergeBlocks = config_cache.bMergeBlocks;
|
||||
StartUp.bDSPHLE = config_cache.bDSPHLE;
|
||||
StartUp.bDSPThread = config_cache.bDSPThread;
|
||||
StartUp.m_strVideoBackend = config_cache.strBackend;
|
||||
VideoBackend::ActivateBackend(StartUp.m_strVideoBackend);
|
||||
StartUp.bHLE_BS2 = config_cache.bHLE_BS2;
|
||||
|
|
|
@ -67,7 +67,12 @@ static const struct {
|
|||
{ "Wiimote4Connect", 347 /* WXK_F8 */, 1 /* wxMOD_ALT */ },
|
||||
{ "BalanceBoardConnect",348 /* WXK_F9 */, 1 /* wxMOD_ALT */ },
|
||||
#endif
|
||||
|
||||
{ "ToggleIR", 0, 0 /* wxMOD_NONE */ },
|
||||
{ "ToggleAspectRatio", 0, 0 /* wxMOD_NONE */ },
|
||||
{ "ToggleEFBCopies", 0, 0 /* wxMOD_NONE */ },
|
||||
{ "ToggleFog", 0, 0 /* wxMOD_NONE */ },
|
||||
{ "IncreaseFrameLimit", 0, 0 /* wxMOD_NONE */ },
|
||||
{ "DecreaseFrameLimit", 0, 0 /* wxMOD_NONE */ },
|
||||
{ "LoadStateSlot1", 340 /* WXK_F1 */, 0 /* wxMOD_NONE */ },
|
||||
{ "LoadStateSlot2", 341 /* WXK_F2 */, 0 /* wxMOD_NONE */ },
|
||||
{ "LoadStateSlot3", 342 /* WXK_F3 */, 0 /* wxMOD_NONE */ },
|
||||
|
@ -76,6 +81,8 @@ static const struct {
|
|||
{ "LoadStateSlot6", 345 /* WXK_F6 */, 0 /* wxMOD_NONE */ },
|
||||
{ "LoadStateSlot7", 346 /* WXK_F7 */, 0 /* wxMOD_NONE */ },
|
||||
{ "LoadStateSlot8", 347 /* WXK_F8 */, 0 /* wxMOD_NONE */ },
|
||||
{ "LoadStateSlot9", 0, 0 /* wxMOD_NONE */ },
|
||||
{ "LoadStateSlot10",0, 0 /* wxMOD_NONE */ },
|
||||
|
||||
{ "SaveStateSlot1", 340 /* WXK_F1 */, 4 /* wxMOD_SHIFT */ },
|
||||
{ "SaveStateSlot2", 341 /* WXK_F2 */, 4 /* wxMOD_SHIFT */ },
|
||||
|
@ -85,6 +92,8 @@ static const struct {
|
|||
{ "SaveStateSlot6", 345 /* WXK_F6 */, 4 /* wxMOD_SHIFT */ },
|
||||
{ "SaveStateSlot7", 346 /* WXK_F7 */, 4 /* wxMOD_SHIFT */ },
|
||||
{ "SaveStateSlot8", 347 /* WXK_F8 */, 4 /* wxMOD_SHIFT */ },
|
||||
{ "SaveStateSlot9", 0, 0 /* wxMOD_NONE */ },
|
||||
{ "SaveStateSlot10",0, 0 /* wxMOD_NONE */ },
|
||||
|
||||
{ "LoadLastState1", 0, 0 /* wxMOD_NONE */ },
|
||||
{ "LoadLastState2", 0, 0 /* wxMOD_NONE */ },
|
||||
|
@ -270,6 +279,9 @@ void SConfig::SaveSettings()
|
|||
ini.Set("DSP", "Backend", sBackend);
|
||||
ini.Set("DSP", "Volume", m_Volume);
|
||||
|
||||
// Fifo Player
|
||||
ini.Set("FifoPlayer", "LoopReplay", m_LocalCoreStartupParameter.bLoopFifoReplay);
|
||||
|
||||
ini.Save(File::GetUserPath(F_DOLPHINCONFIG_IDX));
|
||||
m_SYSCONF->Save();
|
||||
}
|
||||
|
@ -445,6 +457,8 @@ void SConfig::LoadSettings()
|
|||
ini.Get("DSP", "Backend", &sBackend, BACKEND_NULLSOUND);
|
||||
#endif
|
||||
ini.Get("DSP", "Volume", &m_Volume, 100);
|
||||
|
||||
ini.Get("FifoPlayer", "LoopReplay", &m_LocalCoreStartupParameter.bLoopFifoReplay, true);
|
||||
}
|
||||
|
||||
m_SYSCONF = new SysConf();
|
||||
|
|
|
@ -79,8 +79,6 @@ void Callback_WiimoteInterruptChannel(int _number, u16 _channelID, const void* _
|
|||
// Function declarations
|
||||
void EmuThread();
|
||||
|
||||
void Stop();
|
||||
|
||||
bool g_bStopping = false;
|
||||
bool g_bHwInit = false;
|
||||
bool g_bStarted = false;
|
||||
|
@ -520,9 +518,6 @@ void SetState(EState _State)
|
|||
{
|
||||
switch (_State)
|
||||
{
|
||||
case CORE_UNINITIALIZED:
|
||||
Stop();
|
||||
break;
|
||||
case CORE_PAUSE:
|
||||
CCPU::EnableStepping(true); // Break
|
||||
Wiimote::Pause();
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
SCoreStartupParameter::SCoreStartupParameter()
|
||||
: hInstance(0),
|
||||
bEnableDebugging(false), bAutomaticStart(false), bBootToPause(false),
|
||||
bJITNoBlockCache(false), bJITBlockLinking(true),
|
||||
bJITOff(false),
|
||||
bJITLoadStoreOff(false), bJITLoadStorelXzOff(false),
|
||||
|
@ -46,7 +47,8 @@ SCoreStartupParameter::SCoreStartupParameter()
|
|||
bRenderWindowAutoSize(false), bKeepWindowOnTop(false),
|
||||
bFullscreen(false), bRenderToMain(false),
|
||||
bProgressive(false), bDisableScreenSaver(false),
|
||||
iPosX(100), iPosY(100), iWidth(800), iHeight(600)
|
||||
iPosX(100), iPosY(100), iWidth(800), iHeight(600),
|
||||
bLoopFifoReplay(true)
|
||||
{
|
||||
LoadDefaults();
|
||||
}
|
||||
|
@ -54,9 +56,13 @@ SCoreStartupParameter::SCoreStartupParameter()
|
|||
void SCoreStartupParameter::LoadDefaults()
|
||||
{
|
||||
bEnableDebugging = false;
|
||||
bAutomaticStart = false;
|
||||
bBootToPause = false;
|
||||
|
||||
#ifdef USE_GDBSTUB
|
||||
iGDBPort = -1;
|
||||
#endif
|
||||
|
||||
iCPUCore = 1;
|
||||
bCPUThread = false;
|
||||
bSkipIdle = false;
|
||||
|
@ -84,6 +90,8 @@ void SCoreStartupParameter::LoadDefaults()
|
|||
iWidth = 800;
|
||||
iHeight = 600;
|
||||
|
||||
bLoopFifoReplay = true;
|
||||
|
||||
bJITOff = false; // debugger only settings
|
||||
bJITLoadStoreOff = false;
|
||||
bJITLoadStoreFloatingOff = false;
|
||||
|
@ -314,7 +322,10 @@ bool SCoreStartupParameter::AutoSetup(EBootBS2 _BootBS2)
|
|||
m_strSRAM = File::GetUserPath(F_GCSRAM_IDX);
|
||||
if (!bWii)
|
||||
{
|
||||
m_strBootROM = File::GetSysDirectory() + GC_SYS_DIR + DIR_SEP + Region + DIR_SEP GC_IPL;
|
||||
m_strBootROM = File::GetUserPath(D_GCUSER_IDX) + DIR_SEP + Region + DIR_SEP GC_IPL;
|
||||
if (!File::Exists(m_strBootROM))
|
||||
m_strBootROM = File::GetSysDirectory() + GC_SYS_DIR + DIR_SEP + Region + DIR_SEP GC_IPL;
|
||||
|
||||
if (!bHLE_BS2)
|
||||
{
|
||||
if (!File::Exists(m_strBootROM))
|
||||
|
|
|
@ -34,6 +34,14 @@ enum Hotkey
|
|||
HK_WIIMOTE4_CONNECT,
|
||||
HK_BALANCEBOARD_CONNECT,
|
||||
|
||||
HK_TOGGLE_IR,
|
||||
HK_TOGGLE_AR,
|
||||
HK_TOGGLE_EFBCOPIES,
|
||||
HK_TOGGLE_FOG,
|
||||
|
||||
HK_INCREASE_FRAME_LIMIT,
|
||||
HK_DECREASE_FRAME_LIMIT,
|
||||
|
||||
HK_LOAD_STATE_SLOT_1,
|
||||
HK_LOAD_STATE_SLOT_2,
|
||||
HK_LOAD_STATE_SLOT_3,
|
||||
|
@ -42,6 +50,8 @@ enum Hotkey
|
|||
HK_LOAD_STATE_SLOT_6,
|
||||
HK_LOAD_STATE_SLOT_7,
|
||||
HK_LOAD_STATE_SLOT_8,
|
||||
HK_LOAD_STATE_SLOT_9,
|
||||
HK_LOAD_STATE_SLOT_10,
|
||||
|
||||
HK_SAVE_STATE_SLOT_1,
|
||||
HK_SAVE_STATE_SLOT_2,
|
||||
|
@ -51,6 +61,8 @@ enum Hotkey
|
|||
HK_SAVE_STATE_SLOT_6,
|
||||
HK_SAVE_STATE_SLOT_7,
|
||||
HK_SAVE_STATE_SLOT_8,
|
||||
HK_SAVE_STATE_SLOT_9,
|
||||
HK_SAVE_STATE_SLOT_10,
|
||||
|
||||
HK_LOAD_LAST_STATE_1,
|
||||
HK_LOAD_LAST_STATE_2,
|
||||
|
@ -85,6 +97,7 @@ struct SCoreStartupParameter
|
|||
// 0 = Interpreter
|
||||
// 1 = Jit
|
||||
// 2 = JitIL
|
||||
// 3 = JIT ARM
|
||||
int iCPUCore;
|
||||
|
||||
// JIT (shared between JIT and JITIL)
|
||||
|
@ -152,6 +165,9 @@ struct SCoreStartupParameter
|
|||
|
||||
int iPosX, iPosY, iWidth, iHeight;
|
||||
|
||||
// Fifo Player related settings
|
||||
bool bLoopFifoReplay;
|
||||
|
||||
enum EBootBS2
|
||||
{
|
||||
BOOT_DEFAULT,
|
||||
|
|
|
@ -6,7 +6,10 @@
|
|||
#include "FifoPlayer.h"
|
||||
|
||||
#include "Common.h"
|
||||
#include "ConfigManager.h"
|
||||
#include "Core.h"
|
||||
#include "CoreTiming.h"
|
||||
#include "Host.h"
|
||||
|
||||
#include "HW/GPFifo.h"
|
||||
#include "HW/Memmap.h"
|
||||
|
@ -68,10 +71,18 @@ bool FifoPlayer::Play()
|
|||
{
|
||||
if (m_CurrentFrame >= m_FrameRangeEnd)
|
||||
{
|
||||
m_CurrentFrame = m_FrameRangeStart;
|
||||
if (m_Loop)
|
||||
{
|
||||
m_CurrentFrame = m_FrameRangeStart;
|
||||
|
||||
CoreTiming::downcount = 0;
|
||||
CoreTiming::Advance();
|
||||
CoreTiming::downcount = 0;
|
||||
CoreTiming::Advance();
|
||||
}
|
||||
else
|
||||
{
|
||||
PowerPC::Stop();
|
||||
Host_Message(WM_USER_STOP);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -150,6 +161,7 @@ FifoPlayer::FifoPlayer() :
|
|||
m_FrameWrittenCb(NULL),
|
||||
m_File(NULL)
|
||||
{
|
||||
m_Loop = SConfig::GetInstance().m_LocalCoreStartupParameter.bLoopFifoReplay;
|
||||
}
|
||||
|
||||
void FifoPlayer::WriteFrame(const FifoFrameInfo &frame, const AnalyzedFrameInfo &info)
|
||||
|
|
|
@ -86,6 +86,8 @@ private:
|
|||
|
||||
bool ShouldLoadBP(u8 address);
|
||||
|
||||
bool m_Loop;
|
||||
|
||||
u32 m_CurrentFrame;
|
||||
u32 m_FrameRangeStart;
|
||||
u32 m_FrameRangeEnd;
|
||||
|
|
|
@ -193,7 +193,7 @@ bool CEXIETHERNET::Activate()
|
|||
}
|
||||
if (mHAdapter == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
ERROR_LOG(SP1, "Failed to open any TAP");
|
||||
PanicAlert("Failed to open any TAP");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#include "../ConfigManager.h"
|
||||
#include "../CoreTiming.h"
|
||||
#include "../Movie.h"
|
||||
#include "../NetPlay.h"
|
||||
#include "../NetPlayClient.h"
|
||||
|
||||
#include "SystemTimers.h"
|
||||
#include "ProcessorInterface.h"
|
||||
|
@ -262,7 +262,7 @@ void Init()
|
|||
|
||||
if (Movie::IsRecordingInput() || Movie::IsPlayingInput())
|
||||
AddDevice(Movie::IsUsingPad(i) ? (Movie::IsUsingBongo(i) ? SIDEVICE_GC_TARUKONGA : SIDEVICE_GC_CONTROLLER) : SIDEVICE_NONE, i);
|
||||
else if (NetPlay::GetNetPlayPtr())
|
||||
else if (NetPlay::IsNetPlayRunning())
|
||||
AddDevice((SIDevices) g_NetPlaySettings.m_Controllers[i], i);
|
||||
else
|
||||
AddDevice(SConfig::GetInstance().m_SIDevice[i], i);
|
||||
|
@ -644,10 +644,12 @@ void RunSIBuffer()
|
|||
int GetTicksToNextSIPoll()
|
||||
{
|
||||
// Poll for input at regular intervals (once per frame) when playing or recording a movie
|
||||
if (Movie::IsPlayingInput() || Movie::IsRecordingInput() || NetPlay::GetNetPlayPtr())
|
||||
if (Movie::IsPlayingInput() || Movie::IsRecordingInput())
|
||||
{
|
||||
return SystemTimers::GetTicksPerSecond() / VideoInterface::TargetRefreshRate;
|
||||
}
|
||||
if (NetPlay::IsNetPlayRunning())
|
||||
return SystemTimers::GetTicksPerSecond() / VideoInterface::TargetRefreshRate / 2;
|
||||
|
||||
if (!g_Poll.Y && g_Poll.X)
|
||||
return VideoInterface::GetTicksPerLine() * g_Poll.X;
|
||||
|
|
|
@ -32,7 +32,7 @@ void Shutdown()
|
|||
delete *i;
|
||||
g_plugin.controllers.clear();
|
||||
|
||||
WiimoteReal::Shutdown();
|
||||
WiimoteReal::Stop();
|
||||
|
||||
g_controller_interface.Shutdown();
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@ namespace WiimoteReal
|
|||
{
|
||||
|
||||
void Initialize(bool wait = false);
|
||||
void Stop();
|
||||
void Shutdown();
|
||||
void Resume();
|
||||
void Pause();
|
||||
|
|
|
@ -13,15 +13,17 @@ static const u8 nothing_id[] = { 0x00, 0x00, 0x00, 0x00, 0x2e, 0x2e };
|
|||
// The id for a partially inserted extension
|
||||
static const u8 partially_id[] = { 0x00, 0x00, 0x00, 0x00, 0xff, 0xff };
|
||||
|
||||
Attachment::Attachment( const char* const _name ) : name( _name )
|
||||
Attachment::Attachment( const char* const _name, WiimoteEmu::ExtensionReg& _reg )
|
||||
: name( _name ), reg( _reg )
|
||||
{
|
||||
reg.resize( WIIMOTE_REG_EXT_SIZE, 0);
|
||||
memset(id, 0, sizeof(id));
|
||||
memset(calibration, 0, sizeof(calibration));
|
||||
}
|
||||
|
||||
None::None() : Attachment( "None" )
|
||||
None::None( WiimoteEmu::ExtensionReg& _reg ) : Attachment( "None", _reg )
|
||||
{
|
||||
// set up register
|
||||
memcpy( ®[0xfa], nothing_id, sizeof(nothing_id) );
|
||||
memcpy(&id, nothing_id, sizeof(nothing_id));
|
||||
}
|
||||
|
||||
std::string Attachment::GetName() const
|
||||
|
@ -29,6 +31,14 @@ std::string Attachment::GetName() const
|
|||
return name;
|
||||
}
|
||||
|
||||
void Attachment::Reset()
|
||||
{
|
||||
// set up register
|
||||
memset( ®, 0, WIIMOTE_REG_EXT_SIZE );
|
||||
memcpy( ®.constant_id, id, sizeof(id) );
|
||||
memcpy( ®.calibration, calibration, sizeof(calibration) );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ControllerEmu::Extension::GetState( u8* const data, const bool focus )
|
||||
|
|
|
@ -14,19 +14,23 @@ namespace WiimoteEmu
|
|||
class Attachment : public ControllerEmu
|
||||
{
|
||||
public:
|
||||
Attachment( const char* const _name );
|
||||
Attachment( const char* const _name, WiimoteEmu::ExtensionReg& _reg );
|
||||
|
||||
virtual void GetState( u8* const data, const bool focus = true ) {}
|
||||
void Reset();
|
||||
std::string GetName() const;
|
||||
|
||||
const char* const name;
|
||||
std::vector<u8> reg;
|
||||
const char* const name;
|
||||
WiimoteEmu::ExtensionReg& reg;
|
||||
|
||||
u8 id[6];
|
||||
u8 calibration[0x10];
|
||||
};
|
||||
|
||||
class None : public Attachment
|
||||
{
|
||||
public:
|
||||
None();
|
||||
None( WiimoteEmu::ExtensionReg& _reg );
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ static const u16 classic_dpad_bitmasks[] =
|
|||
Classic::PAD_UP, Classic::PAD_DOWN, Classic::PAD_LEFT, Classic::PAD_RIGHT
|
||||
};
|
||||
|
||||
Classic::Classic() : Attachment(_trans("Classic"))
|
||||
Classic::Classic(WiimoteEmu::ExtensionReg& _reg) : Attachment(_trans("Classic"), _reg)
|
||||
{
|
||||
// buttons
|
||||
groups.push_back(m_buttons = new Buttons("Buttons"));
|
||||
|
@ -76,9 +76,9 @@ Classic::Classic() : Attachment(_trans("Classic"))
|
|||
|
||||
// set up register
|
||||
// calibration
|
||||
memcpy(®[0x20], classic_calibration, sizeof(classic_calibration));
|
||||
memcpy(&calibration, classic_calibration, sizeof(classic_calibration));
|
||||
// id
|
||||
memcpy(®[0xfa], classic_id, sizeof(classic_id));
|
||||
memcpy(&id, classic_id, sizeof(classic_id));
|
||||
}
|
||||
|
||||
void Classic::GetState(u8* const data, const bool focus)
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace WiimoteEmu
|
|||
class Classic : public Attachment
|
||||
{
|
||||
public:
|
||||
Classic();
|
||||
Classic(WiimoteEmu::ExtensionReg& _reg);
|
||||
void GetState( u8* const data, const bool focus );
|
||||
|
||||
enum
|
||||
|
|
|
@ -32,7 +32,7 @@ static const u16 drum_button_bitmasks[] =
|
|||
Drums::BUTTON_PLUS,
|
||||
};
|
||||
|
||||
Drums::Drums() : Attachment(_trans("Drums"))
|
||||
Drums::Drums(WiimoteEmu::ExtensionReg& _reg) : Attachment(_trans("Drums"), _reg)
|
||||
{
|
||||
// pads
|
||||
groups.push_back(m_pads = new Buttons(_trans("Pads")));
|
||||
|
@ -49,7 +49,7 @@ Drums::Drums() : Attachment(_trans("Drums"))
|
|||
|
||||
// set up register
|
||||
// id
|
||||
memcpy(®[0xfa], drums_id, sizeof(drums_id));
|
||||
memcpy(&id, drums_id, sizeof(drums_id));
|
||||
}
|
||||
|
||||
void Drums::GetState(u8* const data, const bool focus)
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace WiimoteEmu
|
|||
class Drums : public Attachment
|
||||
{
|
||||
public:
|
||||
Drums();
|
||||
Drums(WiimoteEmu::ExtensionReg& _reg);
|
||||
void GetState( u8* const data, const bool focus );
|
||||
|
||||
enum
|
||||
|
|
|
@ -36,7 +36,7 @@ static const u16 guitar_strum_bitmasks[] =
|
|||
Guitar::BAR_DOWN,
|
||||
};
|
||||
|
||||
Guitar::Guitar() : Attachment(_trans("Guitar"))
|
||||
Guitar::Guitar(WiimoteEmu::ExtensionReg& _reg) : Attachment(_trans("Guitar"), _reg)
|
||||
{
|
||||
// frets
|
||||
groups.push_back(m_frets = new Buttons(_trans("Frets")));
|
||||
|
@ -62,7 +62,7 @@ Guitar::Guitar() : Attachment(_trans("Guitar"))
|
|||
|
||||
// set up register
|
||||
// id
|
||||
memcpy(®[0xfa], guitar_id, sizeof(guitar_id));
|
||||
memcpy(&id, guitar_id, sizeof(guitar_id));
|
||||
}
|
||||
|
||||
void Guitar::GetState(u8* const data, const bool focus)
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace WiimoteEmu
|
|||
class Guitar : public Attachment
|
||||
{
|
||||
public:
|
||||
Guitar();
|
||||
Guitar(WiimoteEmu::ExtensionReg& _reg);
|
||||
void GetState( u8* const data, const bool focus );
|
||||
|
||||
enum
|
||||
|
|
|
@ -31,7 +31,8 @@ static const u8 nunchuk_button_bitmasks[] =
|
|||
Nunchuk::BUTTON_Z,
|
||||
};
|
||||
|
||||
Nunchuk::Nunchuk(UDPWrapper *wrp) : Attachment(_trans("Nunchuk")) , m_udpWrap(wrp)
|
||||
Nunchuk::Nunchuk(UDPWrapper *wrp, WiimoteEmu::ExtensionReg& _reg)
|
||||
: Attachment(_trans("Nunchuk"), _reg) , m_udpWrap(wrp)
|
||||
{
|
||||
// buttons
|
||||
groups.push_back(m_buttons = new Buttons("Buttons"));
|
||||
|
@ -55,9 +56,9 @@ Nunchuk::Nunchuk(UDPWrapper *wrp) : Attachment(_trans("Nunchuk")) , m_udpWrap(wr
|
|||
|
||||
// set up register
|
||||
// calibration
|
||||
memcpy(®[0x20], nunchuck_calibration, sizeof(nunchuck_calibration));
|
||||
memcpy(&calibration, nunchuck_calibration, sizeof(nunchuck_calibration));
|
||||
// id
|
||||
memcpy(®[0xfa], nunchuck_id, sizeof(nunchuck_id));
|
||||
memcpy(&id, nunchuck_id, sizeof(nunchuck_id));
|
||||
|
||||
// this should get set to 0 on disconnect, but it isn't, o well
|
||||
memset(m_shake_step, 0, sizeof(m_shake_step));
|
||||
|
@ -68,11 +69,45 @@ void Nunchuk::GetState(u8* const data, const bool focus)
|
|||
wm_extension* const ncdata = (wm_extension*)data;
|
||||
ncdata->bt = 0;
|
||||
|
||||
// stick / not using calibration data for stick, o well
|
||||
m_stick->GetState(&ncdata->jx, &ncdata->jy, 0x80, focus ? 127 : 0);
|
||||
|
||||
// stick
|
||||
ControlState state[2];
|
||||
m_stick->GetState(&state[0], &state[1], 0, 1);
|
||||
|
||||
nu_cal &cal = *(nu_cal*)®.calibration;
|
||||
nu_js cal_js[2];
|
||||
cal_js[0] = *&cal.jx;
|
||||
cal_js[1] = *&cal.jy;
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
ControlState &s = *&state[i];
|
||||
nu_js c = *&cal_js[i];
|
||||
if (s < 0)
|
||||
s = s * abs(c.min - c.center) + c.center;
|
||||
else if (s > 0)
|
||||
s = s * abs(c.max - c.center) + c.center;
|
||||
else
|
||||
s = c.center;
|
||||
}
|
||||
|
||||
ncdata->jx = u8(trim(state[0]));
|
||||
ncdata->jy = u8(trim(state[1]));
|
||||
|
||||
if (ncdata->jx != cal.jx.center || ncdata->jy != cal.jy.center)
|
||||
{
|
||||
if (ncdata->jy == cal.jy.center)
|
||||
ncdata->jy = cal.jy.center + 1;
|
||||
if (ncdata->jx == cal.jx.center)
|
||||
ncdata->jx = cal.jx.center + 1;
|
||||
}
|
||||
|
||||
if (!focus)
|
||||
{
|
||||
ncdata->jx = cal.jx.center;
|
||||
ncdata->jy = cal.jy.center;
|
||||
}
|
||||
|
||||
AccelData accel;
|
||||
accel_cal* calib = (accel_cal*)®[0x20];
|
||||
accel_cal* calib = (accel_cal*)®.calibration[0];
|
||||
|
||||
// tilt
|
||||
EmulateTilt(&accel, m_tilt, focus);
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace WiimoteEmu
|
|||
class Nunchuk : public Attachment
|
||||
{
|
||||
public:
|
||||
Nunchuk(UDPWrapper * wrp);
|
||||
Nunchuk(UDPWrapper * wrp, WiimoteEmu::ExtensionReg& _reg);
|
||||
|
||||
virtual void GetState( u8* const data, const bool focus );
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ static const char* const turntable_button_names[] =
|
|||
"-", "+", _trans("Euphoria"),
|
||||
};
|
||||
|
||||
Turntable::Turntable() : Attachment(_trans("Turntable"))
|
||||
Turntable::Turntable(WiimoteEmu::ExtensionReg& _reg) : Attachment(_trans("Turntable"), _reg)
|
||||
{
|
||||
// buttons
|
||||
groups.push_back(m_buttons = new Buttons("Buttons"));
|
||||
|
@ -53,7 +53,7 @@ Turntable::Turntable() : Attachment(_trans("Turntable"))
|
|||
|
||||
// set up register
|
||||
// id
|
||||
memcpy(®[0xfa], turntable_id, sizeof(turntable_id));
|
||||
memcpy(&id, turntable_id, sizeof(turntable_id));
|
||||
}
|
||||
|
||||
void Turntable::GetState(u8* const data, const bool focus)
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace WiimoteEmu
|
|||
class Turntable : public Attachment
|
||||
{
|
||||
public:
|
||||
Turntable();
|
||||
Turntable(WiimoteEmu::ExtensionReg& _reg);
|
||||
void GetState(u8* const data, const bool focus);
|
||||
|
||||
enum
|
||||
|
|
|
@ -851,11 +851,8 @@ void Wiimote::HandleExtensionSwap()
|
|||
// set the wanted extension
|
||||
m_extension->active_extension = m_extension->switch_extension;
|
||||
|
||||
// set register, I hate this
|
||||
const std::vector<u8> ® = ((WiimoteEmu::Attachment*)m_extension->attachments[m_extension->active_extension])->reg;
|
||||
memset(&m_reg_ext, 0, WIIMOTE_REG_EXT_SIZE);
|
||||
memcpy(&m_reg_ext, ®[0], reg.size());
|
||||
|
||||
// reset register
|
||||
((WiimoteEmu::Attachment*)m_extension->attachments[m_extension->active_extension])->Reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -286,12 +286,12 @@ Wiimote::Wiimote( const unsigned int index )
|
|||
|
||||
// extension
|
||||
groups.push_back(m_extension = new Extension(_trans("Extension")));
|
||||
m_extension->attachments.push_back(new WiimoteEmu::None());
|
||||
m_extension->attachments.push_back(new WiimoteEmu::Nunchuk(m_udp));
|
||||
m_extension->attachments.push_back(new WiimoteEmu::Classic());
|
||||
m_extension->attachments.push_back(new WiimoteEmu::Guitar());
|
||||
m_extension->attachments.push_back(new WiimoteEmu::Drums());
|
||||
m_extension->attachments.push_back(new WiimoteEmu::Turntable());
|
||||
m_extension->attachments.push_back(new WiimoteEmu::None(m_reg_ext));
|
||||
m_extension->attachments.push_back(new WiimoteEmu::Nunchuk(m_udp, m_reg_ext));
|
||||
m_extension->attachments.push_back(new WiimoteEmu::Classic(m_reg_ext));
|
||||
m_extension->attachments.push_back(new WiimoteEmu::Guitar(m_reg_ext));
|
||||
m_extension->attachments.push_back(new WiimoteEmu::Drums(m_reg_ext));
|
||||
m_extension->attachments.push_back(new WiimoteEmu::Turntable(m_reg_ext));
|
||||
|
||||
m_extension->settings.push_back(new ControlGroup::Setting(_trans("Motion Plus"), 0, 0, 1));
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#include <vector>
|
||||
#include <queue>
|
||||
|
||||
// Registry sizes
|
||||
// Registry sizes
|
||||
#define WIIMOTE_EEPROM_SIZE (16*1024)
|
||||
#define WIIMOTE_EEPROM_FREE_SIZE 0x1700
|
||||
#define WIIMOTE_REG_SPEAKER_SIZE 10
|
||||
|
@ -47,6 +47,30 @@ struct ADPCMState
|
|||
s32 predictor, step;
|
||||
};
|
||||
|
||||
struct ExtensionReg
|
||||
{
|
||||
u8 unknown1[0x08];
|
||||
|
||||
// address 0x08
|
||||
u8 controller_data[0x06];
|
||||
u8 unknown2[0x12];
|
||||
|
||||
// address 0x20
|
||||
u8 calibration[0x10];
|
||||
u8 unknown3[0x10];
|
||||
|
||||
// address 0x40
|
||||
u8 encryption_key[0x10];
|
||||
u8 unknown4[0xA0];
|
||||
|
||||
// address 0xF0
|
||||
u8 encryption;
|
||||
u8 unknown5[0x9];
|
||||
|
||||
// address 0xFA
|
||||
u8 constant_id[6];
|
||||
};
|
||||
|
||||
extern const ReportFeatures reporting_mode_features[];
|
||||
|
||||
void EmulateShake(AccelData* const accel_data
|
||||
|
@ -143,7 +167,7 @@ private:
|
|||
ControlGroup* m_rumble;
|
||||
Extension* m_extension;
|
||||
ControlGroup* m_options;
|
||||
|
||||
|
||||
// WiiMote accel data
|
||||
AccelData m_accel;
|
||||
|
||||
|
@ -151,7 +175,7 @@ private:
|
|||
const u8 m_index;
|
||||
|
||||
double ir_sin, ir_cos; //for the low pass filter
|
||||
|
||||
|
||||
UDPWrapper* m_udp;
|
||||
|
||||
bool m_rumble_on;
|
||||
|
@ -201,30 +225,7 @@ private:
|
|||
|
||||
} m_reg_ir;
|
||||
|
||||
struct ExtensionReg
|
||||
{
|
||||
u8 unknown1[0x08];
|
||||
|
||||
// address 0x08
|
||||
u8 controller_data[0x06];
|
||||
u8 unknown2[0x12];
|
||||
|
||||
// address 0x20
|
||||
u8 calibration[0x10];
|
||||
u8 unknown3[0x10];
|
||||
|
||||
// address 0x40
|
||||
u8 encryption_key[0x10];
|
||||
u8 unknown4[0xA0];
|
||||
|
||||
// address 0xF0
|
||||
u8 encryption;
|
||||
u8 unknown5[0x9];
|
||||
|
||||
// address 0xFA
|
||||
u8 constant_id[6];
|
||||
|
||||
} m_reg_ext;
|
||||
ExtensionReg m_reg_ext;
|
||||
|
||||
struct SpeakerReg
|
||||
{
|
||||
|
|
|
@ -54,7 +54,7 @@ struct wm_ir_basic
|
|||
};
|
||||
|
||||
// Three bytes for one object
|
||||
struct wm_ir_extended
|
||||
struct wm_ir_extended
|
||||
{
|
||||
u8 x;
|
||||
u8 y;
|
||||
|
@ -150,9 +150,9 @@ struct wm_turntable_extension
|
|||
struct wm_motionplus_data
|
||||
{
|
||||
u8 yaw1;
|
||||
|
||||
|
||||
u8 roll1;
|
||||
|
||||
|
||||
u8 pitch1;
|
||||
|
||||
u8 yaw2 : 6;
|
||||
|
@ -230,7 +230,7 @@ struct wm_status_report
|
|||
};
|
||||
|
||||
#define WM_WRITE_DATA 0x16
|
||||
struct wm_write_data
|
||||
struct wm_write_data
|
||||
{
|
||||
u8 rumble : 1;
|
||||
u8 space : 2; //see WM_SPACE_*
|
||||
|
@ -241,7 +241,7 @@ struct wm_write_data
|
|||
};
|
||||
|
||||
#define WM_ACK_DATA 0x22
|
||||
struct wm_acknowledge
|
||||
struct wm_acknowledge
|
||||
{
|
||||
wm_core buttons;
|
||||
u8 reportID;
|
||||
|
@ -276,7 +276,7 @@ struct wm_read_data_reply
|
|||
#define WM_RDERR_WOREG 7
|
||||
#define WM_RDERR_NOMEM 8
|
||||
|
||||
|
||||
|
||||
// Data reports
|
||||
|
||||
#define WM_REPORT_CORE 0x30
|
||||
|
@ -304,20 +304,20 @@ struct wm_report_core_accel_ir12
|
|||
|
||||
#define WM_REPORT_CORE_EXT19 0x34
|
||||
#define WM_REPORT_CORE_ACCEL_EXT16 0x35
|
||||
struct wm_report_core_accel_ext16
|
||||
struct wm_report_core_accel_ext16
|
||||
{
|
||||
wm_core c;
|
||||
wm_accel a;
|
||||
wm_extension ext;
|
||||
//wm_ir_basic ir[2];
|
||||
u8 pad[10];
|
||||
|
||||
|
||||
};
|
||||
|
||||
#define WM_REPORT_CORE_IR10_EXT9 0x36
|
||||
|
||||
#define WM_REPORT_CORE_ACCEL_IR10_EXT6 0x37
|
||||
struct wm_report_core_accel_ir10_ext6
|
||||
struct wm_report_core_accel_ir10_ext6
|
||||
{
|
||||
wm_core c;
|
||||
wm_accel a;
|
||||
|
@ -336,7 +336,7 @@ struct wm_report_ext21
|
|||
#define WM_REPORT_INTERLEAVE2 0x3f
|
||||
|
||||
#define WM_SPEAKER_ENABLE 0x14
|
||||
#define WM_SPEAKER_MUTE 0x19
|
||||
#define WM_SPEAKER_MUTE 0x19
|
||||
#define WM_WRITE_SPEAKER_DATA 0x18
|
||||
struct wm_speaker_data
|
||||
{
|
||||
|
@ -383,9 +383,12 @@ struct cc_trigger
|
|||
struct nu_cal
|
||||
{
|
||||
wm_accel cal_zero; // zero calibration
|
||||
u8 pad1;
|
||||
wm_accel cal_g; // g size
|
||||
u8 pad2;
|
||||
nu_js jx; //
|
||||
nu_js jy; //
|
||||
u8 sum[2];
|
||||
};
|
||||
|
||||
struct cc_cal
|
||||
|
|
|
@ -570,15 +570,17 @@ void Initialize(bool wait)
|
|||
g_real_wiimotes_initialized = true;
|
||||
}
|
||||
|
||||
void Shutdown(void)
|
||||
// called on emulation shutdown
|
||||
void Stop(void)
|
||||
{
|
||||
for (unsigned int i = 0; i < MAX_BBMOTES; ++i)
|
||||
if (g_wiimotes[i] && g_wiimotes[i]->IsConnected())
|
||||
g_wiimotes[i]->EmuStop();
|
||||
}
|
||||
|
||||
// WiimoteReal is shutdown on app exit
|
||||
return;
|
||||
|
||||
// called when the dolphin app exits
|
||||
void Shutdown(void)
|
||||
{
|
||||
g_wiimote_scanner.StopScanning();
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
||||
|
|
|
@ -1256,8 +1256,6 @@ void CWII_IPC_HLE_Device_usb_oh1_57e_305::ExecuteHCICommandMessage(const SHCICom
|
|||
void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandInquiry(u8* _Input)
|
||||
{
|
||||
// Inquiry should not be called normally
|
||||
PanicAlertT("HCI_CMD_INQUIRY is called, please report!");
|
||||
|
||||
hci_inquiry_cp* pInquiry = (hci_inquiry_cp*)_Input;
|
||||
|
||||
INFO_LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_INQUIRY:");
|
||||
|
@ -1863,8 +1861,6 @@ CWII_IPC_HLE_WiiMote* CWII_IPC_HLE_Device_usb_oh1_57e_305::AccessWiiMote(const b
|
|||
}
|
||||
|
||||
ERROR_LOG(WII_IPC_WIIMOTE,"Can't find WiiMote by bd: %02x:%02x:%02x:%02x:%02x:%02x",
|
||||
_rAddr.b[0], _rAddr.b[1], _rAddr.b[2], _rAddr.b[3], _rAddr.b[4], _rAddr.b[5]);
|
||||
PanicAlertT("Can't find WiiMote by bd: %02x:%02x:%02x:%02x:%02x:%02x",
|
||||
_rAddr.b[0], _rAddr.b[1], _rAddr.b[2], _rAddr.b[3], _rAddr.b[4], _rAddr.b[5]);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -156,6 +156,7 @@ void Init()
|
|||
{
|
||||
ReadHeader();
|
||||
std::thread md5thread(CheckMD5);
|
||||
md5thread.detach();
|
||||
if ((strncmp((char *)tmpHeader.gameID, Core::g_CoreStartupParameter.GetUniqueID().c_str(), 6)))
|
||||
{
|
||||
PanicAlert("The recorded game (%s) is not the same as the selected game (%s)", tmpHeader.gameID, Core::g_CoreStartupParameter.GetUniqueID().c_str());
|
||||
|
@ -167,6 +168,7 @@ void Init()
|
|||
{
|
||||
GetSettings();
|
||||
std::thread md5thread(GetMD5);
|
||||
md5thread.detach();
|
||||
}
|
||||
|
||||
g_frameSkipCounter = g_framesToSkip;
|
||||
|
|
|
@ -1,401 +0,0 @@
|
|||
// Copyright 2013 Dolphin Emulator Project
|
||||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "NetPlay.h"
|
||||
|
||||
// for wiimote
|
||||
#include "IPC_HLE/WII_IPC_HLE_Device_usb.h"
|
||||
#include "IPC_HLE/WII_IPC_HLE_WiiMote.h"
|
||||
// for gcpad
|
||||
#include "HW/SI_DeviceGCController.h"
|
||||
#include "HW/SI_DeviceGCSteeringWheel.h"
|
||||
#include "HW/SI_DeviceDanceMat.h"
|
||||
// for gctime
|
||||
#include "HW/EXI_DeviceIPL.h"
|
||||
// for wiimote/ OSD messages
|
||||
#include "Core.h"
|
||||
#include "ConfigManager.h"
|
||||
|
||||
std::mutex crit_netplay_ptr;
|
||||
static NetPlay* netplay_ptr = NULL;
|
||||
NetSettings g_NetPlaySettings;
|
||||
|
||||
#define RPT_SIZE_HACK (1 << 16)
|
||||
|
||||
// called from ---GUI--- thread
|
||||
NetPlay::NetPlay(NetPlayUI* dialog)
|
||||
: m_dialog(dialog), m_is_running(false), m_do_loop(true)
|
||||
{
|
||||
m_target_buffer_size = 20;
|
||||
ClearBuffers();
|
||||
}
|
||||
|
||||
void NetPlay_Enable(NetPlay* const np)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(crit_netplay_ptr);
|
||||
netplay_ptr = np;
|
||||
}
|
||||
|
||||
void NetPlay_Disable()
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(crit_netplay_ptr);
|
||||
netplay_ptr = NULL;
|
||||
}
|
||||
|
||||
// called from ---GUI--- thread
|
||||
NetPlay::~NetPlay()
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(crit_netplay_ptr);
|
||||
netplay_ptr = NULL;
|
||||
|
||||
// not perfect
|
||||
if (m_is_running)
|
||||
StopGame();
|
||||
}
|
||||
|
||||
NetPlay::Player::Player()
|
||||
{
|
||||
memset(pad_map, -1, sizeof(pad_map));
|
||||
}
|
||||
|
||||
// called from ---GUI--- thread
|
||||
std::string NetPlay::Player::ToString() const
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << name << '[' << (char)(pid+'0') << "] : " << revision << " |";
|
||||
for (unsigned int i=0; i<4; ++i)
|
||||
ss << (pad_map[i]>=0 ? (char)(pad_map[i]+'1') : '-');
|
||||
ss << '|';
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
NetPad::NetPad()
|
||||
{
|
||||
nHi = 0x00808080;
|
||||
nLo = 0x80800000;
|
||||
}
|
||||
|
||||
NetPad::NetPad(const SPADStatus* const pad_status)
|
||||
{
|
||||
nHi = (u32)((u8)pad_status->stickY);
|
||||
nHi |= (u32)((u8)pad_status->stickX << 8);
|
||||
nHi |= (u32)((u16)pad_status->button << 16);
|
||||
nHi |= 0x00800000;
|
||||
nLo = (u8)pad_status->triggerRight;
|
||||
nLo |= (u32)((u8)pad_status->triggerLeft << 8);
|
||||
nLo |= (u32)((u8)pad_status->substickY << 16);
|
||||
nLo |= (u32)((u8)pad_status->substickX << 24);
|
||||
}
|
||||
|
||||
// called from ---NETPLAY--- thread
|
||||
void NetPlay::ClearBuffers()
|
||||
{
|
||||
// clear pad buffers, Clear method isn't thread safe
|
||||
for (unsigned int i=0; i<4; ++i)
|
||||
{
|
||||
while (m_pad_buffer[i].Size())
|
||||
m_pad_buffer[i].Pop();
|
||||
|
||||
while (m_wiimote_buffer[i].Size())
|
||||
m_wiimote_buffer[i].Pop();
|
||||
|
||||
m_wiimote_input[i].clear();
|
||||
}
|
||||
}
|
||||
|
||||
// called from ---CPU--- thread
|
||||
bool NetPlay::GetNetPads(const u8 pad_nb, const SPADStatus* const pad_status, NetPad* const netvalues)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
|
||||
|
||||
// in game mapping for this local pad
|
||||
unsigned int in_game_num = m_local_player->pad_map[pad_nb];
|
||||
|
||||
// does this local pad map in game?
|
||||
if (in_game_num < 4)
|
||||
{
|
||||
NetPad np(pad_status);
|
||||
|
||||
// adjust the buffer either up or down
|
||||
// inserting multiple padstates or dropping states
|
||||
while (m_pad_buffer[in_game_num].Size() <= m_target_buffer_size)
|
||||
{
|
||||
// add to buffer
|
||||
m_pad_buffer[in_game_num].Push(np);
|
||||
|
||||
// send
|
||||
SendPadState(pad_nb, np);
|
||||
}
|
||||
}
|
||||
|
||||
} // unlock players
|
||||
|
||||
//Common::Timer bufftimer;
|
||||
//bufftimer.Start();
|
||||
|
||||
// get padstate from buffer and send to game
|
||||
while (!m_pad_buffer[pad_nb].Pop(*netvalues))
|
||||
{
|
||||
// wait for receiving thread to push some data
|
||||
Common::SleepCurrentThread(1);
|
||||
|
||||
if (false == m_is_running)
|
||||
return false;
|
||||
|
||||
// TODO: check the time of bufftimer here,
|
||||
// if it gets pretty high, ask the user if they want to disconnect
|
||||
|
||||
}
|
||||
|
||||
//u64 hangtime = bufftimer.GetTimeElapsed();
|
||||
//if (hangtime > 10)
|
||||
//{
|
||||
// std::ostringstream ss;
|
||||
// ss << "Pad " << (int)pad_nb << ": Had to wait " << hangtime << "ms for pad data. (increase pad Buffer maybe)";
|
||||
// Core::DisplayMessage(ss.str(), 1000);
|
||||
//}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// called from ---CPU--- thread
|
||||
void NetPlay::WiimoteInput(int _number, u16 _channelID, const void* _pData, u32 _Size)
|
||||
{
|
||||
//// in game mapping for this local wiimote
|
||||
unsigned int in_game_num = m_local_player->pad_map[_number]; // just using gc pad_map for now
|
||||
|
||||
// does this local pad map in game?
|
||||
if (in_game_num < 4)
|
||||
{
|
||||
m_wiimote_input[_number].resize(m_wiimote_input[_number].size() + 1);
|
||||
m_wiimote_input[_number].back().assign((char*)_pData, (char*)_pData + _Size);
|
||||
m_wiimote_input[_number].back().channel = _channelID;
|
||||
}
|
||||
}
|
||||
|
||||
// called from ---CPU--- thread
|
||||
void NetPlay::WiimoteUpdate(int _number)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
|
||||
|
||||
// in game mapping for this local wiimote
|
||||
unsigned int in_game_num = m_local_player->pad_map[_number]; // just using gc pad_map for now
|
||||
|
||||
// does this local pad map in game?
|
||||
if (in_game_num < 4)
|
||||
{
|
||||
m_wiimote_buffer[in_game_num].Push(m_wiimote_input[_number]);
|
||||
|
||||
// TODO: send it
|
||||
|
||||
m_wiimote_input[_number].clear();
|
||||
}
|
||||
|
||||
} // unlock players
|
||||
|
||||
if (0 == m_wiimote_buffer[_number].Size())
|
||||
{
|
||||
//PanicAlert("PANIC");
|
||||
return;
|
||||
}
|
||||
|
||||
NetWiimote nw;
|
||||
m_wiimote_buffer[_number].Pop(nw);
|
||||
|
||||
NetWiimote::const_iterator
|
||||
i = nw.begin(), e = nw.end();
|
||||
for ( ; i!=e; ++i)
|
||||
Core::Callback_WiimoteInterruptChannel(_number, i->channel, &(*i)[0], (u32)i->size() + RPT_SIZE_HACK);
|
||||
}
|
||||
|
||||
// called from ---GUI--- thread
|
||||
bool NetPlay::StartGame(const std::string &path)
|
||||
{
|
||||
if (m_is_running)
|
||||
{
|
||||
PanicAlertT("Game is already running!");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_dialog->AppendChat(" -- STARTING GAME -- ");
|
||||
|
||||
m_is_running = true;
|
||||
NetPlay_Enable(this);
|
||||
|
||||
ClearBuffers();
|
||||
|
||||
// boot game
|
||||
m_dialog->BootGame(path);
|
||||
|
||||
// temporary
|
||||
NetWiimote nw;
|
||||
for (unsigned int i = 0; i<4; ++i)
|
||||
for (unsigned int f = 0; f<2; ++f)
|
||||
m_wiimote_buffer[i].Push(nw);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// called from ---GUI--- thread and ---NETPLAY--- thread (client side)
|
||||
bool NetPlay::StopGame()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
|
||||
|
||||
if (false == m_is_running)
|
||||
{
|
||||
PanicAlertT("Game isn't running!");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_dialog->AppendChat(" -- STOPPING GAME -- ");
|
||||
|
||||
m_is_running = false;
|
||||
NetPlay_Disable();
|
||||
|
||||
// stop game
|
||||
m_dialog->StopGame();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NetPlay::SetMemcardWriteEnabled(bool enabled)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
|
||||
g_NetPlaySettings.m_WriteToMemcard = enabled;
|
||||
}
|
||||
|
||||
// called from ---CPU--- thread
|
||||
u8 NetPlay::GetPadNum(u8 numPAD)
|
||||
{
|
||||
// TODO: i don't like that this loop is running everytime there is rumble
|
||||
unsigned int i = 0;
|
||||
for (; i<4; ++i)
|
||||
if (numPAD == m_local_player->pad_map[i])
|
||||
break;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
void NetPlay::GetNetSettings()
|
||||
{
|
||||
SConfig &instance = SConfig::GetInstance();
|
||||
g_NetPlaySettings.m_DSPHLE = instance.m_LocalCoreStartupParameter.bDSPHLE;
|
||||
g_NetPlaySettings.m_DSPEnableJIT = instance.m_EnableJIT;
|
||||
|
||||
for (unsigned int i = 0; i < 4; ++i)
|
||||
g_NetPlaySettings.m_Controllers[i] = SConfig::GetInstance().m_SIDevice[i];
|
||||
}
|
||||
|
||||
// stuff hacked into dolphin
|
||||
|
||||
// called from ---CPU--- thread
|
||||
// Actual Core function which is called on every frame
|
||||
bool CSIDevice_GCController::NetPlay_GetInput(u8 numPAD, SPADStatus PadStatus, u32 *PADStatus)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(crit_netplay_ptr);
|
||||
|
||||
if (netplay_ptr)
|
||||
return netplay_ptr->GetNetPads(numPAD, &PadStatus, (NetPad*)PADStatus);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CSIDevice_GCSteeringWheel::NetPlay_GetInput(u8 numPAD, SPADStatus PadStatus, u32 *PADStatus)
|
||||
{
|
||||
return CSIDevice_GCController::NetPlay_GetInput(numPAD, PadStatus, PADStatus);
|
||||
}
|
||||
|
||||
bool CSIDevice_DanceMat::NetPlay_GetInput(u8 numPAD, SPADStatus PadStatus, u32 *PADStatus)
|
||||
{
|
||||
return CSIDevice_GCController::NetPlay_GetInput(numPAD, PadStatus, PADStatus);
|
||||
}
|
||||
|
||||
// called from ---CPU--- thread
|
||||
// so all players' games get the same time
|
||||
u32 CEXIIPL::NetPlay_GetGCTime()
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(crit_netplay_ptr);
|
||||
|
||||
if (netplay_ptr)
|
||||
return 1272737767; // watev
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
// called from ---CPU--- thread
|
||||
// return the local pad num that should rumble given a ingame pad num
|
||||
u8 CSIDevice_GCController::NetPlay_GetPadNum(u8 numPAD)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(crit_netplay_ptr);
|
||||
|
||||
if (netplay_ptr)
|
||||
return netplay_ptr->GetPadNum(numPAD);
|
||||
else
|
||||
return numPAD;
|
||||
}
|
||||
|
||||
u8 CSIDevice_GCSteeringWheel::NetPlay_GetPadNum(u8 numPAD)
|
||||
{
|
||||
return CSIDevice_GCController::NetPlay_GetPadNum(numPAD);
|
||||
}
|
||||
|
||||
u8 CSIDevice_DanceMat::NetPlay_GetPadNum(u8 numPAD)
|
||||
{
|
||||
return CSIDevice_GCController::NetPlay_GetPadNum(numPAD);
|
||||
}
|
||||
|
||||
// called from ---CPU--- thread
|
||||
// wiimote update / used for frame counting
|
||||
//void CWII_IPC_HLE_Device_usb_oh1_57e_305::NetPlay_WiimoteUpdate(int _number)
|
||||
void CWII_IPC_HLE_Device_usb_oh1_57e_305::NetPlay_WiimoteUpdate(int)
|
||||
{
|
||||
//CritLocker crit(crit_netplay_ptr);
|
||||
|
||||
//if (netplay_ptr)
|
||||
// netplay_ptr->WiimoteUpdate(_number);
|
||||
}
|
||||
|
||||
// called from ---CPU--- thread
|
||||
//
|
||||
int CWII_IPC_HLE_WiiMote::NetPlay_GetWiimoteNum(int _number)
|
||||
{
|
||||
//CritLocker crit(crit_netplay_ptr);
|
||||
|
||||
//if (netplay_ptr)
|
||||
// return netplay_ptr->GetPadNum(_number); // just using gcpad mapping for now
|
||||
//else
|
||||
return _number;
|
||||
}
|
||||
|
||||
// called from ---CPU--- thread
|
||||
// intercept wiimote input callback
|
||||
//bool CWII_IPC_HLE_WiiMote::NetPlay_WiimoteInput(int _number, u16 _channelID, const void* _pData, u32& _Size)
|
||||
bool CWII_IPC_HLE_WiiMote::NetPlay_WiimoteInput(int, u16, const void*, u32&)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(crit_netplay_ptr);
|
||||
|
||||
if (netplay_ptr)
|
||||
//{
|
||||
// if (_Size >= RPT_SIZE_HACK)
|
||||
// {
|
||||
// _Size -= RPT_SIZE_HACK;
|
||||
// return false;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// netplay_ptr->WiimoteInput(_number, _channelID, _pData, _Size);
|
||||
// // don't use this packet
|
||||
return true;
|
||||
// }
|
||||
//}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
NetPlay* NetPlay::GetNetPlayPtr()
|
||||
{
|
||||
return netplay_ptr;
|
||||
}
|
|
@ -1,275 +0,0 @@
|
|||
// Copyright 2013 Dolphin Emulator Project
|
||||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#ifndef _NETPLAY_H
|
||||
#define _NETPLAY_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "CommonTypes.h"
|
||||
#include "Thread.h"
|
||||
#include "Timer.h"
|
||||
|
||||
#include <SFML/Network.hpp>
|
||||
|
||||
#include "GCPadStatus.h"
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <queue>
|
||||
#include <sstream>
|
||||
|
||||
#include "FifoQueue.h"
|
||||
|
||||
class NetPad
|
||||
{
|
||||
public:
|
||||
NetPad();
|
||||
NetPad(const SPADStatus* const);
|
||||
|
||||
u32 nHi;
|
||||
u32 nLo;
|
||||
};
|
||||
|
||||
struct NetSettings
|
||||
{
|
||||
bool m_DSPHLE;
|
||||
bool m_DSPEnableJIT;
|
||||
bool m_WriteToMemcard;
|
||||
u8 m_Controllers[4];
|
||||
};
|
||||
extern NetSettings g_NetPlaySettings;
|
||||
|
||||
struct Rpt : public std::vector<u8>
|
||||
{
|
||||
u16 channel;
|
||||
};
|
||||
|
||||
typedef std::vector<Rpt> NetWiimote;
|
||||
|
||||
#define NETPLAY_VERSION "Dolphin NetPlay 2013-07-22"
|
||||
|
||||
// messages
|
||||
enum
|
||||
{
|
||||
NP_MSG_PLAYER_JOIN = 0x10,
|
||||
NP_MSG_PLAYER_LEAVE = 0x11,
|
||||
|
||||
NP_MSG_CHAT_MESSAGE = 0x30,
|
||||
|
||||
NP_MSG_PAD_DATA = 0x60,
|
||||
NP_MSG_PAD_MAPPING = 0x61,
|
||||
NP_MSG_PAD_BUFFER = 0x62,
|
||||
|
||||
NP_MSG_WIIMOTE_DATA = 0x70,
|
||||
NP_MSG_WIIMOTE_MAPPING = 0x71, // just using pad mapping for now
|
||||
|
||||
NP_MSG_START_GAME = 0xA0,
|
||||
NP_MSG_CHANGE_GAME = 0xA1,
|
||||
NP_MSG_STOP_GAME = 0xA2,
|
||||
NP_MSG_DISABLE_GAME = 0xA3,
|
||||
|
||||
NP_MSG_READY = 0xD0,
|
||||
NP_MSG_NOT_READY = 0xD1,
|
||||
|
||||
NP_MSG_PING = 0xE0,
|
||||
NP_MSG_PONG = 0xE1,
|
||||
};
|
||||
|
||||
typedef u8 MessageId;
|
||||
typedef u8 PlayerId;
|
||||
typedef s8 PadMapping;
|
||||
typedef u32 FrameNum;
|
||||
|
||||
enum
|
||||
{
|
||||
CON_ERR_SERVER_FULL = 1,
|
||||
CON_ERR_GAME_RUNNING,
|
||||
CON_ERR_VERSION_MISMATCH
|
||||
};
|
||||
|
||||
class NetPlayUI
|
||||
{
|
||||
public:
|
||||
virtual ~NetPlayUI() {};
|
||||
|
||||
virtual void BootGame(const std::string& filename) = 0;
|
||||
virtual void StopGame() = 0;
|
||||
|
||||
virtual void Update() = 0;
|
||||
virtual void AppendChat(const std::string& msg) = 0;
|
||||
|
||||
virtual void OnMsgChangeGame(const std::string& filename) = 0;
|
||||
virtual void OnMsgStartGame() = 0;
|
||||
virtual void OnMsgStopGame() = 0;
|
||||
};
|
||||
|
||||
class NetPlay
|
||||
{
|
||||
public:
|
||||
NetPlay(NetPlayUI* _dialog);
|
||||
virtual ~NetPlay();
|
||||
//virtual void ThreadFunc() = 0;
|
||||
|
||||
bool is_connected;
|
||||
|
||||
// Send and receive pads values
|
||||
void WiimoteInput(int _number, u16 _channelID, const void* _pData, u32 _Size);
|
||||
void WiimoteUpdate(int _number);
|
||||
bool GetNetPads(const u8 pad_nb, const SPADStatus* const, NetPad* const netvalues);
|
||||
virtual bool ChangeGame(const std::string& game) = 0;
|
||||
virtual void GetPlayerList(std::string& list, std::vector<int>& pid_list) = 0;
|
||||
virtual void SendChatMessage(const std::string& msg) = 0;
|
||||
|
||||
virtual bool StartGame(const std::string &path);
|
||||
virtual bool StopGame();
|
||||
|
||||
virtual void SetMemcardWriteEnabled(bool enabled);
|
||||
//void PushPadStates(unsigned int count);
|
||||
|
||||
u8 GetPadNum(u8 numPAD);
|
||||
static NetPlay* GetNetPlayPtr();
|
||||
|
||||
protected:
|
||||
//void GetBufferedPad(const u8 pad_nb, NetPad* const netvalues);
|
||||
void ClearBuffers();
|
||||
void GetNetSettings();
|
||||
virtual void SendPadState(const PadMapping local_nb, const NetPad& np) = 0;
|
||||
|
||||
struct
|
||||
{
|
||||
std::recursive_mutex game;
|
||||
// lock order
|
||||
std::recursive_mutex players, send;
|
||||
} m_crit;
|
||||
|
||||
class Player
|
||||
{
|
||||
public:
|
||||
Player();
|
||||
std::string ToString() const;
|
||||
|
||||
PlayerId pid;
|
||||
std::string name;
|
||||
PadMapping pad_map[4];
|
||||
std::string revision;
|
||||
};
|
||||
|
||||
Common::FifoQueue<NetPad> m_pad_buffer[4];
|
||||
Common::FifoQueue<NetWiimote> m_wiimote_buffer[4];
|
||||
|
||||
NetWiimote m_wiimote_input[4];
|
||||
|
||||
NetPlayUI* m_dialog;
|
||||
sf::SocketTCP m_socket;
|
||||
std::thread m_thread;
|
||||
sf::Selector<sf::SocketTCP> m_selector;
|
||||
|
||||
std::string m_selected_game;
|
||||
volatile bool m_is_running;
|
||||
volatile bool m_do_loop;
|
||||
|
||||
unsigned int m_target_buffer_size;
|
||||
|
||||
Player* m_local_player;
|
||||
|
||||
u32 m_current_game;
|
||||
};
|
||||
|
||||
void NetPlay_Enable(NetPlay* const np);
|
||||
void NetPlay_Disable();
|
||||
|
||||
class NetPlayServer : public NetPlay
|
||||
{
|
||||
public:
|
||||
void ThreadFunc();
|
||||
|
||||
NetPlayServer(const u16 port, const std::string& name, NetPlayUI* dialog);
|
||||
~NetPlayServer();
|
||||
|
||||
void GetPlayerList(std::string& list, std::vector<int>& pid_list);
|
||||
|
||||
// Send and receive pads values
|
||||
//bool GetNetPads(const u8 pad_nb, const SPADStatus* const, NetPad* const netvalues);
|
||||
bool ChangeGame(const std::string& game);
|
||||
void SendChatMessage(const std::string& msg);
|
||||
|
||||
bool StartGame(const std::string &path);
|
||||
bool StopGame();
|
||||
|
||||
bool GetPadMapping(const int pid, int map[]);
|
||||
bool SetPadMapping(const int pid, const int map[]);
|
||||
|
||||
u64 CalculateMinimumBufferTime();
|
||||
void AdjustPadBufferSize(unsigned int size);
|
||||
|
||||
#ifdef USE_UPNP
|
||||
void TryPortmapping(u16 port);
|
||||
#endif
|
||||
|
||||
private:
|
||||
class Client : public Player
|
||||
{
|
||||
public:
|
||||
Client() : ping(0), current_game(0) {}
|
||||
|
||||
sf::SocketTCP socket;
|
||||
u64 ping;
|
||||
u32 current_game;
|
||||
};
|
||||
|
||||
void SendPadState(const PadMapping local_nb, const NetPad& np);
|
||||
void SendToClients(sf::Packet& packet, const PlayerId skip_pid = 0);
|
||||
unsigned int OnConnect(sf::SocketTCP& socket);
|
||||
unsigned int OnDisconnect(sf::SocketTCP& socket);
|
||||
unsigned int OnData(sf::Packet& packet, sf::SocketTCP& socket);
|
||||
void UpdatePadMapping();
|
||||
|
||||
std::map<sf::SocketTCP, Client> m_players;
|
||||
|
||||
Common::Timer m_ping_timer;
|
||||
u32 m_ping_key;
|
||||
bool m_update_pings;
|
||||
|
||||
#ifdef USE_UPNP
|
||||
static void mapPortThread(const u16 port);
|
||||
static void unmapPortThread();
|
||||
|
||||
static bool initUPnP();
|
||||
static bool UPnPMapPort(const std::string& addr, const u16 port);
|
||||
static bool UPnPUnmapPort(const u16 port);
|
||||
|
||||
static struct UPNPUrls m_upnp_urls;
|
||||
static struct IGDdatas m_upnp_data;
|
||||
static u16 m_upnp_mapped;
|
||||
static bool m_upnp_inited;
|
||||
static bool m_upnp_error;
|
||||
static std::thread m_upnp_thread;
|
||||
#endif
|
||||
};
|
||||
|
||||
class NetPlayClient : public NetPlay
|
||||
{
|
||||
public:
|
||||
void ThreadFunc();
|
||||
|
||||
NetPlayClient(const std::string& address, const u16 port, NetPlayUI* dialog, const std::string& name);
|
||||
~NetPlayClient();
|
||||
|
||||
void GetPlayerList(std::string& list, std::vector<int>& pid_list);
|
||||
|
||||
// Send and receive pads values
|
||||
//bool GetNetPads(const u8 pad_nb, const SPADStatus* const, NetPad* const netvalues);
|
||||
bool StartGame(const std::string &path);
|
||||
bool ChangeGame(const std::string& game);
|
||||
void SendChatMessage(const std::string& msg);
|
||||
|
||||
private:
|
||||
void SendPadState(const PadMapping local_nb, const NetPad& np);
|
||||
unsigned int OnData(sf::Packet& packet);
|
||||
|
||||
PlayerId m_pid;
|
||||
std::map<PlayerId, Player> m_players;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -2,21 +2,81 @@
|
|||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "NetPlay.h"
|
||||
#include "NetPlayClient.h"
|
||||
|
||||
// for wiimote
|
||||
#include "IPC_HLE/WII_IPC_HLE_Device_usb.h"
|
||||
#include "IPC_HLE/WII_IPC_HLE_WiiMote.h"
|
||||
// for gcpad
|
||||
#include "HW/SI_DeviceGCController.h"
|
||||
#include "HW/SI_DeviceGCSteeringWheel.h"
|
||||
#include "HW/SI_DeviceDanceMat.h"
|
||||
// for gctime
|
||||
#include "HW/EXI_DeviceIPL.h"
|
||||
// for wiimote/ OSD messages
|
||||
#include "Core.h"
|
||||
#include "ConfigManager.h"
|
||||
|
||||
std::mutex crit_netplay_client;
|
||||
static NetPlayClient * netplay_client = NULL;
|
||||
NetSettings g_NetPlaySettings;
|
||||
|
||||
#define RPT_SIZE_HACK (1 << 16)
|
||||
|
||||
NetPlayClient::Player::Player()
|
||||
{
|
||||
memset(pad_map, -1, sizeof(pad_map));
|
||||
}
|
||||
|
||||
// called from ---GUI--- thread
|
||||
std::string NetPlayClient::Player::ToString() const
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << name << '[' << (char)(pid+'0') << "] : " << revision << " |";
|
||||
for (unsigned int i=0; i<4; ++i)
|
||||
ss << (pad_map[i]>=0 ? (char)(pad_map[i]+'1') : '-');
|
||||
ss << " | " << ping << "ms";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
NetPad::NetPad()
|
||||
{
|
||||
nHi = 0x00808080;
|
||||
nLo = 0x80800000;
|
||||
}
|
||||
|
||||
NetPad::NetPad(const SPADStatus* const pad_status)
|
||||
{
|
||||
nHi = (u32)((u8)pad_status->stickY);
|
||||
nHi |= (u32)((u8)pad_status->stickX << 8);
|
||||
nHi |= (u32)((u16)pad_status->button << 16);
|
||||
nHi |= 0x00800000;
|
||||
nLo = (u8)pad_status->triggerRight;
|
||||
nLo |= (u32)((u8)pad_status->triggerLeft << 8);
|
||||
nLo |= (u32)((u8)pad_status->substickY << 16);
|
||||
nLo |= (u32)((u8)pad_status->substickX << 24);
|
||||
}
|
||||
|
||||
// called from ---GUI--- thread
|
||||
NetPlayClient::~NetPlayClient()
|
||||
{
|
||||
// not perfect
|
||||
if (m_is_running)
|
||||
StopGame();
|
||||
|
||||
if (is_connected)
|
||||
{
|
||||
m_do_loop = false;
|
||||
m_thread.join();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// called from ---GUI--- thread
|
||||
NetPlayClient::NetPlayClient(const std::string& address, const u16 port, NetPlayUI* dialog, const std::string& name) : NetPlay(dialog)
|
||||
NetPlayClient::NetPlayClient(const std::string& address, const u16 port, NetPlayUI* dialog, const std::string& name) : m_dialog(dialog), m_is_running(false), m_do_loop(true)
|
||||
{
|
||||
m_target_buffer_size = 20;
|
||||
ClearBuffers();
|
||||
|
||||
is_connected = false;
|
||||
|
||||
// why is false successful? documentation says true is
|
||||
|
@ -193,6 +253,7 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet)
|
|||
{
|
||||
std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
|
||||
packet >> m_current_game;
|
||||
packet >> g_NetPlaySettings.m_CPUthread;
|
||||
packet >> g_NetPlaySettings.m_DSPEnableJIT;
|
||||
packet >> g_NetPlaySettings.m_DSPHLE;
|
||||
packet >> g_NetPlaySettings.m_WriteToMemcard;
|
||||
|
@ -233,6 +294,21 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet)
|
|||
}
|
||||
break;
|
||||
|
||||
case NP_MSG_PLAYER_PING_DATA:
|
||||
{
|
||||
PlayerId pid;
|
||||
packet >> pid;
|
||||
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
|
||||
Player& player = m_players[pid];
|
||||
packet >> player.ping;
|
||||
}
|
||||
|
||||
m_dialog->Update();
|
||||
}
|
||||
break;
|
||||
|
||||
default :
|
||||
PanicAlertT("Unknown message received with id : %d", mid);
|
||||
break;
|
||||
|
@ -328,12 +404,31 @@ bool NetPlayClient::StartGame(const std::string &path)
|
|||
spac << m_current_game;
|
||||
spac << (char *)&g_NetPlaySettings;
|
||||
|
||||
if (false == NetPlay::StartGame(path))
|
||||
return false;
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lks(m_crit.send);
|
||||
m_socket.Send(spac);
|
||||
|
||||
if (m_is_running)
|
||||
{
|
||||
PanicAlertT("Game is already running!");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_dialog->AppendChat(" -- STARTING GAME -- ");
|
||||
|
||||
m_is_running = true;
|
||||
NetPlay_Enable(this);
|
||||
|
||||
ClearBuffers();
|
||||
|
||||
// boot game
|
||||
m_dialog->BootGame(path);
|
||||
|
||||
// temporary
|
||||
NetWiimote nw;
|
||||
for (unsigned int i = 0; i<4; ++i)
|
||||
for (unsigned int f = 0; f<2; ++f)
|
||||
m_wiimote_buffer[i].Push(nw);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -342,3 +437,283 @@ bool NetPlayClient::ChangeGame(const std::string&)
|
|||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// called from ---NETPLAY--- thread
|
||||
void NetPlayClient::ClearBuffers()
|
||||
{
|
||||
// clear pad buffers, Clear method isn't thread safe
|
||||
for (unsigned int i=0; i<4; ++i)
|
||||
{
|
||||
while (m_pad_buffer[i].Size())
|
||||
m_pad_buffer[i].Pop();
|
||||
|
||||
while (m_wiimote_buffer[i].Size())
|
||||
m_wiimote_buffer[i].Pop();
|
||||
|
||||
m_wiimote_input[i].clear();
|
||||
}
|
||||
}
|
||||
|
||||
// called from ---CPU--- thread
|
||||
bool NetPlayClient::GetNetPads(const u8 pad_nb, const SPADStatus* const pad_status, NetPad* const netvalues)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
|
||||
|
||||
// in game mapping for this local pad
|
||||
unsigned int in_game_num = m_local_player->pad_map[pad_nb];
|
||||
|
||||
// does this local pad map in game?
|
||||
if (in_game_num < 4)
|
||||
{
|
||||
NetPad np(pad_status);
|
||||
|
||||
// adjust the buffer either up or down
|
||||
// inserting multiple padstates or dropping states
|
||||
while (m_pad_buffer[in_game_num].Size() <= m_target_buffer_size)
|
||||
{
|
||||
// add to buffer
|
||||
m_pad_buffer[in_game_num].Push(np);
|
||||
|
||||
// send
|
||||
SendPadState(pad_nb, np);
|
||||
}
|
||||
}
|
||||
|
||||
} // unlock players
|
||||
|
||||
//Common::Timer bufftimer;
|
||||
//bufftimer.Start();
|
||||
|
||||
// get padstate from buffer and send to game
|
||||
while (!m_pad_buffer[pad_nb].Pop(*netvalues))
|
||||
{
|
||||
// wait for receiving thread to push some data
|
||||
Common::SleepCurrentThread(1);
|
||||
|
||||
if (false == m_is_running)
|
||||
return false;
|
||||
|
||||
// TODO: check the time of bufftimer here,
|
||||
// if it gets pretty high, ask the user if they want to disconnect
|
||||
|
||||
}
|
||||
|
||||
//u64 hangtime = bufftimer.GetTimeElapsed();
|
||||
//if (hangtime > 10)
|
||||
//{
|
||||
// std::ostringstream ss;
|
||||
// ss << "Pad " << (int)pad_nb << ": Had to wait " << hangtime << "ms for pad data. (increase pad Buffer maybe)";
|
||||
// Core::DisplayMessage(ss.str(), 1000);
|
||||
//}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// called from ---CPU--- thread
|
||||
void NetPlayClient::WiimoteInput(int _number, u16 _channelID, const void* _pData, u32 _Size)
|
||||
{
|
||||
//// in game mapping for this local wiimote
|
||||
unsigned int in_game_num = m_local_player->pad_map[_number]; // just using gc pad_map for now
|
||||
|
||||
// does this local pad map in game?
|
||||
if (in_game_num < 4)
|
||||
{
|
||||
m_wiimote_input[_number].resize(m_wiimote_input[_number].size() + 1);
|
||||
m_wiimote_input[_number].back().assign((char*)_pData, (char*)_pData + _Size);
|
||||
m_wiimote_input[_number].back().channel = _channelID;
|
||||
}
|
||||
}
|
||||
|
||||
// called from ---CPU--- thread
|
||||
void NetPlayClient::WiimoteUpdate(int _number)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
|
||||
|
||||
// in game mapping for this local wiimote
|
||||
unsigned int in_game_num = m_local_player->pad_map[_number]; // just using gc pad_map for now
|
||||
|
||||
// does this local pad map in game?
|
||||
if (in_game_num < 4)
|
||||
{
|
||||
m_wiimote_buffer[in_game_num].Push(m_wiimote_input[_number]);
|
||||
|
||||
// TODO: send it
|
||||
|
||||
m_wiimote_input[_number].clear();
|
||||
}
|
||||
|
||||
} // unlock players
|
||||
|
||||
if (0 == m_wiimote_buffer[_number].Size())
|
||||
{
|
||||
//PanicAlert("PANIC");
|
||||
return;
|
||||
}
|
||||
|
||||
NetWiimote nw;
|
||||
m_wiimote_buffer[_number].Pop(nw);
|
||||
|
||||
NetWiimote::const_iterator
|
||||
i = nw.begin(), e = nw.end();
|
||||
for ( ; i!=e; ++i)
|
||||
Core::Callback_WiimoteInterruptChannel(_number, i->channel, &(*i)[0], (u32)i->size() + RPT_SIZE_HACK);
|
||||
}
|
||||
|
||||
// called from ---GUI--- thread and ---NETPLAY--- thread (client side)
|
||||
bool NetPlayClient::StopGame()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
|
||||
|
||||
if (false == m_is_running)
|
||||
{
|
||||
PanicAlertT("Game isn't running!");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_dialog->AppendChat(" -- STOPPING GAME -- ");
|
||||
|
||||
m_is_running = false;
|
||||
NetPlay_Disable();
|
||||
|
||||
// stop game
|
||||
m_dialog->StopGame();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// called from ---CPU--- thread
|
||||
u8 NetPlayClient::GetPadNum(u8 numPAD)
|
||||
{
|
||||
// TODO: i don't like that this loop is running everytime there is rumble
|
||||
unsigned int i = 0;
|
||||
for (; i<4; ++i)
|
||||
if (numPAD == m_local_player->pad_map[i])
|
||||
break;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
// stuff hacked into dolphin
|
||||
|
||||
// called from ---CPU--- thread
|
||||
// Actual Core function which is called on every frame
|
||||
bool CSIDevice_GCController::NetPlay_GetInput(u8 numPAD, SPADStatus PadStatus, u32 *PADStatus)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(crit_netplay_client);
|
||||
|
||||
if (netplay_client)
|
||||
return netplay_client->GetNetPads(numPAD, &PadStatus, (NetPad*)PADStatus);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CSIDevice_GCSteeringWheel::NetPlay_GetInput(u8 numPAD, SPADStatus PadStatus, u32 *PADStatus)
|
||||
{
|
||||
return CSIDevice_GCController::NetPlay_GetInput(numPAD, PadStatus, PADStatus);
|
||||
}
|
||||
|
||||
bool CSIDevice_DanceMat::NetPlay_GetInput(u8 numPAD, SPADStatus PadStatus, u32 *PADStatus)
|
||||
{
|
||||
return CSIDevice_GCController::NetPlay_GetInput(numPAD, PadStatus, PADStatus);
|
||||
}
|
||||
|
||||
// called from ---CPU--- thread
|
||||
// so all players' games get the same time
|
||||
u32 CEXIIPL::NetPlay_GetGCTime()
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(crit_netplay_client);
|
||||
|
||||
if (netplay_client)
|
||||
return 1272737767; // watev
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
// called from ---CPU--- thread
|
||||
// return the local pad num that should rumble given a ingame pad num
|
||||
u8 CSIDevice_GCController::NetPlay_GetPadNum(u8 numPAD)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(crit_netplay_client);
|
||||
|
||||
if (netplay_client)
|
||||
return netplay_client->GetPadNum(numPAD);
|
||||
else
|
||||
return numPAD;
|
||||
}
|
||||
|
||||
u8 CSIDevice_GCSteeringWheel::NetPlay_GetPadNum(u8 numPAD)
|
||||
{
|
||||
return CSIDevice_GCController::NetPlay_GetPadNum(numPAD);
|
||||
}
|
||||
|
||||
u8 CSIDevice_DanceMat::NetPlay_GetPadNum(u8 numPAD)
|
||||
{
|
||||
return CSIDevice_GCController::NetPlay_GetPadNum(numPAD);
|
||||
}
|
||||
|
||||
// called from ---CPU--- thread
|
||||
// wiimote update / used for frame counting
|
||||
//void CWII_IPC_HLE_Device_usb_oh1_57e_305::NetPlay_WiimoteUpdate(int _number)
|
||||
void CWII_IPC_HLE_Device_usb_oh1_57e_305::NetPlay_WiimoteUpdate(int)
|
||||
{
|
||||
//CritLocker crit(crit_netplay_client);
|
||||
|
||||
//if (netplay_client)
|
||||
// netplay_client->WiimoteUpdate(_number);
|
||||
}
|
||||
|
||||
// called from ---CPU--- thread
|
||||
//
|
||||
int CWII_IPC_HLE_WiiMote::NetPlay_GetWiimoteNum(int _number)
|
||||
{
|
||||
//CritLocker crit(crit_netplay_client);
|
||||
|
||||
//if (netplay_client)
|
||||
// return netplay_client->GetPadNum(_number); // just using gcpad mapping for now
|
||||
//else
|
||||
return _number;
|
||||
}
|
||||
|
||||
// called from ---CPU--- thread
|
||||
// intercept wiimote input callback
|
||||
//bool CWII_IPC_HLE_WiiMote::NetPlay_WiimoteInput(int _number, u16 _channelID, const void* _pData, u32& _Size)
|
||||
bool CWII_IPC_HLE_WiiMote::NetPlay_WiimoteInput(int, u16, const void*, u32&)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(crit_netplay_client);
|
||||
|
||||
if (netplay_client)
|
||||
//{
|
||||
// if (_Size >= RPT_SIZE_HACK)
|
||||
// {
|
||||
// _Size -= RPT_SIZE_HACK;
|
||||
// return false;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// netplay_client->WiimoteInput(_number, _channelID, _pData, _Size);
|
||||
// // don't use this packet
|
||||
return true;
|
||||
// }
|
||||
//}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NetPlay::IsNetPlayRunning()
|
||||
{
|
||||
return netplay_client != NULL;
|
||||
}
|
||||
|
||||
void NetPlay_Enable(NetPlayClient* const np)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(crit_netplay_client);
|
||||
netplay_client = np;
|
||||
}
|
||||
|
||||
void NetPlay_Disable()
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(crit_netplay_client);
|
||||
netplay_client = NULL;
|
||||
}
|
||||
|
|
135
Source/Core/Core/Src/NetPlayClient.h
Normal file
135
Source/Core/Core/Src/NetPlayClient.h
Normal file
|
@ -0,0 +1,135 @@
|
|||
// Copyright 2013 Dolphin Emulator Project
|
||||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#ifndef _NETPLAY_H
|
||||
#define _NETPLAY_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "CommonTypes.h"
|
||||
#include "Thread.h"
|
||||
#include "Timer.h"
|
||||
|
||||
#include <SFML/Network.hpp>
|
||||
|
||||
#include "NetPlayProto.h"
|
||||
#include "GCPadStatus.h"
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <queue>
|
||||
#include <sstream>
|
||||
|
||||
#include "FifoQueue.h"
|
||||
|
||||
class NetPad
|
||||
{
|
||||
public:
|
||||
NetPad();
|
||||
NetPad(const SPADStatus* const);
|
||||
|
||||
u32 nHi;
|
||||
u32 nLo;
|
||||
};
|
||||
|
||||
class NetPlayUI
|
||||
{
|
||||
public:
|
||||
virtual ~NetPlayUI() {};
|
||||
|
||||
virtual void BootGame(const std::string& filename) = 0;
|
||||
virtual void StopGame() = 0;
|
||||
|
||||
virtual void Update() = 0;
|
||||
virtual void AppendChat(const std::string& msg) = 0;
|
||||
|
||||
virtual void OnMsgChangeGame(const std::string& filename) = 0;
|
||||
virtual void OnMsgStartGame() = 0;
|
||||
virtual void OnMsgStopGame() = 0;
|
||||
};
|
||||
|
||||
extern NetSettings g_NetPlaySettings;
|
||||
|
||||
class NetPlayClient
|
||||
{
|
||||
public:
|
||||
void ThreadFunc();
|
||||
|
||||
NetPlayClient(const std::string& address, const u16 port, NetPlayUI* dialog, const std::string& name);
|
||||
~NetPlayClient();
|
||||
|
||||
void GetPlayerList(std::string& list, std::vector<int>& pid_list);
|
||||
|
||||
bool is_connected;
|
||||
|
||||
bool StartGame(const std::string &path);
|
||||
bool StopGame();
|
||||
bool ChangeGame(const std::string& game);
|
||||
void SendChatMessage(const std::string& msg);
|
||||
|
||||
// Send and receive pads values
|
||||
void WiimoteInput(int _number, u16 _channelID, const void* _pData, u32 _Size);
|
||||
void WiimoteUpdate(int _number);
|
||||
bool GetNetPads(const u8 pad_nb, const SPADStatus* const, NetPad* const netvalues);
|
||||
|
||||
u8 GetPadNum(u8 numPAD);
|
||||
|
||||
protected:
|
||||
void ClearBuffers();
|
||||
|
||||
struct
|
||||
{
|
||||
std::recursive_mutex game;
|
||||
// lock order
|
||||
std::recursive_mutex players, send;
|
||||
} m_crit;
|
||||
|
||||
class Player
|
||||
{
|
||||
public:
|
||||
Player();
|
||||
std::string ToString() const;
|
||||
|
||||
PlayerId pid;
|
||||
std::string name;
|
||||
PadMapping pad_map[4];
|
||||
std::string revision;
|
||||
u32 ping;
|
||||
};
|
||||
|
||||
Common::FifoQueue<NetPad> m_pad_buffer[4];
|
||||
Common::FifoQueue<NetWiimote> m_wiimote_buffer[4];
|
||||
|
||||
NetWiimote m_wiimote_input[4];
|
||||
|
||||
NetPlayUI* m_dialog;
|
||||
sf::SocketTCP m_socket;
|
||||
std::thread m_thread;
|
||||
sf::Selector<sf::SocketTCP> m_selector;
|
||||
|
||||
std::string m_selected_game;
|
||||
volatile bool m_is_running;
|
||||
volatile bool m_do_loop;
|
||||
|
||||
unsigned int m_target_buffer_size;
|
||||
|
||||
Player* m_local_player;
|
||||
|
||||
u32 m_current_game;
|
||||
|
||||
private:
|
||||
void SendPadState(const PadMapping local_nb, const NetPad& np);
|
||||
unsigned int OnData(sf::Packet& packet);
|
||||
|
||||
PlayerId m_pid;
|
||||
std::map<PlayerId, Player> m_players;
|
||||
};
|
||||
|
||||
namespace NetPlay {
|
||||
bool IsNetPlayRunning();
|
||||
};
|
||||
|
||||
void NetPlay_Enable(NetPlayClient* const np);
|
||||
void NetPlay_Disable();
|
||||
|
||||
#endif
|
69
Source/Core/Core/Src/NetPlayProto.h
Normal file
69
Source/Core/Core/Src/NetPlayProto.h
Normal file
|
@ -0,0 +1,69 @@
|
|||
// Copyright 2013 Dolphin Emulator Project
|
||||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#ifndef _NETPLAY_PROTO_H
|
||||
#define _NETPLAY_PROTO_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "CommonTypes.h"
|
||||
|
||||
struct NetSettings
|
||||
{
|
||||
bool m_CPUthread;
|
||||
bool m_DSPHLE;
|
||||
bool m_DSPEnableJIT;
|
||||
bool m_WriteToMemcard;
|
||||
u8 m_Controllers[4];
|
||||
};
|
||||
|
||||
struct Rpt : public std::vector<u8>
|
||||
{
|
||||
u16 channel;
|
||||
};
|
||||
|
||||
typedef std::vector<Rpt> NetWiimote;
|
||||
|
||||
#define NETPLAY_VERSION "Dolphin NetPlay 2013-08-18"
|
||||
|
||||
// messages
|
||||
enum
|
||||
{
|
||||
NP_MSG_PLAYER_JOIN = 0x10,
|
||||
NP_MSG_PLAYER_LEAVE = 0x11,
|
||||
|
||||
NP_MSG_CHAT_MESSAGE = 0x30,
|
||||
|
||||
NP_MSG_PAD_DATA = 0x60,
|
||||
NP_MSG_PAD_MAPPING = 0x61,
|
||||
NP_MSG_PAD_BUFFER = 0x62,
|
||||
|
||||
NP_MSG_WIIMOTE_DATA = 0x70,
|
||||
NP_MSG_WIIMOTE_MAPPING = 0x71, // just using pad mapping for now
|
||||
|
||||
NP_MSG_START_GAME = 0xA0,
|
||||
NP_MSG_CHANGE_GAME = 0xA1,
|
||||
NP_MSG_STOP_GAME = 0xA2,
|
||||
NP_MSG_DISABLE_GAME = 0xA3,
|
||||
|
||||
NP_MSG_READY = 0xD0,
|
||||
NP_MSG_NOT_READY = 0xD1,
|
||||
|
||||
NP_MSG_PING = 0xE0,
|
||||
NP_MSG_PONG = 0xE1,
|
||||
NP_MSG_PLAYER_PING_DATA = 0xE2,
|
||||
};
|
||||
|
||||
typedef u8 MessageId;
|
||||
typedef u8 PlayerId;
|
||||
typedef s8 PadMapping;
|
||||
typedef u32 FrameNum;
|
||||
|
||||
enum
|
||||
{
|
||||
CON_ERR_SERVER_FULL = 1,
|
||||
CON_ERR_GAME_RUNNING,
|
||||
CON_ERR_VERSION_MISMATCH
|
||||
};
|
||||
|
||||
#endif
|
|
@ -2,15 +2,20 @@
|
|||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "NetPlay.h"
|
||||
#include "NetPlayServer.h"
|
||||
|
||||
NetPlayServer::Client::Client()
|
||||
{
|
||||
memset(pad_map, -1, sizeof(pad_map));
|
||||
}
|
||||
|
||||
// called from ---GUI--- thread
|
||||
NetPlayServer::~NetPlayServer()
|
||||
{
|
||||
if (is_connected)
|
||||
{
|
||||
m_do_loop = false;
|
||||
m_thread.join();
|
||||
m_socket.Close();
|
||||
}
|
||||
|
||||
#ifdef USE_UPNP
|
||||
|
@ -22,35 +27,15 @@ NetPlayServer::~NetPlayServer()
|
|||
}
|
||||
|
||||
// called from ---GUI--- thread
|
||||
NetPlayServer::NetPlayServer(const u16 port, const std::string& name, NetPlayUI* dialog) : NetPlay(dialog)
|
||||
NetPlayServer::NetPlayServer(const u16 port) : is_connected(false), m_is_running(false)
|
||||
{
|
||||
m_update_pings = true;
|
||||
|
||||
if (m_socket.Listen(port))
|
||||
{
|
||||
Client player;
|
||||
player.pid = 0;
|
||||
player.revision = netplay_dolphin_ver;
|
||||
player.socket = m_socket;
|
||||
player.name = name;
|
||||
|
||||
// map local pad 1 to game pad 1
|
||||
player.pad_map[0] = 0;
|
||||
|
||||
// add self to player list
|
||||
m_players[m_socket] = player;
|
||||
m_local_player = &m_players[m_socket];
|
||||
//PanicAlertT("Listening");
|
||||
|
||||
m_dialog->Update();
|
||||
|
||||
is_connected = true;
|
||||
|
||||
m_do_loop = true;
|
||||
m_selector.Add(m_socket);
|
||||
m_thread = std::thread(std::mem_fun(&NetPlayServer::ThreadFunc), this);
|
||||
}
|
||||
else
|
||||
is_connected = false;
|
||||
}
|
||||
|
||||
// called from ---NETPLAY--- thread
|
||||
|
@ -258,8 +243,6 @@ unsigned int NetPlayServer::OnConnect(sf::SocketTCP& socket)
|
|||
// add client to selector/ used for receiving
|
||||
m_selector.Add(socket);
|
||||
|
||||
m_dialog->Update();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -271,7 +254,6 @@ unsigned int NetPlayServer::OnDisconnect(sf::SocketTCP& socket)
|
|||
PanicAlertT("Client disconnect while game is running!! NetPlay is disabled. You must manually stop the game.");
|
||||
std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
|
||||
m_is_running = false;
|
||||
NetPlay_Disable();
|
||||
|
||||
sf::Packet spac;
|
||||
spac << (MessageId)NP_MSG_DISABLE_GAME;
|
||||
|
@ -293,8 +275,6 @@ unsigned int NetPlayServer::OnDisconnect(sf::SocketTCP& socket)
|
|||
std::lock_guard<std::recursive_mutex> lks(m_crit.send);
|
||||
SendToClients(spac);
|
||||
|
||||
m_dialog->Update();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -357,8 +337,6 @@ bool NetPlayServer::SetPadMapping(const int pid, const int map[])
|
|||
std::lock_guard<std::recursive_mutex> lks(m_crit.send);
|
||||
UpdatePadMapping(); // sync pad mappings with everyone
|
||||
|
||||
m_dialog->Update();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -380,29 +358,6 @@ void NetPlayServer::UpdatePadMapping()
|
|||
|
||||
}
|
||||
|
||||
// called from ---GUI--- thread and ---NETPLAY--- thread
|
||||
u64 NetPlayServer::CalculateMinimumBufferTime()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
|
||||
|
||||
std::map<sf::SocketTCP, Client>::const_iterator
|
||||
i = m_players.begin(),
|
||||
e = m_players.end();
|
||||
std::priority_queue<unsigned int> pings;
|
||||
for ( ;i!=e; ++i)
|
||||
pings.push(i->second.ping/2);
|
||||
|
||||
unsigned int required_ms = pings.top();
|
||||
// if there is more than 1 client, buffersize must be >= (2 highest ping times combined)
|
||||
if (pings.size() > 1)
|
||||
{
|
||||
pings.pop();
|
||||
required_ms += pings.top();
|
||||
}
|
||||
|
||||
return required_ms;
|
||||
}
|
||||
|
||||
// called from ---GUI--- thread and ---NETPLAY--- thread
|
||||
void NetPlayServer::AdjustPadBufferSize(unsigned int size)
|
||||
{
|
||||
|
@ -447,12 +402,6 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, sf::SocketTCP& socket)
|
|||
std::lock_guard<std::recursive_mutex> lks(m_crit.send);
|
||||
SendToClients(spac, player.pid);
|
||||
}
|
||||
|
||||
// add to gui
|
||||
std::ostringstream ss;
|
||||
ss << player.name << '[' << (char)(player.pid+'0') << "]: " << msg;
|
||||
|
||||
m_dialog->AppendChat(ss.str());
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -463,8 +412,8 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, sf::SocketTCP& socket)
|
|||
break;
|
||||
|
||||
PadMapping map = 0;
|
||||
NetPad np;
|
||||
packet >> map >> np.nHi >> np.nLo;
|
||||
int hi, lo;
|
||||
packet >> map >> hi >> lo;
|
||||
|
||||
// check if client's pad indeed maps in game
|
||||
if (map >= 0 && map < 4)
|
||||
|
@ -477,14 +426,12 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, sf::SocketTCP& socket)
|
|||
if (map < 0)
|
||||
return 1;
|
||||
|
||||
// add to pad buffer
|
||||
m_pad_buffer[(unsigned)map].Push(np);
|
||||
|
||||
// relay to clients
|
||||
sf::Packet spac;
|
||||
spac << (MessageId)NP_MSG_PAD_DATA;
|
||||
spac << map; // in game mapping
|
||||
spac << np.nHi << np.nLo;
|
||||
spac << hi << lo;
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lks(m_crit.send);
|
||||
SendToClients(spac, player.pid);
|
||||
|
@ -498,11 +445,15 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, sf::SocketTCP& socket)
|
|||
packet >> ping_key;
|
||||
|
||||
if (m_ping_key == ping_key)
|
||||
{
|
||||
//PanicAlertT("Good pong");
|
||||
player.ping = ping;
|
||||
}
|
||||
m_dialog->Update();
|
||||
|
||||
sf::Packet spac;
|
||||
spac << (MessageId)NP_MSG_PLAYER_PING_DATA;
|
||||
spac << player.pid;
|
||||
spac << player.ping;
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lks(m_crit.send);
|
||||
SendToClients(spac);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -522,25 +473,6 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, sf::SocketTCP& socket)
|
|||
return 0;
|
||||
}
|
||||
|
||||
// called from ---GUI--- thread
|
||||
void NetPlayServer::GetPlayerList(std::string& list, std::vector<int>& pid_list)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
|
||||
|
||||
std::ostringstream ss;
|
||||
|
||||
std::map<sf::SocketTCP, Client>::const_iterator
|
||||
i = m_players.begin(),
|
||||
e = m_players.end();
|
||||
for ( ; i!=e; ++i)
|
||||
{
|
||||
ss << i->second.ToString() << " " << i->second.ping << "ms\n";
|
||||
pid_list.push_back(i->second.pid);
|
||||
}
|
||||
|
||||
list = ss.str();
|
||||
}
|
||||
|
||||
// called from ---GUI--- thread / and ---NETPLAY--- thread
|
||||
void NetPlayServer::SendChatMessage(const std::string& msg)
|
||||
{
|
||||
|
@ -573,29 +505,16 @@ bool NetPlayServer::ChangeGame(const std::string &game)
|
|||
return true;
|
||||
}
|
||||
|
||||
// called from ---CPU--- thread
|
||||
void NetPlayServer::SendPadState(const PadMapping local_nb, const NetPad& np)
|
||||
// called from ---GUI--- thread
|
||||
void NetPlayServer::SetNetSettings(const NetSettings &settings)
|
||||
{
|
||||
// send to server
|
||||
sf::Packet spac;
|
||||
spac << (MessageId)NP_MSG_PAD_DATA;
|
||||
spac << m_local_player->pad_map[local_nb]; // in-game pad num
|
||||
spac << np.nHi << np.nLo;
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lks(m_crit.send);
|
||||
SendToClients(spac);
|
||||
m_settings = settings;
|
||||
}
|
||||
|
||||
// called from ---GUI--- thread
|
||||
bool NetPlayServer::StartGame(const std::string &path)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
|
||||
|
||||
GetNetSettings();
|
||||
if (false == NetPlay::StartGame(path))
|
||||
return false;
|
||||
|
||||
// TODO: i dont like this here
|
||||
m_current_game = Common::Timer::GetTimeMs();
|
||||
|
||||
// no change, just update with clients
|
||||
|
@ -604,17 +523,20 @@ bool NetPlayServer::StartGame(const std::string &path)
|
|||
// tell clients to start game
|
||||
sf::Packet spac;
|
||||
spac << (MessageId)NP_MSG_START_GAME;
|
||||
spac << NetPlay::m_current_game;
|
||||
spac << g_NetPlaySettings.m_DSPEnableJIT;
|
||||
spac << g_NetPlaySettings.m_DSPHLE;
|
||||
spac << g_NetPlaySettings.m_WriteToMemcard;
|
||||
spac << m_current_game;
|
||||
spac << m_settings.m_CPUthread;
|
||||
spac << m_settings.m_DSPEnableJIT;
|
||||
spac << m_settings.m_DSPHLE;
|
||||
spac << m_settings.m_WriteToMemcard;
|
||||
for (unsigned int i = 0; i < 4; ++i)
|
||||
spac << g_NetPlaySettings.m_Controllers[i];
|
||||
spac << m_settings.m_Controllers[i];
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
|
||||
std::lock_guard<std::recursive_mutex> lks(m_crit.send);
|
||||
SendToClients(spac);
|
||||
|
||||
m_is_running = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -622,9 +544,6 @@ bool NetPlayServer::StartGame(const std::string &path)
|
|||
// called from ---GUI--- thread
|
||||
bool NetPlayServer::StopGame()
|
||||
{
|
||||
if (false == NetPlay::StopGame())
|
||||
return false;
|
||||
|
||||
// tell clients to stop game
|
||||
sf::Packet spac;
|
||||
spac << (MessageId)NP_MSG_STOP_GAME;
|
||||
|
@ -633,6 +552,8 @@ bool NetPlayServer::StopGame()
|
|||
std::lock_guard<std::recursive_mutex> lks(m_crit.send);
|
||||
SendToClients(spac);
|
||||
|
||||
m_is_running = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
113
Source/Core/Core/Src/NetPlayServer.h
Normal file
113
Source/Core/Core/Src/NetPlayServer.h
Normal file
|
@ -0,0 +1,113 @@
|
|||
// Copyright 2013 Dolphin Emulator Project
|
||||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#ifndef _NETPLAY_SERVER_H
|
||||
#define _NETPLAY_SERVER_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "CommonTypes.h"
|
||||
#include "Thread.h"
|
||||
#include "Timer.h"
|
||||
|
||||
#include <SFML/Network.hpp>
|
||||
|
||||
#include "NetPlayProto.h"
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <queue>
|
||||
#include <sstream>
|
||||
|
||||
class NetPlayServer
|
||||
{
|
||||
public:
|
||||
void ThreadFunc();
|
||||
|
||||
NetPlayServer(const u16 port);
|
||||
~NetPlayServer();
|
||||
|
||||
bool ChangeGame(const std::string& game);
|
||||
void SendChatMessage(const std::string& msg);
|
||||
|
||||
void SetNetSettings(const NetSettings &settings);
|
||||
|
||||
bool StartGame(const std::string &path);
|
||||
bool StopGame();
|
||||
|
||||
bool GetPadMapping(const int pid, int map[]);
|
||||
bool SetPadMapping(const int pid, const int map[]);
|
||||
|
||||
void AdjustPadBufferSize(unsigned int size);
|
||||
|
||||
bool is_connected;
|
||||
|
||||
#ifdef USE_UPNP
|
||||
void TryPortmapping(u16 port);
|
||||
#endif
|
||||
|
||||
private:
|
||||
class Client
|
||||
{
|
||||
public:
|
||||
Client();
|
||||
|
||||
PlayerId pid;
|
||||
std::string name;
|
||||
PadMapping pad_map[4];
|
||||
std::string revision;
|
||||
|
||||
sf::SocketTCP socket;
|
||||
u32 ping;
|
||||
u32 current_game;
|
||||
};
|
||||
|
||||
void SendToClients(sf::Packet& packet, const PlayerId skip_pid = 0);
|
||||
unsigned int OnConnect(sf::SocketTCP& socket);
|
||||
unsigned int OnDisconnect(sf::SocketTCP& socket);
|
||||
unsigned int OnData(sf::Packet& packet, sf::SocketTCP& socket);
|
||||
void UpdatePadMapping();
|
||||
|
||||
NetSettings m_settings;
|
||||
|
||||
bool m_is_running;
|
||||
bool m_do_loop;
|
||||
Common::Timer m_ping_timer;
|
||||
u32 m_ping_key;
|
||||
bool m_update_pings;
|
||||
u32 m_current_game;
|
||||
unsigned int m_target_buffer_size;
|
||||
|
||||
std::map<sf::SocketTCP, Client> m_players;
|
||||
|
||||
struct
|
||||
{
|
||||
std::recursive_mutex game;
|
||||
// lock order
|
||||
std::recursive_mutex players, send;
|
||||
} m_crit;
|
||||
|
||||
std::string m_selected_game;
|
||||
|
||||
sf::SocketTCP m_socket;
|
||||
std::thread m_thread;
|
||||
sf::Selector<sf::SocketTCP> m_selector;
|
||||
|
||||
#ifdef USE_UPNP
|
||||
static void mapPortThread(const u16 port);
|
||||
static void unmapPortThread();
|
||||
|
||||
static bool initUPnP();
|
||||
static bool UPnPMapPort(const std::string& addr, const u16 port);
|
||||
static bool UPnPUnmapPort(const u16 port);
|
||||
|
||||
static struct UPNPUrls m_upnp_urls;
|
||||
static struct IGDdatas m_upnp_data;
|
||||
static u16 m_upnp_mapped;
|
||||
static bool m_upnp_inited;
|
||||
static bool m_upnp_error;
|
||||
static std::thread m_upnp_thread;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
|
@ -48,15 +48,10 @@ 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;
|
||||
jo.optimizeGatherPipe = true;
|
||||
}
|
||||
|
||||
void JitArm::ClearCache()
|
||||
|
@ -137,7 +132,11 @@ static void ImHere()
|
|||
void JitArm::Cleanup()
|
||||
{
|
||||
if (jo.optimizeGatherPipe && js.fifoBytesThisBlock > 0)
|
||||
{
|
||||
PUSH(4, R0, R1, R2, R3);
|
||||
QuickCallFunction(R14, (void*)&GPFifo::CheckGatherPipe);
|
||||
POP(4, R0, R1, R2, R3);
|
||||
}
|
||||
}
|
||||
void JitArm::DoDownCount()
|
||||
{
|
||||
|
@ -290,9 +289,9 @@ void STACKALIGN JitArm::Jit(u32 em_address)
|
|||
ClearCache();
|
||||
}
|
||||
|
||||
int block_num = blocks.AllocateBlock(em_address);
|
||||
int block_num = blocks.AllocateBlock(PowerPC::ppcState.pc);
|
||||
JitBlock *b = blocks.GetBlock(block_num);
|
||||
const u8* BlockPtr = DoJit(em_address, &code_buffer, b);
|
||||
const u8* BlockPtr = DoJit(PowerPC::ppcState.pc, &code_buffer, b);
|
||||
blocks.FinalizeBlock(block_num, jo.enableBlocklink, BlockPtr);
|
||||
}
|
||||
void JitArm::Break(UGeckoInstruction inst)
|
||||
|
@ -360,13 +359,13 @@ const u8* JitArm::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBlo
|
|||
|
||||
// Downcount flag check, Only valid for linked blocks
|
||||
{
|
||||
FixupBranch skip = B_CC(CC_PL);
|
||||
SetCC(CC_MI);
|
||||
ARMReg rA = gpr.GetReg(false);
|
||||
MOVI2R(rA, js.blockStart);
|
||||
STR(rA, R9, PPCSTATE_OFF(pc));
|
||||
MOVI2R(rA, (u32)asm_routines.doTiming);
|
||||
B(rA);
|
||||
SetJumpTarget(skip);
|
||||
SetCC();
|
||||
}
|
||||
|
||||
const u8 *normalEntry = GetCodePtr();
|
||||
|
@ -384,11 +383,11 @@ const u8* JitArm::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBlo
|
|||
MOVI2R(C, js.blockStart); // R3
|
||||
LDR(A, R9, PPCSTATE_OFF(msr));
|
||||
TST(A, Shift);
|
||||
FixupBranch b1 = B_CC(CC_NEQ);
|
||||
SetCC(CC_EQ);
|
||||
STR(C, R9, PPCSTATE_OFF(pc));
|
||||
MOVI2R(A, (u32)asm_routines.fpException);
|
||||
B(A);
|
||||
SetJumpTarget(b1);
|
||||
SetCC();
|
||||
gpr.Unlock(A, C);
|
||||
}
|
||||
// Conditionally add profiling code.
|
||||
|
@ -451,8 +450,9 @@ const u8* JitArm::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBlo
|
|||
if (jo.optimizeGatherPipe && js.fifoBytesThisBlock >= 32)
|
||||
{
|
||||
js.fifoBytesThisBlock -= 32;
|
||||
// TODO: This needs thunkmanager for ARM
|
||||
//ARMABI_CallFunction(thunks.ProtectFunction((void *)&GPFifo::CheckGatherPipe, 0));
|
||||
PUSH(4, R0, R1, R2, R3);
|
||||
QuickCallFunction(R14, (void*)&GPFifo::CheckGatherPipe);
|
||||
POP(4, R0, R1, R2, R3);
|
||||
}
|
||||
if (Core::g_CoreStartupParameter.bEnableDebugging)
|
||||
{
|
||||
|
|
|
@ -116,10 +116,11 @@ public:
|
|||
|
||||
void GenerateRC(int cr = 0);
|
||||
void ComputeRC(int cr = 0);
|
||||
void ComputeRC(s32 value, int cr);
|
||||
|
||||
// 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);
|
||||
void ComputeCarry();
|
||||
void GetCarryAndClear(ARMReg reg);
|
||||
void FinalizeCarry(ARMReg reg);
|
||||
|
||||
// OPCODES
|
||||
void unknown_instruction(UGeckoInstruction _inst);
|
||||
|
@ -147,18 +148,27 @@ public:
|
|||
void addi(UGeckoInstruction _inst);
|
||||
void addis(UGeckoInstruction _inst);
|
||||
void addx(UGeckoInstruction _inst);
|
||||
void addcx(UGeckoInstruction _inst);
|
||||
void addex(UGeckoInstruction _inst);
|
||||
void cmp (UGeckoInstruction _inst);
|
||||
void cmpi(UGeckoInstruction _inst);
|
||||
void cmpl(UGeckoInstruction _inst);
|
||||
void cmpli(UGeckoInstruction _inst);
|
||||
void negx(UGeckoInstruction _inst);
|
||||
void mulli(UGeckoInstruction _inst);
|
||||
void mullwx(UGeckoInstruction _inst);
|
||||
void mulhwux(UGeckoInstruction _inst);
|
||||
void ori(UGeckoInstruction _inst);
|
||||
void oris(UGeckoInstruction _inst);
|
||||
void orx(UGeckoInstruction _inst);
|
||||
void xorx(UGeckoInstruction _inst);
|
||||
void andx(UGeckoInstruction _inst);
|
||||
void andi_rc(UGeckoInstruction _inst);
|
||||
void andis_rc(UGeckoInstruction _inst);
|
||||
void rlwimix(UGeckoInstruction _inst);
|
||||
void rlwinmx(UGeckoInstruction _inst);
|
||||
void subfx(UGeckoInstruction _inst);
|
||||
void srawix(UGeckoInstruction _inst);
|
||||
void extshx(UGeckoInstruction inst);
|
||||
void extsbx(UGeckoInstruction inst);
|
||||
|
||||
|
@ -171,8 +181,10 @@ public:
|
|||
|
||||
// LoadStore
|
||||
void icbi(UGeckoInstruction _inst);
|
||||
void dcbst(UGeckoInstruction _inst);
|
||||
void lbz(UGeckoInstruction _inst);
|
||||
void lhz(UGeckoInstruction _inst);
|
||||
void lha(UGeckoInstruction _inst);
|
||||
void lwz(UGeckoInstruction _inst);
|
||||
void lwzx(UGeckoInstruction _inst);
|
||||
void stb(UGeckoInstruction _inst);
|
||||
|
@ -184,12 +196,24 @@ public:
|
|||
|
||||
// Floating point
|
||||
void fabsx(UGeckoInstruction _inst);
|
||||
void faddsx(UGeckoInstruction _inst);
|
||||
void faddx(UGeckoInstruction _inst);
|
||||
void fsubsx(UGeckoInstruction _inst);
|
||||
void fsubx(UGeckoInstruction _inst);
|
||||
void fmulsx(UGeckoInstruction _inst);
|
||||
void fmulx(UGeckoInstruction _inst);
|
||||
void fmrx(UGeckoInstruction _inst);
|
||||
|
||||
// Floating point loadStore
|
||||
void lfs(UGeckoInstruction _inst);
|
||||
void lfd(UGeckoInstruction _inst);
|
||||
|
||||
// Paired Singles
|
||||
void ps_add(UGeckoInstruction _inst);
|
||||
void ps_sum0(UGeckoInstruction _inst);
|
||||
void ps_madd(UGeckoInstruction _inst);
|
||||
void ps_sub(UGeckoInstruction _inst);
|
||||
void ps_mul(UGeckoInstruction _inst);
|
||||
};
|
||||
|
||||
#endif // _JIT64_H
|
||||
|
|
|
@ -51,6 +51,21 @@ void JitArm::fabsx(UGeckoInstruction inst)
|
|||
if (inst.Rc) Helper_UpdateCR1(vD);
|
||||
}
|
||||
|
||||
void JitArm::faddsx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(FloatingPoint)
|
||||
|
||||
ARMReg vA = fpr.R0(inst.FA);
|
||||
ARMReg vB = fpr.R0(inst.FB);
|
||||
ARMReg vD0 = fpr.R0(inst.FD);
|
||||
ARMReg vD1 = fpr.R1(inst.FD);
|
||||
|
||||
VADD(vD0, vA, vB);
|
||||
VMOV(vD1, vD0);
|
||||
if (inst.Rc) Helper_UpdateCR1(vD0);
|
||||
}
|
||||
|
||||
void JitArm::faddx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
|
@ -64,6 +79,63 @@ void JitArm::faddx(UGeckoInstruction inst)
|
|||
if (inst.Rc) Helper_UpdateCR1(vD);
|
||||
}
|
||||
|
||||
void JitArm::fsubsx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(FloatingPoint)
|
||||
|
||||
ARMReg vA = fpr.R0(inst.FA);
|
||||
ARMReg vB = fpr.R0(inst.FB);
|
||||
ARMReg vD0 = fpr.R0(inst.FD);
|
||||
ARMReg vD1 = fpr.R1(inst.FD);
|
||||
|
||||
VSUB(vD0, vA, vB);
|
||||
VMOV(vD1, vD0);
|
||||
if (inst.Rc) Helper_UpdateCR1(vD0);
|
||||
}
|
||||
|
||||
void JitArm::fsubx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(FloatingPoint)
|
||||
|
||||
ARMReg vD = fpr.R0(inst.FD);
|
||||
ARMReg vA = fpr.R0(inst.FA);
|
||||
ARMReg vB = fpr.R0(inst.FB);
|
||||
|
||||
VSUB(vD, vA, vB);
|
||||
if (inst.Rc) Helper_UpdateCR1(vD);
|
||||
}
|
||||
|
||||
// Breaks Animal Crossing
|
||||
void JitArm::fmulsx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(FloatingPoint)
|
||||
|
||||
Default(inst); return;
|
||||
|
||||
ARMReg vA = fpr.R0(inst.FA);
|
||||
ARMReg vC = fpr.R0(inst.FC);
|
||||
ARMReg vD0 = fpr.R0(inst.FD);
|
||||
ARMReg vD1 = fpr.R1(inst.FD);
|
||||
|
||||
VMUL(vD0, vA, vC);
|
||||
VMOV(vD1, vD0);
|
||||
if (inst.Rc) Helper_UpdateCR1(vD0);
|
||||
}
|
||||
void JitArm::fmulx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(FloatingPoint)
|
||||
|
||||
ARMReg vD0 = fpr.R0(inst.FD);
|
||||
ARMReg vA = fpr.R0(inst.FA);
|
||||
ARMReg vC = fpr.R0(inst.FC);
|
||||
|
||||
VMUL(vD0, vA, vC);
|
||||
if (inst.Rc) Helper_UpdateCR1(vD0);
|
||||
}
|
||||
void JitArm::fmrx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
|
|
|
@ -54,82 +54,234 @@ void JitArm::ComputeRC(int cr) {
|
|||
STRB(rB, R9, PPCSTATE_OFF(cr_fast) + cr);
|
||||
gpr.Unlock(rB);
|
||||
}
|
||||
void JitArm::ComputeRC(s32 value, int cr) {
|
||||
ARMReg rB = gpr.GetReg();
|
||||
|
||||
if (value < 0)
|
||||
MOV(rB, 0x8);
|
||||
else if (value > 0)
|
||||
MOV(rB, 0x4);
|
||||
else
|
||||
MOV(rB, 0x2);
|
||||
|
||||
STRB(rB, R9, PPCSTATE_OFF(cr_fast) + cr);
|
||||
gpr.Unlock(rB);
|
||||
}
|
||||
|
||||
void JitArm::ComputeCarry()
|
||||
{
|
||||
ARMReg tmp = gpr.GetReg();
|
||||
Operand2 mask = Operand2(2, 2); // XER_CA_MASK
|
||||
LDR(tmp, R9, PPCSTATE_OFF(spr[SPR_XER]));
|
||||
SetCC(CC_CS);
|
||||
ORR(tmp, tmp, mask);
|
||||
SetCC(CC_CC);
|
||||
BIC(tmp, tmp, mask);
|
||||
SetCC();
|
||||
STR(tmp, R9, PPCSTATE_OFF(spr[SPR_XER]));
|
||||
gpr.Unlock(tmp);
|
||||
}
|
||||
|
||||
void JitArm::GetCarryAndClear(ARMReg reg)
|
||||
{
|
||||
ARMReg tmp = gpr.GetReg();
|
||||
Operand2 mask = Operand2(2, 2); // XER_CA_MASK
|
||||
LDR(tmp, R9, PPCSTATE_OFF(spr[SPR_XER]));
|
||||
AND(reg, tmp, mask);
|
||||
BIC(tmp, tmp, mask);
|
||||
STR(tmp, R9, PPCSTATE_OFF(spr[SPR_XER]));
|
||||
gpr.Unlock(tmp);
|
||||
}
|
||||
|
||||
void JitArm::FinalizeCarry(ARMReg reg)
|
||||
{
|
||||
ARMReg tmp = gpr.GetReg();
|
||||
Operand2 mask = Operand2(2, 2); // XER_CA_MASK
|
||||
SetCC(CC_CS);
|
||||
ORR(reg, reg, mask);
|
||||
SetCC();
|
||||
LDR(tmp, R9, PPCSTATE_OFF(spr[SPR_XER]));
|
||||
ORR(tmp, tmp, reg);
|
||||
STR(tmp, R9, PPCSTATE_OFF(spr[SPR_XER]));
|
||||
gpr.Unlock(tmp);
|
||||
}
|
||||
|
||||
void JitArm::addi(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
|
||||
ARMReg RD = gpr.R(inst.RD);
|
||||
|
||||
if (inst.RA)
|
||||
u32 d = inst.RD, a = inst.RA;
|
||||
if (a)
|
||||
{
|
||||
if (gpr.IsImm(a))
|
||||
{
|
||||
gpr.SetImmediate(d, gpr.GetImm(a) + inst.SIMM_16);
|
||||
return;
|
||||
}
|
||||
ARMReg rA = gpr.GetReg(false);
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ARMReg RA = gpr.R(a);
|
||||
ARMReg RD = gpr.R(d);
|
||||
MOVI2R(rA, (u32)inst.SIMM_16);
|
||||
ADD(RD, RA, rA);
|
||||
}
|
||||
else
|
||||
MOVI2R(RD, inst.SIMM_16);
|
||||
gpr.SetImmediate(d, inst.SIMM_16);
|
||||
}
|
||||
void JitArm::addis(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
|
||||
ARMReg RD = gpr.R(inst.RD);
|
||||
if (inst.RA)
|
||||
|
||||
u32 d = inst.RD, a = inst.RA;
|
||||
if (a)
|
||||
{
|
||||
if (gpr.IsImm(a))
|
||||
{
|
||||
gpr.SetImmediate(d, gpr.GetImm(a) + (inst.SIMM_16 << 16));
|
||||
return;
|
||||
}
|
||||
ARMReg rA = gpr.GetReg(false);
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ARMReg RA = gpr.R(a);
|
||||
ARMReg RD = gpr.R(d);
|
||||
MOVI2R(rA, inst.SIMM_16 << 16);
|
||||
ADD(RD, RA, rA);
|
||||
}
|
||||
else
|
||||
MOVI2R(RD, inst.SIMM_16 << 16);
|
||||
gpr.SetImmediate(d, 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);
|
||||
u32 a = inst.RA, b = inst.RB, d = inst.RD;
|
||||
|
||||
if (gpr.IsImm(a) && gpr.IsImm(b))
|
||||
{
|
||||
gpr.SetImmediate(d, gpr.GetImm(a) + gpr.GetImm(b));
|
||||
if (inst.Rc) ComputeRC(gpr.GetImm(d), 0);
|
||||
return;
|
||||
}
|
||||
ARMReg RA = gpr.R(a);
|
||||
ARMReg RB = gpr.R(b);
|
||||
ARMReg RD = gpr.R(d);
|
||||
ADDS(RD, RA, RB);
|
||||
if (inst.Rc) ComputeRC();
|
||||
}
|
||||
|
||||
void JitArm::addcx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
u32 a = inst.RA, b = inst.RB, d = inst.RD;
|
||||
|
||||
ARMReg RA = gpr.R(a);
|
||||
ARMReg RB = gpr.R(b);
|
||||
ARMReg RD = gpr.R(d);
|
||||
ADDS(RD, RA, RB);
|
||||
ComputeCarry();
|
||||
if (inst.Rc) ComputeRC();
|
||||
}
|
||||
void JitArm::addex(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
u32 a = inst.RA, b = inst.RB, d = inst.RD;
|
||||
Default(inst); return;
|
||||
ARMReg RA = gpr.R(a);
|
||||
ARMReg RB = gpr.R(b);
|
||||
ARMReg RD = gpr.R(d);
|
||||
ARMReg rA = gpr.GetReg();
|
||||
GetCarryAndClear(rA);
|
||||
ADDS(RD, RA, RB);
|
||||
FinalizeCarry(rA);
|
||||
if (inst.Rc) ComputeRC();
|
||||
gpr.Unlock(rA);
|
||||
}
|
||||
|
||||
void JitArm::subfx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ARMReg RB = gpr.R(inst.RB);
|
||||
ARMReg RD = gpr.R(inst.RD);
|
||||
SUBS(RD, RB, RA);
|
||||
u32 a = inst.RA, b = inst.RB, d = inst.RD;
|
||||
|
||||
if (inst.OE) PanicAlert("OE: subfx");
|
||||
|
||||
if (gpr.IsImm(a) && gpr.IsImm(b))
|
||||
{
|
||||
gpr.SetImmediate(d, gpr.GetImm(b) - gpr.GetImm(a));
|
||||
if (inst.Rc) ComputeRC(gpr.GetImm(d), 0);
|
||||
return;
|
||||
}
|
||||
ARMReg RA = gpr.R(a);
|
||||
ARMReg RB = gpr.R(b);
|
||||
ARMReg RD = gpr.R(d);
|
||||
SUBS(RD, RB, RA);
|
||||
if (inst.Rc) GenerateRC();
|
||||
}
|
||||
void JitArm::mulli(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
u32 a = inst.RA, d = inst.RD;
|
||||
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ARMReg RD = gpr.R(inst.RD);
|
||||
if (gpr.IsImm(a))
|
||||
{
|
||||
gpr.SetImmediate(d, gpr.GetImm(a) * inst.SIMM_16);
|
||||
return;
|
||||
}
|
||||
ARMReg RA = gpr.R(a);
|
||||
ARMReg RD = gpr.R(d);
|
||||
ARMReg rA = gpr.GetReg();
|
||||
MOVI2R(rA, inst.SIMM_16);
|
||||
MUL(RD, RA, rA);
|
||||
gpr.Unlock(rA);
|
||||
}
|
||||
void JitArm::ori(UGeckoInstruction inst)
|
||||
|
||||
void JitArm::mullwx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ARMReg RS = gpr.R(inst.RS);
|
||||
u32 a = inst.RA, b = inst.RB, d = inst.RD;
|
||||
|
||||
ARMReg RA = gpr.R(a);
|
||||
ARMReg RB = gpr.R(b);
|
||||
ARMReg RD = gpr.R(d);
|
||||
MULS(RD, RA, RB);
|
||||
if (inst.OE) PanicAlert("OE: mullwx");
|
||||
if (inst.Rc) ComputeRC();
|
||||
}
|
||||
|
||||
void JitArm::mulhwux(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
|
||||
u32 a = inst.RA, b = inst.RB, d = inst.RD;
|
||||
|
||||
ARMReg RA = gpr.R(a);
|
||||
ARMReg RB = gpr.R(b);
|
||||
ARMReg RD = gpr.R(d);
|
||||
ARMReg rA = gpr.GetReg(false);
|
||||
UMULLS(rA, RD, RA, RB);
|
||||
if (inst.Rc) ComputeRC();
|
||||
}
|
||||
|
||||
void JitArm::ori(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
u32 a = inst.RA, s = inst.RS;
|
||||
|
||||
if (gpr.IsImm(s))
|
||||
{
|
||||
gpr.SetImmediate(a, gpr.GetImm(s) | inst.UIMM);
|
||||
return;
|
||||
}
|
||||
ARMReg RA = gpr.R(a);
|
||||
ARMReg RS = gpr.R(s);
|
||||
ARMReg rA = gpr.GetReg();
|
||||
MOVI2R(rA, inst.UIMM);
|
||||
ORR(RA, RS, rA);
|
||||
|
@ -139,9 +291,15 @@ void JitArm::oris(UGeckoInstruction inst)
|
|||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
u32 a = inst.RA, s = inst.RS;
|
||||
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ARMReg RS = gpr.R(inst.RS);
|
||||
if (gpr.IsImm(s))
|
||||
{
|
||||
gpr.SetImmediate(a, gpr.GetImm(s) | (inst.UIMM << 16));
|
||||
return;
|
||||
}
|
||||
ARMReg RA = gpr.R(a);
|
||||
ARMReg RS = gpr.R(s);
|
||||
ARMReg rA = gpr.GetReg();
|
||||
MOVI2R(rA, inst.UIMM << 16);
|
||||
ORR(RA, RS, rA);
|
||||
|
@ -152,10 +310,17 @@ void JitArm::orx(UGeckoInstruction inst)
|
|||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
u32 a = inst.RA, b = inst.RB, s = inst.RS;
|
||||
|
||||
ARMReg rA = gpr.R(inst.RA);
|
||||
ARMReg rS = gpr.R(inst.RS);
|
||||
ARMReg rB = gpr.R(inst.RB);
|
||||
if (gpr.IsImm(b) && gpr.IsImm(s))
|
||||
{
|
||||
gpr.SetImmediate(a, gpr.GetImm(s) | gpr.GetImm(b));
|
||||
if (inst.Rc) ComputeRC(gpr.GetImm(a), 0);
|
||||
return;
|
||||
}
|
||||
ARMReg rA = gpr.R(a);
|
||||
ARMReg rB = gpr.R(b);
|
||||
ARMReg rS = gpr.R(s);
|
||||
ORRS(rA, rS, rB);
|
||||
if (inst.Rc)
|
||||
ComputeRC();
|
||||
|
@ -166,23 +331,100 @@ void JitArm::xorx(UGeckoInstruction inst)
|
|||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
|
||||
ARMReg rA = gpr.R(inst.RA);
|
||||
ARMReg rS = gpr.R(inst.RS);
|
||||
ARMReg rB = gpr.R(inst.RB);
|
||||
u32 a = inst.RA, b = inst.RB, s = inst.RS;
|
||||
|
||||
if (gpr.IsImm(b) && gpr.IsImm(s))
|
||||
{
|
||||
gpr.SetImmediate(a, gpr.GetImm(s) ^ gpr.GetImm(b));
|
||||
if (inst.Rc) ComputeRC(gpr.GetImm(a), 0);
|
||||
return;
|
||||
}
|
||||
ARMReg rA = gpr.R(a);
|
||||
ARMReg rB = gpr.R(b);
|
||||
ARMReg rS = gpr.R(s);
|
||||
EORS(rA, rS, rB);
|
||||
if (inst.Rc)
|
||||
ComputeRC();
|
||||
}
|
||||
|
||||
void JitArm::andx(UGeckoInstruction inst)
|
||||
{
|
||||
u32 a = inst.RA, b = inst.RB, s = inst.RS;
|
||||
|
||||
if (gpr.IsImm(s) && gpr.IsImm(b))
|
||||
{
|
||||
gpr.SetImmediate(a, gpr.GetImm(s) & gpr.GetImm(b));
|
||||
if (inst.Rc) ComputeRC(gpr.GetImm(a), 0);
|
||||
return;
|
||||
}
|
||||
ARMReg rA = gpr.R(a);
|
||||
ARMReg rB = gpr.R(b);
|
||||
ARMReg rS = gpr.R(s);
|
||||
|
||||
ANDS(rA, rS, rB);
|
||||
|
||||
if (inst.Rc) ComputeRC();
|
||||
}
|
||||
|
||||
void JitArm::andi_rc(UGeckoInstruction inst)
|
||||
{
|
||||
u32 a = inst.RA, s = inst.RS;
|
||||
|
||||
if (gpr.IsImm(s))
|
||||
{
|
||||
gpr.SetImmediate(a, gpr.GetImm(s) & inst.UIMM);
|
||||
ComputeRC(gpr.GetImm(a), 0);
|
||||
return;
|
||||
}
|
||||
ARMReg rA = gpr.R(a);
|
||||
ARMReg rS = gpr.R(s);
|
||||
ARMReg RA = gpr.GetReg();
|
||||
|
||||
MOVI2R(RA, inst.UIMM);
|
||||
ANDS(rA, rS, RA);
|
||||
|
||||
ComputeRC();
|
||||
gpr.Unlock(RA);
|
||||
}
|
||||
|
||||
void JitArm::andis_rc(UGeckoInstruction inst)
|
||||
{
|
||||
u32 a = inst.RA, s = inst.RS;
|
||||
|
||||
if (gpr.IsImm(s))
|
||||
{
|
||||
gpr.SetImmediate(a, gpr.GetImm(s) & ((u32)inst.UIMM << 16));
|
||||
ComputeRC(gpr.GetImm(a), 0);
|
||||
return;
|
||||
}
|
||||
ARMReg rA = gpr.R(a);
|
||||
ARMReg rS = gpr.R(s);
|
||||
ARMReg RA = gpr.GetReg();
|
||||
|
||||
MOVI2R(RA, (u32)inst.UIMM << 16);
|
||||
ANDS(rA, rS, RA);
|
||||
|
||||
ComputeRC();
|
||||
gpr.Unlock(RA);
|
||||
}
|
||||
|
||||
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);
|
||||
u32 a = inst.RA, s = inst.RS;
|
||||
|
||||
if (gpr.IsImm(s))
|
||||
{
|
||||
gpr.SetImmediate(a, (u32)(s32)(s16)gpr.GetImm(s));
|
||||
if (inst.Rc) ComputeRC(gpr.GetImm(a), 0);
|
||||
return;
|
||||
}
|
||||
ARMReg rA = gpr.R(a);
|
||||
ARMReg rS = gpr.R(s);
|
||||
SXTH(rA, rS);
|
||||
if (inst.Rc){
|
||||
CMP(RA, 0);
|
||||
CMP(rA, 0);
|
||||
ComputeRC();
|
||||
}
|
||||
}
|
||||
|
@ -190,12 +432,19 @@ 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);
|
||||
u32 a = inst.RA, s = inst.RS;
|
||||
|
||||
if (gpr.IsImm(s))
|
||||
{
|
||||
gpr.SetImmediate(a, (u32)(s32)(s8)gpr.GetImm(s));
|
||||
if (inst.Rc) ComputeRC(gpr.GetImm(a), 0);
|
||||
return;
|
||||
}
|
||||
ARMReg rA = gpr.R(a);
|
||||
ARMReg rS = gpr.R(s);
|
||||
SXTB(rA, rS);
|
||||
if (inst.Rc){
|
||||
CMP(RA, 0);
|
||||
CMP(rA, 0);
|
||||
ComputeRC();
|
||||
}
|
||||
}
|
||||
|
@ -204,32 +453,66 @@ 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;
|
||||
u32 a = inst.RA, b = inst.RB;
|
||||
|
||||
if (gpr.IsImm(a) && gpr.IsImm(b))
|
||||
{
|
||||
ComputeRC((s32)gpr.GetImm(a) - (s32)gpr.GetImm(b), crf);
|
||||
return;
|
||||
}
|
||||
|
||||
ARMReg RA = gpr.R(a);
|
||||
ARMReg RB = gpr.R(b);
|
||||
CMP(RA, RB);
|
||||
|
||||
ComputeRC(crf);
|
||||
}
|
||||
void JitArm::cmpi(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
u32 a = inst.RA;
|
||||
int crf = inst.CRFD;
|
||||
if (inst.SIMM_16 >= 0 && inst.SIMM_16 < 256)
|
||||
{
|
||||
CMP(RA, inst.SIMM_16);
|
||||
}
|
||||
if (gpr.IsImm(a))
|
||||
ComputeRC((s32)gpr.GetImm(a) - inst.SIMM_16, crf);
|
||||
else
|
||||
{
|
||||
ARMReg rA = gpr.GetReg();
|
||||
MOVI2R(rA, inst.SIMM_16);
|
||||
CMP(RA, rA);
|
||||
gpr.Unlock(rA);
|
||||
ARMReg RA = gpr.R(a);
|
||||
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);
|
||||
}
|
||||
ComputeRC(crf);
|
||||
}
|
||||
void JitArm::cmpl(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
|
||||
ARMReg RA = gpr.R(inst.RA);
|
||||
ARMReg RB = gpr.R(inst.RB);
|
||||
ARMReg rA = gpr.GetReg();
|
||||
int crf = inst.CRFD;
|
||||
|
||||
CMP(RA, RB);
|
||||
// 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(rA, R9, PPCSTATE_OFF(cr_fast) + crf);
|
||||
gpr.Unlock(rA);
|
||||
}
|
||||
|
||||
void JitArm::cmpli(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
|
@ -259,7 +542,7 @@ void JitArm::cmpli(UGeckoInstruction inst)
|
|||
gpr.Unlock(rA);
|
||||
|
||||
}
|
||||
// Wrong - 27/10/2012
|
||||
|
||||
void JitArm::negx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
|
@ -330,4 +613,49 @@ void JitArm::rlwinmx(UGeckoInstruction inst)
|
|||
|
||||
//m_GPR[inst.RA] = _rotl(m_GPR[inst.RS],inst.SH) & mask;
|
||||
}
|
||||
void JitArm::srawix(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Integer)
|
||||
int a = inst.RA;
|
||||
int s = inst.RS;
|
||||
int amount = inst.SH;
|
||||
if (amount != 0)
|
||||
{
|
||||
Default(inst); return;
|
||||
ARMReg RA = gpr.R(a);
|
||||
ARMReg RS = gpr.R(s);
|
||||
ARMReg tmp = gpr.GetReg();
|
||||
Operand2 mask = Operand2(2, 2); // XER_CA_MASK
|
||||
|
||||
MOV(tmp, RS);
|
||||
ASRS(RA, RS, amount);
|
||||
if (inst.Rc)
|
||||
GenerateRC();
|
||||
LSL(tmp, tmp, 32 - amount);
|
||||
TST(tmp, RA);
|
||||
|
||||
LDR(tmp, R9, PPCSTATE_OFF(spr[SPR_XER]));
|
||||
BIC(tmp, tmp, mask);
|
||||
SetCC(CC_EQ);
|
||||
ORR(tmp, tmp, mask);
|
||||
SetCC();
|
||||
STR(tmp, R9, PPCSTATE_OFF(spr[SPR_XER]));
|
||||
gpr.Unlock(tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
ARMReg RA = gpr.R(a);
|
||||
ARMReg RS = gpr.R(s);
|
||||
MOV(RA, RS);
|
||||
|
||||
ARMReg tmp = gpr.GetReg();
|
||||
Operand2 mask = Operand2(2, 2); // XER_CA_MASK
|
||||
LDR(tmp, R9, PPCSTATE_OFF(spr[SPR_XER]));
|
||||
BIC(tmp, tmp, mask);
|
||||
STR(tmp, R9, PPCSTATE_OFF(spr[SPR_XER]));
|
||||
gpr.Unlock(tmp);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,36 +31,12 @@
|
|||
#include "JitRegCache.h"
|
||||
#include "JitAsm.h"
|
||||
|
||||
#ifdef ANDROID
|
||||
#define FASTMEM 0
|
||||
#else
|
||||
#define FASTMEM 0
|
||||
#endif
|
||||
void JitArm::stb(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(LoadStore)
|
||||
|
||||
ARMReg RS = gpr.R(inst.RS);
|
||||
#if 0 // 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, 16, 0);
|
||||
#else
|
||||
ARMReg ValueReg = gpr.GetReg();
|
||||
ARMReg Addr = gpr.GetReg();
|
||||
ARMReg Function = gpr.GetReg();
|
||||
|
@ -82,7 +58,6 @@ void JitArm::stb(UGeckoInstruction inst)
|
|||
BL(Function);
|
||||
POP(4, R0, R1, R2, R3);
|
||||
gpr.Unlock(ValueReg, Addr, Function);
|
||||
#endif
|
||||
}
|
||||
|
||||
void JitArm::stbu(UGeckoInstruction inst)
|
||||
|
@ -122,25 +97,6 @@ void JitArm::sth(UGeckoInstruction inst)
|
|||
JITDISABLE(LoadStore)
|
||||
|
||||
ARMReg RS = gpr.R(inst.RS);
|
||||
#if 0 // 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, 16, 0);
|
||||
#else
|
||||
ARMReg ValueReg = gpr.GetReg();
|
||||
ARMReg Addr = gpr.GetReg();
|
||||
ARMReg Function = gpr.GetReg();
|
||||
|
@ -162,7 +118,6 @@ void JitArm::sth(UGeckoInstruction inst)
|
|||
BL(Function);
|
||||
POP(4, R0, R1, R2, R3);
|
||||
gpr.Unlock(ValueReg, Addr, Function);
|
||||
#endif
|
||||
}
|
||||
void JitArm::sthu(UGeckoInstruction inst)
|
||||
{
|
||||
|
@ -203,29 +158,6 @@ void JitArm::stw(UGeckoInstruction inst)
|
|||
JITDISABLE(LoadStore)
|
||||
|
||||
ARMReg RS = gpr.R(inst.RS);
|
||||
#if FASTMEM
|
||||
// R10 contains the dest address
|
||||
if (Core::g_CoreStartupParameter.bFastmem)
|
||||
{
|
||||
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
|
||||
#endif
|
||||
{
|
||||
ARMReg ValueReg = gpr.GetReg();
|
||||
ARMReg Addr = gpr.GetReg();
|
||||
|
@ -282,79 +214,6 @@ void JitArm::stwu(UGeckoInstruction inst)
|
|||
|
||||
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(value, dest); // 8
|
||||
break;
|
||||
case 16:
|
||||
STRH(value, dest);
|
||||
break;
|
||||
case 8:
|
||||
STRB(value, dest);
|
||||
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
|
||||
|
@ -366,24 +225,6 @@ void JitArm::lbz(UGeckoInstruction inst)
|
|||
LDR(rA, R9, PPCSTATE_OFF(Exceptions));
|
||||
CMP(rA, EXCEPTION_DSI);
|
||||
FixupBranch DoNotLoad = B_CC(CC_EQ);
|
||||
#if FASTMEM
|
||||
// Backpatch route
|
||||
// Gets loaded in to RD
|
||||
// Address is in R10
|
||||
if (Core::g_CoreStartupParameter.bFastmem)
|
||||
{
|
||||
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
|
||||
#endif
|
||||
{
|
||||
if (inst.RA)
|
||||
{
|
||||
|
@ -417,22 +258,6 @@ void JitArm::lhz(UGeckoInstruction inst)
|
|||
LDR(rA, R9, PPCSTATE_OFF(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);
|
||||
MOV(R10, RA); // - 4
|
||||
}
|
||||
else
|
||||
MOV(R10, 0); // - 4
|
||||
|
||||
LoadToReg(RD, R10, 16, (u32)inst.SIMM_16);
|
||||
#else
|
||||
|
||||
if (inst.RA)
|
||||
{
|
||||
MOVI2R(rB, inst.SIMM_16);
|
||||
|
@ -450,9 +275,41 @@ void JitArm::lhz(UGeckoInstruction inst)
|
|||
POP(4, R0, R1, R2, R3);
|
||||
MOV(RD, rA);
|
||||
gpr.Unlock(rA, rB);
|
||||
#endif
|
||||
SetJumpTarget(DoNotLoad);
|
||||
}
|
||||
void JitArm::lha(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(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_U16);
|
||||
PUSH(4, R0, R1, R2, R3);
|
||||
MOV(R0, rB);
|
||||
BL(rA);
|
||||
MOV(rA, R0);
|
||||
SXTH(rA, rA);
|
||||
POP(4, R0, R1, R2, R3);
|
||||
MOV(RD, rA);
|
||||
gpr.Unlock(rA, rB);
|
||||
SetJumpTarget(DoNotLoad);
|
||||
}
|
||||
|
||||
void JitArm::lwz(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
|
@ -464,25 +321,6 @@ void JitArm::lwz(UGeckoInstruction inst)
|
|||
LDR(rA, R9, PPCSTATE_OFF(Exceptions));
|
||||
CMP(rA, EXCEPTION_DSI);
|
||||
FixupBranch DoNotLoad = B_CC(CC_EQ);
|
||||
|
||||
#if FASTMEM
|
||||
// Backpatch route
|
||||
// Gets loaded in to RD
|
||||
// Address is in R10
|
||||
if (Core::g_CoreStartupParameter.bFastmem)
|
||||
{
|
||||
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
|
||||
#endif
|
||||
{
|
||||
if (inst.RA)
|
||||
{
|
||||
|
@ -543,24 +381,6 @@ void JitArm::lwzx(UGeckoInstruction inst)
|
|||
LDR(rA, R9, PPCSTATE_OFF(Exceptions));
|
||||
CMP(rA, EXCEPTION_DSI);
|
||||
FixupBranch DoNotLoad = B_CC(CC_EQ);
|
||||
#if FASTMEM
|
||||
// Backpatch route
|
||||
// Gets loaded in to RD
|
||||
// Address is in R10
|
||||
if (Core::g_CoreStartupParameter.bFastmem)
|
||||
{
|
||||
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
|
||||
#endif
|
||||
{
|
||||
if (inst.RA)
|
||||
{
|
||||
|
@ -582,6 +402,21 @@ void JitArm::lwzx(UGeckoInstruction inst)
|
|||
SetJumpTarget(DoNotLoad);
|
||||
//// u32 temp = Memory::Read_U32(_inst.RA ? (m_GPR[_inst.RA] + m_GPR[_inst.RB]) : m_GPR[_inst.RB]);
|
||||
}
|
||||
|
||||
void JitArm::dcbst(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(LoadStore)
|
||||
|
||||
// If the dcbst instruction is preceded by dcbt, it is flushing a prefetched
|
||||
// memory location. Do not invalidate the JIT cache in this case as the memory
|
||||
// will be the same.
|
||||
// dcbt = 0x7c00022c
|
||||
if ((Memory::ReadUnchecked_U32(js.compilerPC - 4) & 0x7c00022c) != 0x7c00022c)
|
||||
{
|
||||
Default(inst); return;
|
||||
}
|
||||
}
|
||||
void JitArm::icbi(UGeckoInstruction inst)
|
||||
{
|
||||
Default(inst);
|
||||
|
|
|
@ -33,6 +33,42 @@
|
|||
#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(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);
|
||||
|
||||
ARMReg v0 = fpr.R0(inst.FD);
|
||||
ARMReg v1 = fpr.R1(inst.FD);
|
||||
|
||||
MOVI2R(rA, (u32)&Memory::Read_F32);
|
||||
PUSH(4, R0, R1, R2, R3);
|
||||
MOV(R0, rB);
|
||||
BL(rA);
|
||||
VCVT(v0, S0, 0);
|
||||
VCVT(v1, S0, 0);
|
||||
POP(4, R0, R1, R2, R3);
|
||||
|
||||
gpr.Unlock(rA, rB);
|
||||
SetJumpTarget(DoNotLoad);
|
||||
}
|
||||
|
||||
void JitArm::lfd(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(LoadStoreFloating)
|
||||
|
@ -53,43 +89,7 @@ void JitArm::lfs(UGeckoInstruction inst)
|
|||
else
|
||||
MOVI2R(rB, (u32)inst.SIMM_16);
|
||||
|
||||
ARMReg v0 = fpr.R0(inst.FD, false);
|
||||
ARMReg v1 = fpr.R1(inst.FD, false);
|
||||
|
||||
MOVI2R(rA, (u32)&Memory::Read_F32);
|
||||
PUSH(4, R0, R1, R2, R3);
|
||||
MOV(R0, rB);
|
||||
BL(rA);
|
||||
// XXX: Need to use VCVT here.
|
||||
VMOV(v0, D0);
|
||||
VMOV(v1, D0);
|
||||
POP(4, R0, R1, R2, R3);
|
||||
|
||||
gpr.Unlock(rA, rB);
|
||||
SetJumpTarget(DoNotLoad);
|
||||
}
|
||||
|
||||
void JitArm::lfd(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(LoadStoreFloating)
|
||||
|
||||
ARMReg rA = gpr.GetReg();
|
||||
ARMReg rB = gpr.GetReg();
|
||||
LDR(rA, R9, PPCSTATE_OFF(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);
|
||||
|
||||
ARMReg v0 = fpr.R0(inst.FD, false);
|
||||
ARMReg v0 = fpr.R0(inst.FD);
|
||||
|
||||
MOVI2R(rA, (u32)&Memory::Read_F64);
|
||||
PUSH(4, R0, R1, R2, R3);
|
||||
|
|
149
Source/Core/Core/Src/PowerPC/JitArm32/JitArm_Paired.cpp
Normal file
149
Source/Core/Core/Src/PowerPC/JitArm32/JitArm_Paired.cpp
Normal file
|
@ -0,0 +1,149 @@
|
|||
// 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"
|
||||
|
||||
// Wrong, THP videos like SMS and Ikaruga show artifacts
|
||||
void JitArm::ps_add(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Paired)
|
||||
|
||||
Default(inst); return;
|
||||
|
||||
u32 a = inst.FA, b = inst.FB, d = inst.FD;
|
||||
if (inst.Rc){
|
||||
Default(inst); return;
|
||||
}
|
||||
ARMReg vA0 = fpr.R0(a);
|
||||
ARMReg vA1 = fpr.R1(a);
|
||||
ARMReg vB0 = fpr.R0(b);
|
||||
ARMReg vB1 = fpr.R1(b);
|
||||
ARMReg vD0 = fpr.R0(d);
|
||||
ARMReg vD1 = fpr.R1(d);
|
||||
|
||||
VADD(vD0, vA0, vB0);
|
||||
VADD(vD1, vA1, vB1);
|
||||
}
|
||||
|
||||
// Wrong, THP videos like SMS and Ikaruga show artifacts
|
||||
void JitArm::ps_madd(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Paired)
|
||||
|
||||
Default(inst); return;
|
||||
|
||||
u32 a = inst.FA, b = inst.FB, c = inst.FC, d = inst.FD;
|
||||
|
||||
if (inst.Rc) {
|
||||
Default(inst); return;
|
||||
}
|
||||
ARMReg vA0 = fpr.R0(a);
|
||||
ARMReg vA1 = fpr.R1(a);
|
||||
ARMReg vB0 = fpr.R0(b);
|
||||
ARMReg vB1 = fpr.R1(b);
|
||||
ARMReg vC0 = fpr.R0(c);
|
||||
ARMReg vC1 = fpr.R1(c);
|
||||
ARMReg vD0 = fpr.R0(d);
|
||||
ARMReg vD1 = fpr.R1(d);
|
||||
|
||||
ARMReg V0 = fpr.GetReg();
|
||||
ARMReg V1 = fpr.GetReg();
|
||||
|
||||
VMOV(V0, vC0);
|
||||
VMOV(V1, vC1);
|
||||
|
||||
VMLA(V0, vA0, vB0);
|
||||
VMLA(V1, vA1, vB1);
|
||||
|
||||
VMOV(vD0, V0);
|
||||
VMOV(vD1, V1);
|
||||
|
||||
fpr.Unlock(V0);
|
||||
fpr.Unlock(V1);
|
||||
}
|
||||
|
||||
void JitArm::ps_sum0(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Paired)
|
||||
|
||||
u32 a = inst.FA, b = inst.FB, c = inst.FC, d = inst.FD;
|
||||
|
||||
if (inst.Rc) {
|
||||
Default(inst); return;
|
||||
}
|
||||
ARMReg vA0 = fpr.R0(a);
|
||||
ARMReg vB1 = fpr.R1(b);
|
||||
ARMReg vC1 = fpr.R1(c);
|
||||
ARMReg vD0 = fpr.R0(d);
|
||||
ARMReg vD1 = fpr.R1(d);
|
||||
|
||||
VADD(vD0, vA0, vB1);
|
||||
VMOV(vD1, vC1);
|
||||
}
|
||||
|
||||
void JitArm::ps_sub(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Paired)
|
||||
|
||||
u32 a = inst.FA, b = inst.FB, d = inst.FD;
|
||||
if (inst.Rc){
|
||||
Default(inst); return;
|
||||
}
|
||||
ARMReg vA0 = fpr.R0(a);
|
||||
ARMReg vA1 = fpr.R1(a);
|
||||
ARMReg vB0 = fpr.R0(b);
|
||||
ARMReg vB1 = fpr.R1(b);
|
||||
ARMReg vD0 = fpr.R0(d);
|
||||
ARMReg vD1 = fpr.R1(d);
|
||||
|
||||
VSUB(vD0, vA0, vB0);
|
||||
VSUB(vD1, vA1, vB1);
|
||||
}
|
||||
|
||||
void JitArm::ps_mul(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(Paired)
|
||||
u32 a = inst.FA, c = inst.FC, d = inst.FD;
|
||||
if (inst.Rc){
|
||||
Default(inst); return;
|
||||
}
|
||||
ARMReg vA0 = fpr.R0(a);
|
||||
ARMReg vA1 = fpr.R1(a);
|
||||
ARMReg vC0 = fpr.R0(c);
|
||||
ARMReg vC1 = fpr.R1(c);
|
||||
ARMReg vD0 = fpr.R0(d);
|
||||
ARMReg vD1 = fpr.R1(d);
|
||||
|
||||
VMUL(vD0, vA0, vC0);
|
||||
VMUL(vD1, vA1, vC1);
|
||||
}
|
||||
|
|
@ -33,7 +33,6 @@ void JitArm::mtspr(UGeckoInstruction inst)
|
|||
JITDISABLE(SystemRegisters)
|
||||
|
||||
u32 iIndex = (inst.SPRU << 5) | (inst.SPRL & 0x1F);
|
||||
ARMReg RD = gpr.R(inst.RD);
|
||||
|
||||
switch (iIndex)
|
||||
{
|
||||
|
@ -70,6 +69,7 @@ void JitArm::mtspr(UGeckoInstruction inst)
|
|||
}
|
||||
|
||||
// OK, this is easy.
|
||||
ARMReg RD = gpr.R(inst.RD);
|
||||
STR(RD, R9, PPCSTATE_OFF(spr) + iIndex * 4);
|
||||
}
|
||||
void JitArm::mftb(UGeckoInstruction inst)
|
||||
|
@ -105,13 +105,16 @@ void JitArm::mtmsr(UGeckoInstruction inst)
|
|||
//JITDISABLE(SystemRegisters)
|
||||
|
||||
STR(gpr.R(inst.RS), R9, PPCSTATE_OFF(msr));
|
||||
|
||||
gpr.Flush();
|
||||
fpr.Flush();
|
||||
|
||||
WriteExit(js.compilerPC + 4, 0);
|
||||
}
|
||||
void JitArm::mfmsr(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(SystemRegisters)
|
||||
Default(inst); return;
|
||||
|
||||
LDR(gpr.R(inst.RD), R9, PPCSTATE_OFF(msr));
|
||||
}
|
||||
|
|
|
@ -75,8 +75,8 @@ static GekkoOPTemplate primarytable[] =
|
|||
{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}},
|
||||
{28, &JitArm::andi_rc}, //"andi_rc", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_SET_CR0}},
|
||||
{29, &JitArm::andis_rc}, //"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}},
|
||||
|
@ -84,7 +84,7 @@ static GekkoOPTemplate primarytable[] =
|
|||
{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}},
|
||||
{42, &JitArm::lha}, //"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::sth}, //"sth", OPTYPE_STORE, FL_IN_A | FL_IN_S}},
|
||||
|
@ -143,21 +143,21 @@ static GekkoOPTemplate table4[] =
|
|||
|
||||
static GekkoOPTemplate table4_2[] =
|
||||
{
|
||||
{10, &JitArm::Default}, //"ps_sum0", OPTYPE_PS, 0}},
|
||||
{10, &JitArm::ps_sum0}, //"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}},
|
||||
{20, &JitArm::ps_sub}, //"ps_sub", OPTYPE_PS, 0}},
|
||||
{21, &JitArm::ps_add}, //"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}},
|
||||
{25, &JitArm::ps_mul}, //"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}},
|
||||
{29, &JitArm::ps_madd}, //"ps_madd", OPTYPE_PS, 0}},
|
||||
{30, &JitArm::Default}, //"ps_nmsub", OPTYPE_PS, 0}},
|
||||
{31, &JitArm::Default}, //"ps_nmadd", OPTYPE_PS, 0}},
|
||||
};
|
||||
|
@ -194,7 +194,7 @@ static GekkoOPTemplate table19[] =
|
|||
|
||||
static GekkoOPTemplate table31[] =
|
||||
{
|
||||
{28, &JitArm::Default}, //"andx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
|
||||
{28, &JitArm::andx}, //"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}},
|
||||
|
@ -203,16 +203,16 @@ static GekkoOPTemplate table31[] =
|
|||
{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}},
|
||||
{32, &JitArm::cmpl}, //"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}},
|
||||
{824, &JitArm::srawix}, //"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}},
|
||||
{54, &JitArm::dcbst}, //"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}},
|
||||
|
@ -309,8 +309,8 @@ 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}},
|
||||
{10, &JitArm::addcx}, //"addcx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT}},
|
||||
{138, &JitArm::addex}, //"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}},
|
||||
|
@ -318,8 +318,8 @@ static GekkoOPTemplate table31_2[] =
|
|||
{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}},
|
||||
{11, &JitArm::mulhwux}, //"mulhwux", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}},
|
||||
{235, &JitArm::mullwx}, //"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::subfx}, //"subfx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
|
||||
|
@ -333,11 +333,11 @@ static GekkoOPTemplate table31_2[] =
|
|||
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}},
|
||||
{20, &JitArm::fsubsx}, //"fsubsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{21, &JitArm::faddsx}, //"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}},
|
||||
{25, &JitArm::fmulsx}, //"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}},
|
||||
|
@ -367,11 +367,11 @@ static GekkoOPTemplate table63[] =
|
|||
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}},
|
||||
{20, &JitArm::fsubx}, //"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}},
|
||||
{25, &JitArm::fmulx}, //"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}},
|
||||
|
|
|
@ -65,34 +65,30 @@ void JitArmAsmRoutineManager::Generate()
|
|||
// It runs though to the compiling portion if it isn't found
|
||||
LDR(R12, R9, PPCSTATE_OFF(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
|
||||
Operand2 iCacheMask = Operand2(0xE, 2); // JIT_ICACHE_MASK
|
||||
BIC(R12, R12, iCacheMask); // R12 contains PC & JIT_ICACHE_MASK here.
|
||||
|
||||
MOVI2R(R14, (u32)jit->GetBlockCache()->GetICache());
|
||||
// Confirmed That this loads the base iCache Location correctly 08-04-12
|
||||
|
||||
LDR(R12, R14, R12); // 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); // Load the block address in to R14
|
||||
SetCC(CC_EQ);
|
||||
// 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); // 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
|
||||
B(R14);
|
||||
// No need to jump anywhere after here, the block will go back to dispatcher start
|
||||
SetCC();
|
||||
|
||||
// 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(pc));
|
||||
BL(R14);
|
||||
|
||||
B(dispatcherNoCheck);
|
||||
|
@ -129,7 +125,6 @@ void JitArmAsmRoutineManager::Generate()
|
|||
TST(R0, R1);
|
||||
FixupBranch Exit = B_CC(CC_NEQ);
|
||||
|
||||
SetJumpTarget(NextBlock);
|
||||
B(dispatcher);
|
||||
|
||||
SetJumpTarget(Exit);
|
||||
|
|
|
@ -35,6 +35,7 @@ void ArmFPRCache::Init(ARMXEmitter *emitter)
|
|||
ArmCRegs[a].Reg = PPCRegs[a];
|
||||
ArmCRegs[a].LastLoad = 0;
|
||||
ArmCRegs[a].PS1 = false;
|
||||
ArmCRegs[a].Away = true;
|
||||
}
|
||||
for(u8 a = 0; a < NUMARMREG; ++a)
|
||||
{
|
||||
|
@ -117,30 +118,40 @@ ARMReg ArmFPRCache::GetPPCReg(u32 preg, bool PS1, bool preLoad)
|
|||
if (ArmCRegs[a].PPCReg == preg && ArmCRegs[a].PS1 == PS1)
|
||||
{
|
||||
ArmCRegs[a].LastLoad = 0;
|
||||
// Check if the value is actually in the reg
|
||||
if (ArmCRegs[a].Away && preLoad)
|
||||
{
|
||||
// Load it now since we want it
|
||||
s16 offset = PPCSTATE_OFF(ps) + (preg * 16) + (PS1 ? 8 : 0);
|
||||
emit->VLDR(ArmCRegs[a].Reg, R9, offset);
|
||||
ArmCRegs[a].Away = false;
|
||||
}
|
||||
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(ps) + (preg * 16) + (PS1 ? 8 : 0);
|
||||
s16 offset = PPCSTATE_OFF(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;
|
||||
ArmCRegs[a].Away = !preLoad;
|
||||
return ArmCRegs[a].Reg;
|
||||
}
|
||||
// Alright, we couldn't get a free space, dump that least used register
|
||||
u16 offsetOld = PPCSTATE_OFF(ps) + (ArmCRegs[Num].PPCReg * 16) + (ArmCRegs[Num].PS1 ? 8 : 0);
|
||||
s16 offsetOld = PPCSTATE_OFF(ps) + (ArmCRegs[Num].PPCReg * 16) + (ArmCRegs[Num].PS1 ? 8 : 0);
|
||||
emit->VSTR(ArmCRegs[Num].Reg, R9, offsetOld);
|
||||
|
||||
u16 offsetNew = PPCSTATE_OFF(ps) + (preg * 16) + (PS1 ? 8 : 0);
|
||||
s16 offsetNew = PPCSTATE_OFF(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;
|
||||
ArmCRegs[Num].Away = !preLoad;
|
||||
return ArmCRegs[Num].Reg;
|
||||
|
||||
}
|
||||
|
@ -160,10 +171,11 @@ void ArmFPRCache::Flush()
|
|||
for(u8 a = 0; a < NUMPPCREG; ++a)
|
||||
if (ArmCRegs[a].PPCReg != 33)
|
||||
{
|
||||
u16 offset = PPCSTATE_OFF(ps) + (ArmCRegs[a].PPCReg * 16) + (ArmCRegs[a].PS1 ? 8 : 0);
|
||||
s16 offset = PPCSTATE_OFF(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;
|
||||
ArmCRegs[a].Away = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,13 +28,7 @@ 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;
|
||||
|
@ -49,14 +43,8 @@ void ArmRegCache::Init(ARMXEmitter *emitter)
|
|||
}
|
||||
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
|
||||
|
@ -94,16 +82,7 @@ ARMReg ArmRegCache::GetReg(bool AutoLock)
|
|||
_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)
|
||||
|
@ -118,51 +97,130 @@ void ArmRegCache::Unlock(ARMReg R0, ARMReg R1, ARMReg R2, ARMReg R3)
|
|||
if( R3 != INVALID_REG && ArmRegs[RegNum].Reg == R3) ArmRegs[RegNum].free = true;
|
||||
}
|
||||
}
|
||||
|
||||
ARMReg ArmRegCache::R(u32 preg)
|
||||
u32 ArmRegCache::GetLeastUsedRegister(bool increment)
|
||||
{
|
||||
u32 HighestUsed = 0;
|
||||
u8 Num = 0;
|
||||
u8 lastRegIndex = 0;
|
||||
for(u8 a = 0; a < NUMPPCREG; ++a){
|
||||
++ArmCRegs[a].LastLoad;
|
||||
if (increment)
|
||||
++ArmCRegs[a].LastLoad;
|
||||
if (ArmCRegs[a].LastLoad > HighestUsed)
|
||||
{
|
||||
HighestUsed = ArmCRegs[a].LastLoad;
|
||||
Num = a;
|
||||
lastRegIndex = 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
|
||||
return lastRegIndex;
|
||||
}
|
||||
bool ArmRegCache::FindFreeRegister(u32 ®index)
|
||||
{
|
||||
for (u8 a = 0; a < NUMPPCREG; ++a)
|
||||
if (ArmCRegs[a].PPCReg == 33)
|
||||
{
|
||||
emit->LDR(ArmCRegs[a].Reg, R9, PPCSTATE_OFF(gpr) + preg * 4);
|
||||
ArmCRegs[a].PPCReg = preg;
|
||||
ArmCRegs[a].LastLoad = 0;
|
||||
return ArmCRegs[a].Reg;
|
||||
regindex = a;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ARMReg ArmRegCache::R(u32 preg)
|
||||
{
|
||||
if (regs[preg].GetType() == REG_IMM)
|
||||
{
|
||||
return BindToRegister(preg);
|
||||
//asm ("bkpt #1;");
|
||||
}
|
||||
u32 lastRegIndex = GetLeastUsedRegister(true);
|
||||
|
||||
// Check if already Loaded
|
||||
if(regs[preg].GetType() == REG_REG)
|
||||
{
|
||||
u8 a = regs[preg].GetRegIndex();
|
||||
ArmCRegs[a].LastLoad = 0;
|
||||
return ArmCRegs[a].Reg;
|
||||
}
|
||||
|
||||
// Check if we have a free register
|
||||
u32 regindex;
|
||||
if (FindFreeRegister(regindex))
|
||||
{
|
||||
emit->LDR(ArmCRegs[regindex].Reg, R9, PPCSTATE_OFF(gpr) + preg * 4);
|
||||
ArmCRegs[regindex].PPCReg = preg;
|
||||
ArmCRegs[regindex].LastLoad = 0;
|
||||
|
||||
regs[preg].LoadToReg(regindex);
|
||||
return ArmCRegs[regindex].Reg;
|
||||
}
|
||||
|
||||
// Alright, we couldn't get a free space, dump that least used register
|
||||
emit->STR(ArmCRegs[Num].Reg, R9, PPCSTATE_OFF(gpr) + ArmCRegs[Num].PPCReg * 4);
|
||||
emit->LDR(ArmCRegs[Num].Reg, R9, PPCSTATE_OFF(gpr) + preg * 4);
|
||||
ArmCRegs[Num].PPCReg = preg;
|
||||
ArmCRegs[Num].LastLoad = 0;
|
||||
return ArmCRegs[Num].Reg;
|
||||
emit->STR(ArmCRegs[lastRegIndex].Reg, R9, PPCSTATE_OFF(gpr) + ArmCRegs[lastRegIndex].PPCReg * 4);
|
||||
emit->LDR(ArmCRegs[lastRegIndex].Reg, R9, PPCSTATE_OFF(gpr) + preg * 4);
|
||||
|
||||
regs[ArmCRegs[lastRegIndex].PPCReg].Flush();
|
||||
|
||||
ArmCRegs[lastRegIndex].PPCReg = preg;
|
||||
ArmCRegs[lastRegIndex].LastLoad = 0;
|
||||
|
||||
regs[preg].LoadToReg(lastRegIndex);
|
||||
|
||||
return ArmCRegs[lastRegIndex].Reg;
|
||||
}
|
||||
|
||||
ARMReg ArmRegCache::BindToRegister(u32 preg)
|
||||
{
|
||||
_assert_msg_(DYNA_REC, regs[preg].GetType() == REG_IMM, "Can't BindToRegister with a REG");
|
||||
u32 lastRegIndex = GetLeastUsedRegister(false);
|
||||
u32 freeRegIndex;
|
||||
if (FindFreeRegister(freeRegIndex))
|
||||
{
|
||||
emit->MOVI2R(ArmCRegs[freeRegIndex].Reg, regs[preg].GetImm());
|
||||
ArmCRegs[freeRegIndex].PPCReg = preg;
|
||||
ArmCRegs[freeRegIndex].LastLoad = 0;
|
||||
regs[preg].LoadToReg(freeRegIndex);
|
||||
return ArmCRegs[freeRegIndex].Reg;
|
||||
}
|
||||
else
|
||||
{
|
||||
emit->STR(ArmCRegs[lastRegIndex].Reg, R9, PPCSTATE_OFF(gpr) + ArmCRegs[lastRegIndex].PPCReg * 4);
|
||||
emit->MOVI2R(ArmCRegs[lastRegIndex].Reg, regs[preg].GetImm());
|
||||
|
||||
regs[ArmCRegs[lastRegIndex].PPCReg].Flush();
|
||||
|
||||
ArmCRegs[lastRegIndex].PPCReg = preg;
|
||||
ArmCRegs[lastRegIndex].LastLoad = 0;
|
||||
|
||||
regs[preg].LoadToReg(lastRegIndex);
|
||||
return ArmCRegs[lastRegIndex].Reg;
|
||||
}
|
||||
}
|
||||
|
||||
void ArmRegCache::SetImmediate(u32 preg, u32 imm)
|
||||
{
|
||||
if (regs[preg].GetType() == REG_REG)
|
||||
{
|
||||
// Dump real reg at this point
|
||||
u32 regindex = regs[preg].GetRegIndex();
|
||||
ArmCRegs[regindex].PPCReg = 33;
|
||||
ArmCRegs[regindex].LastLoad = 0;
|
||||
}
|
||||
regs[preg].LoadToImm(imm);
|
||||
}
|
||||
|
||||
void ArmRegCache::Flush()
|
||||
{
|
||||
for(u8 a = 0; a < NUMPPCREG; ++a)
|
||||
if (ArmCRegs[a].PPCReg != 33)
|
||||
for (u8 a = 0; a < 32; ++a)
|
||||
{
|
||||
if (regs[a].GetType() == REG_IMM)
|
||||
BindToRegister(a);
|
||||
if (regs[a].GetType() == REG_REG)
|
||||
{
|
||||
emit->STR(ArmCRegs[a].Reg, R9, PPCSTATE_OFF(gpr) + ArmCRegs[a].PPCReg * 4);
|
||||
ArmCRegs[a].PPCReg = 33;
|
||||
ArmCRegs[a].LastLoad = 0;
|
||||
u32 regindex = regs[a].GetRegIndex();
|
||||
emit->STR(ArmCRegs[regindex].Reg, R9, PPCSTATE_OFF(gpr) + a * 4);
|
||||
ArmCRegs[regindex].PPCReg = 33;
|
||||
ArmCRegs[regindex].LastLoad = 0;
|
||||
}
|
||||
|
||||
regs[a].Flush();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,17 +35,68 @@ using namespace ArmGen;
|
|||
// it
|
||||
// So we have R14, R12, R11, R10 to work with instructions
|
||||
|
||||
struct PPCCachedReg
|
||||
enum RegType
|
||||
{
|
||||
const u8 *location;
|
||||
u32 UsesLeft;
|
||||
REG_NOTLOADED = 0,
|
||||
REG_REG,
|
||||
REG_IMM,
|
||||
};
|
||||
|
||||
class OpArg
|
||||
{
|
||||
private:
|
||||
class Reg{
|
||||
public:
|
||||
RegType m_type;
|
||||
u8 m_reg; // index to register
|
||||
u32 m_value;
|
||||
Reg()
|
||||
{
|
||||
m_type = REG_NOTLOADED;
|
||||
m_reg = 33;
|
||||
m_value = 0;
|
||||
}
|
||||
} Reg;
|
||||
|
||||
public:
|
||||
OpArg(){}
|
||||
|
||||
RegType GetType()
|
||||
{
|
||||
return Reg.m_type;
|
||||
}
|
||||
|
||||
u8 GetRegIndex()
|
||||
{
|
||||
return Reg.m_reg;
|
||||
}
|
||||
u32 GetImm()
|
||||
{
|
||||
return Reg.m_value;
|
||||
}
|
||||
void LoadToReg(u8 reg)
|
||||
{
|
||||
Reg.m_type = REG_REG;
|
||||
Reg.m_reg = reg;
|
||||
}
|
||||
void LoadToImm(u32 imm)
|
||||
{
|
||||
Reg.m_type = REG_IMM;
|
||||
Reg.m_value = imm;
|
||||
}
|
||||
void Flush()
|
||||
{
|
||||
Reg.m_type = REG_NOTLOADED;
|
||||
}
|
||||
};
|
||||
|
||||
struct JRCPPC
|
||||
{
|
||||
u32 PPCReg; // Tied to which PPC Register
|
||||
bool PS1;
|
||||
ARMReg Reg; // Tied to which ARM Register
|
||||
u32 LastLoad;
|
||||
bool Away; // Only used in FPR cache
|
||||
};
|
||||
struct JRCReg
|
||||
{
|
||||
|
@ -55,7 +106,7 @@ struct JRCReg
|
|||
class ArmRegCache
|
||||
{
|
||||
private:
|
||||
PPCCachedReg regs[32];
|
||||
OpArg regs[32];
|
||||
JRCPPC ArmCRegs[ARMREGS];
|
||||
JRCReg ArmRegs[ARMREGS]; // Four registers remaining
|
||||
|
||||
|
@ -64,7 +115,9 @@ private:
|
|||
|
||||
ARMReg *GetAllocationOrder(int &count);
|
||||
ARMReg *GetPPCAllocationOrder(int &count);
|
||||
|
||||
|
||||
u32 GetLeastUsedRegister(bool increment);
|
||||
bool FindFreeRegister(u32 ®index);
|
||||
protected:
|
||||
ARMXEmitter *emit;
|
||||
|
||||
|
@ -74,16 +127,16 @@ public:
|
|||
|
||||
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
|
||||
|
||||
bool IsImm(u32 preg) { return regs[preg].GetType() == REG_IMM; }
|
||||
u32 GetImm(u32 preg) { return regs[preg].GetImm(); }
|
||||
void SetImmediate(u32 preg, u32 imm);
|
||||
ARMReg BindToRegister(u32 preg);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -120,10 +120,12 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
// is full and when saving and loading states.
|
||||
void JitBaseBlockCache::Clear()
|
||||
{
|
||||
#if defined(_DEBUG) || defined(DEBUGFAST)
|
||||
if (IsFull())
|
||||
Core::DisplayMessage("Clearing block cache.", 3000);
|
||||
else
|
||||
Core::DisplayMessage("Clearing code cache.", 3000);
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < num_blocks; i++)
|
||||
{
|
||||
|
|
|
@ -213,7 +213,7 @@ void LogCompiledInstructions()
|
|||
{
|
||||
if (m_allInstructions[i]->compileCount > 0)
|
||||
{
|
||||
fprintf(f.GetHandle(), "%s\t%i\t%i\t%08x\n", m_allInstructions[i]->opname,
|
||||
fprintf(f.GetHandle(), "%s\t%i\t%lld\t%08x\n", m_allInstructions[i]->opname,
|
||||
m_allInstructions[i]->compileCount, m_allInstructions[i]->runCount, m_allInstructions[i]->lastUse);
|
||||
}
|
||||
}
|
||||
|
@ -223,7 +223,7 @@ void LogCompiledInstructions()
|
|||
{
|
||||
if (m_allInstructions[i]->compileCount == 0)
|
||||
{
|
||||
fprintf(f.GetHandle(), "%s\t%i\t%i\n", m_allInstructions[i]->opname,
|
||||
fprintf(f.GetHandle(), "%s\t%i\t%lld\n", m_allInstructions[i]->opname,
|
||||
m_allInstructions[i]->compileCount, m_allInstructions[i]->runCount);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,11 +77,7 @@ struct GekkoOPInfo
|
|||
int type;
|
||||
int flags;
|
||||
int numCyclesMinusOne;
|
||||
#ifdef _M_ARM
|
||||
u64 runCount;
|
||||
#else
|
||||
int runCount;
|
||||
#endif
|
||||
int compileCount;
|
||||
u32 lastUse;
|
||||
};
|
||||
|
|
|
@ -117,17 +117,6 @@ void Init(int cpu_core)
|
|||
{
|
||||
FPURoundMode::SetPrecisionMode(FPURoundMode::PREC_53);
|
||||
|
||||
memset(ppcState.mojs, 0, sizeof(ppcState.mojs));
|
||||
memset(ppcState.sr, 0, sizeof(ppcState.sr));
|
||||
ppcState.DebugCount = 0;
|
||||
ppcState.dtlb_last = 0;
|
||||
ppcState.dtlb_last = 0;
|
||||
memset(ppcState.dtlb_va, 0, sizeof(ppcState.dtlb_va));
|
||||
memset(ppcState.dtlb_pa, 0, sizeof(ppcState.dtlb_pa));
|
||||
ppcState.itlb_last = 0;
|
||||
memset(ppcState.itlb_va, 0, sizeof(ppcState.itlb_va));
|
||||
memset(ppcState.itlb_pa, 0, sizeof(ppcState.itlb_pa));
|
||||
|
||||
memset(ppcState.mojs, 0, sizeof(ppcState.mojs));
|
||||
memset(ppcState.sr, 0, sizeof(ppcState.sr));
|
||||
ppcState.DebugCount = 0;
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace State
|
|||
{
|
||||
|
||||
// number of states
|
||||
static const u32 NUM_STATES = 8;
|
||||
static const u32 NUM_STATES = 10;
|
||||
|
||||
struct StateHeader
|
||||
{
|
||||
|
|
|
@ -170,6 +170,7 @@ if(USE_UPNP)
|
|||
endif()
|
||||
|
||||
if(ANDROID)
|
||||
set(DOLPHIN_EXE main)
|
||||
add_library(${DOLPHIN_EXE} SHARED ${SRCS})
|
||||
target_link_libraries(${DOLPHIN_EXE}
|
||||
log
|
||||
|
@ -181,9 +182,12 @@ if(ANDROID)
|
|||
if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "mips")
|
||||
set (SO_TARGET "mips")
|
||||
endif()
|
||||
if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "^arm")
|
||||
if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "armv7-a")
|
||||
set (SO_TARGET "armeabi-v7a")
|
||||
endif()
|
||||
if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "armv5te")
|
||||
set (SO_TARGET "armeabi")
|
||||
endif()
|
||||
if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "i686")
|
||||
set (SO_TARGET "x86")
|
||||
endif()
|
||||
|
@ -196,6 +200,9 @@ if(ANDROID)
|
|||
add_custom_command(TARGET ${DOLPHIN_EXE} POST_BUILD
|
||||
COMMAND cp ARGS ${CMAKE_SOURCE_DIR}/Data/Sys/GC/* ${CMAKE_SOURCE_DIR}/Source/Android/assets/
|
||||
)
|
||||
add_custom_command(TARGET ${DOLPHIN_EXE} POST_BUILD
|
||||
COMMAND cp ARGS ${CMAKE_SOURCE_DIR}/Data/Sys/Wii/* ${CMAKE_SOURCE_DIR}/Source/Android/assets/
|
||||
)
|
||||
else()
|
||||
add_executable(${DOLPHIN_EXE} ${SRCS})
|
||||
target_link_libraries(${DOLPHIN_EXE} ${LIBS} ${WXLIBS})
|
||||
|
|
|
@ -238,7 +238,6 @@ xcopy "$(SolutionDir)..\Externals\SDL\$(PlatformName)\*.dll" "$(TargetDir)" /e /
|
|||
<ClCompile Include="Src\FrameAui.cpp" />
|
||||
<ClCompile Include="Src\FrameTools.cpp" />
|
||||
<ClCompile Include="Src\GameListCtrl.cpp" />
|
||||
<ClCompile Include="Src\GCMicDlg.cpp" />
|
||||
<ClCompile Include="Src\GeckoCodeDiag.cpp" />
|
||||
<ClCompile Include="Src\HotkeyDlg.cpp" />
|
||||
<ClCompile Include="Src\InputConfigDiag.cpp" />
|
||||
|
@ -301,7 +300,6 @@ xcopy "$(SolutionDir)..\Externals\SDL\$(PlatformName)\*.dll" "$(TargetDir)" /e /
|
|||
<ClInclude Include="Src\FifoPlayerDlg.h" />
|
||||
<ClInclude Include="Src\Frame.h" />
|
||||
<ClInclude Include="Src\GameListCtrl.h" />
|
||||
<ClInclude Include="Src\GCMicDlg.h" />
|
||||
<ClInclude Include="Src\GeckoCodeDiag.h" />
|
||||
<ClInclude Include="Src\Globals.h" />
|
||||
<ClInclude Include="Src\HotkeyDlg.h" />
|
||||
|
|
|
@ -132,9 +132,6 @@
|
|||
<ClCompile Include="Src\TASInputDlg.cpp">
|
||||
<Filter>GUI</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Src\GCMicDlg.cpp">
|
||||
<Filter>GUI</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Src\GLInterface\WGL.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -262,9 +259,6 @@
|
|||
<ClInclude Include="Src\TASInputDlg.h">
|
||||
<Filter>GUI</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Src\GCMicDlg.h">
|
||||
<Filter>GUI</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Src\GLInterface.h" />
|
||||
<ClInclude Include="Src\GLInterface\InterfaceBase.h" />
|
||||
<ClInclude Include="Src\GLInterface\WGL.h" />
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
<key>CFBundleIconFile</key>
|
||||
<string>Dolphin.icns</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.dolphin-emulator.dolphin</string>
|
||||
<string>org.dolphin-emu.dolphin</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleLocalizations</key>
|
||||
|
|
|
@ -213,9 +213,9 @@ EVT_MENU(IDM_UNDOSAVESTATE, CFrame::OnUndoSaveState)
|
|||
EVT_MENU(IDM_LOADSTATEFILE, CFrame::OnLoadStateFromFile)
|
||||
EVT_MENU(IDM_SAVESTATEFILE, CFrame::OnSaveStateToFile)
|
||||
|
||||
EVT_MENU_RANGE(IDM_LOADSLOT1, IDM_LOADSLOT8, CFrame::OnLoadState)
|
||||
EVT_MENU_RANGE(IDM_LOADSLOT1, IDM_LOADSLOT10, CFrame::OnLoadState)
|
||||
EVT_MENU_RANGE(IDM_LOADLAST1, IDM_LOADLAST8, CFrame::OnLoadLastState)
|
||||
EVT_MENU_RANGE(IDM_SAVESLOT1, IDM_SAVESLOT8, CFrame::OnSaveState)
|
||||
EVT_MENU_RANGE(IDM_SAVESLOT1, IDM_SAVESLOT10, CFrame::OnSaveState)
|
||||
EVT_MENU_RANGE(IDM_FRAMESKIP0, IDM_FRAMESKIP9, CFrame::OnFrameSkip)
|
||||
EVT_MENU_RANGE(IDM_DRIVE1, IDM_DRIVE24, CFrame::OnBootDrive)
|
||||
EVT_MENU_RANGE(IDM_CONNECT_WIIMOTE1, IDM_CONNECT_BALANCEBOARD, CFrame::OnConnectWiimote)
|
||||
|
@ -756,6 +756,8 @@ int GetCmdForHotkey(unsigned int key)
|
|||
case HK_LOAD_STATE_SLOT_6: return IDM_LOADSLOT6;
|
||||
case HK_LOAD_STATE_SLOT_7: return IDM_LOADSLOT7;
|
||||
case HK_LOAD_STATE_SLOT_8: return IDM_LOADSLOT8;
|
||||
case HK_LOAD_STATE_SLOT_9: return IDM_LOADSLOT9;
|
||||
case HK_LOAD_STATE_SLOT_10: return IDM_LOADSLOT10;
|
||||
|
||||
case HK_SAVE_STATE_SLOT_1: return IDM_SAVESLOT1;
|
||||
case HK_SAVE_STATE_SLOT_2: return IDM_SAVESLOT2;
|
||||
|
@ -765,6 +767,8 @@ int GetCmdForHotkey(unsigned int key)
|
|||
case HK_SAVE_STATE_SLOT_6: return IDM_SAVESLOT6;
|
||||
case HK_SAVE_STATE_SLOT_7: return IDM_SAVESLOT7;
|
||||
case HK_SAVE_STATE_SLOT_8: return IDM_SAVESLOT8;
|
||||
case HK_SAVE_STATE_SLOT_9: return IDM_SAVESLOT9;
|
||||
case HK_SAVE_STATE_SLOT_10: return IDM_SAVESLOT10;
|
||||
|
||||
case HK_LOAD_LAST_STATE_1: return IDM_LOADLAST1;
|
||||
case HK_LOAD_LAST_STATE_2: return IDM_LOADLAST2;
|
||||
|
@ -846,6 +850,48 @@ void CFrame::OnKeyDown(wxKeyEvent& event)
|
|||
WiimoteId = 3;
|
||||
else if (IsHotkey(event, HK_BALANCEBOARD_CONNECT))
|
||||
WiimoteId = 4;
|
||||
else if (IsHotkey(event, HK_TOGGLE_IR))
|
||||
{
|
||||
OSDChoice = 1;
|
||||
// Toggle native resolution
|
||||
if (++g_Config.iEFBScale > SCALE_4X)
|
||||
g_Config.iEFBScale = SCALE_AUTO;
|
||||
}
|
||||
else if (IsHotkey(event, HK_TOGGLE_AR))
|
||||
{
|
||||
OSDChoice = 2;
|
||||
// Toggle aspect ratio
|
||||
g_Config.iAspectRatio = (g_Config.iAspectRatio + 1) & 3;
|
||||
}
|
||||
else if (IsHotkey(event, HK_TOGGLE_EFBCOPIES))
|
||||
{
|
||||
OSDChoice = 3;
|
||||
// Toggle EFB copy
|
||||
if (!g_Config.bEFBCopyEnable || g_Config.bCopyEFBToTexture)
|
||||
{
|
||||
g_Config.bEFBCopyEnable ^= true;
|
||||
g_Config.bCopyEFBToTexture = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_Config.bCopyEFBToTexture = !g_Config.bCopyEFBToTexture;
|
||||
}
|
||||
}
|
||||
else if (IsHotkey(event, HK_TOGGLE_FOG))
|
||||
{
|
||||
OSDChoice = 4;
|
||||
g_Config.bDisableFog = !g_Config.bDisableFog;
|
||||
}
|
||||
else if (IsHotkey(event, HK_INCREASE_FRAME_LIMIT))
|
||||
{
|
||||
if (++SConfig::GetInstance().m_Framelimit > 0x19)
|
||||
SConfig::GetInstance().m_Framelimit = 0;
|
||||
}
|
||||
else if (IsHotkey(event, HK_DECREASE_FRAME_LIMIT))
|
||||
{
|
||||
if (--SConfig::GetInstance().m_Framelimit > 0x19)
|
||||
SConfig::GetInstance().m_Framelimit = 0x19;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int i = NUM_HOTKEYS;
|
||||
|
@ -891,43 +937,6 @@ void CFrame::OnKeyDown(wxKeyEvent& event)
|
|||
ConnectWiimote(WiimoteId, connect);
|
||||
}
|
||||
|
||||
if (g_Config.bOSDHotKey && event.GetModifiers() == wxMOD_NONE)
|
||||
{
|
||||
switch (event.GetKeyCode())
|
||||
{
|
||||
case '3':
|
||||
OSDChoice = 1;
|
||||
// Toggle native resolution
|
||||
g_Config.iEFBScale = g_Config.iEFBScale + 1;
|
||||
if (g_Config.iEFBScale > 7) g_Config.iEFBScale = 0;
|
||||
break;
|
||||
case '4':
|
||||
OSDChoice = 2;
|
||||
// Toggle aspect ratio
|
||||
g_Config.iAspectRatio = (g_Config.iAspectRatio + 1) & 3;
|
||||
break;
|
||||
case '5':
|
||||
OSDChoice = 3;
|
||||
// Toggle EFB copy
|
||||
if (!g_Config.bEFBCopyEnable || g_Config.bCopyEFBToTexture)
|
||||
{
|
||||
g_Config.bEFBCopyEnable ^= true;
|
||||
g_Config.bCopyEFBToTexture = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_Config.bCopyEFBToTexture = !g_Config.bCopyEFBToTexture;
|
||||
}
|
||||
break;
|
||||
case '6':
|
||||
OSDChoice = 4;
|
||||
g_Config.bDisableFog = !g_Config.bDisableFog;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_Config.bFreeLook && event.GetModifiers() == wxMOD_SHIFT)
|
||||
{
|
||||
static float debugSpeed = 1.0f;
|
||||
|
|
|
@ -382,6 +382,8 @@ wxString CFrame::GetMenuLabel(int Id)
|
|||
case HK_LOAD_STATE_SLOT_6:
|
||||
case HK_LOAD_STATE_SLOT_7:
|
||||
case HK_LOAD_STATE_SLOT_8:
|
||||
case HK_LOAD_STATE_SLOT_9:
|
||||
case HK_LOAD_STATE_SLOT_10:
|
||||
Label = wxString::Format(_("Slot %i"),
|
||||
Id - HK_LOAD_STATE_SLOT_1 + 1);
|
||||
break;
|
||||
|
@ -394,6 +396,8 @@ wxString CFrame::GetMenuLabel(int Id)
|
|||
case HK_SAVE_STATE_SLOT_6:
|
||||
case HK_SAVE_STATE_SLOT_7:
|
||||
case HK_SAVE_STATE_SLOT_8:
|
||||
case HK_SAVE_STATE_SLOT_9:
|
||||
case HK_SAVE_STATE_SLOT_10:
|
||||
Label = wxString::Format(_("Slot %i"),
|
||||
Id - HK_SAVE_STATE_SLOT_1 + 1);
|
||||
break;
|
||||
|
@ -1006,6 +1010,8 @@ void CFrame::DoPause()
|
|||
// Stop the emulation
|
||||
void CFrame::DoStop()
|
||||
{
|
||||
if (!Core::IsRunningAndStarted())
|
||||
return;
|
||||
if (confirmStop)
|
||||
return;
|
||||
|
||||
|
@ -1045,6 +1051,7 @@ void CFrame::DoStop()
|
|||
DoRecordingSave();
|
||||
if(Movie::IsPlayingInput() || Movie::IsRecordingInput())
|
||||
Movie::EndPlayInput(false);
|
||||
NetPlay::StopGame();
|
||||
|
||||
wxBeginBusyCursor();
|
||||
BootManager::Stop();
|
||||
|
@ -1581,7 +1588,11 @@ void CFrame::UpdateGUI()
|
|||
|
||||
// Update Menu Accelerators
|
||||
for (unsigned int i = 0; i < NUM_HOTKEYS; i++)
|
||||
{
|
||||
if (GetCmdForHotkey(i) == -1)
|
||||
continue;
|
||||
GetMenuBar()->FindItem(GetCmdForHotkey(i))->SetItemLabel(GetMenuLabel(i));
|
||||
}
|
||||
|
||||
GetMenuBar()->FindItem(IDM_LOADSTATE)->Enable(Initialized);
|
||||
GetMenuBar()->FindItem(IDM_SAVESTATE)->Enable(Initialized);
|
||||
|
|
|
@ -1,274 +0,0 @@
|
|||
// Copyright 2013 Dolphin Emulator Project
|
||||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <wx/notebook.h>
|
||||
|
||||
#include "GCMicDlg.h"
|
||||
#include "ConfigManager.h"
|
||||
|
||||
BEGIN_EVENT_TABLE(GCMicDialog,wxDialog)
|
||||
EVT_COMMAND_RANGE(0, NUM_HOTKEYS - 1,
|
||||
wxEVT_COMMAND_BUTTON_CLICKED, GCMicDialog::OnButtonClick)
|
||||
EVT_TIMER(wxID_ANY, GCMicDialog::OnButtonTimer)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
GCMicDialog::GCMicDialog(wxWindow *parent, wxWindowID id, const wxString &title,
|
||||
const wxPoint &position, const wxSize& size, long style)
|
||||
: wxDialog(parent, id, title, position, size, style)
|
||||
{
|
||||
CreateHotkeyGUIControls();
|
||||
|
||||
#if wxUSE_TIMER
|
||||
m_ButtonMappingTimer = new wxTimer(this, wxID_ANY);
|
||||
g_Pressed = 0;
|
||||
g_Modkey = 0;
|
||||
ClickedButton = NULL;
|
||||
GetButtonWaitingID = 0;
|
||||
GetButtonWaitingTimer = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
GCMicDialog::~GCMicDialog()
|
||||
{
|
||||
delete m_ButtonMappingTimer;
|
||||
}
|
||||
|
||||
// Save keyboard key mapping
|
||||
void GCMicDialog::SaveButtonMapping(int Id, int Key, int Modkey)
|
||||
{
|
||||
SConfig::GetInstance().m_LocalCoreStartupParameter.iHotkey[Id] = Key;
|
||||
SConfig::GetInstance().m_LocalCoreStartupParameter.iHotkeyModifier[Id] = Modkey;
|
||||
}
|
||||
|
||||
void GCMicDialog::EndGetButtons(void)
|
||||
{
|
||||
wxTheApp->Unbind(wxEVT_KEY_DOWN, &GCMicDialog::OnKeyDown, this);
|
||||
m_ButtonMappingTimer->Stop();
|
||||
GetButtonWaitingTimer = 0;
|
||||
GetButtonWaitingID = 0;
|
||||
ClickedButton = NULL;
|
||||
SetEscapeId(wxID_ANY);
|
||||
}
|
||||
|
||||
void GCMicDialog::OnKeyDown(wxKeyEvent& event)
|
||||
{
|
||||
if(ClickedButton != NULL)
|
||||
{
|
||||
// Save the key
|
||||
g_Pressed = event.GetKeyCode();
|
||||
g_Modkey = event.GetModifiers();
|
||||
|
||||
// Don't allow modifier keys
|
||||
if (g_Pressed == WXK_CONTROL || g_Pressed == WXK_ALT ||
|
||||
g_Pressed == WXK_SHIFT || g_Pressed == WXK_COMMAND)
|
||||
return;
|
||||
|
||||
// Use the space key to set a blank key
|
||||
if (g_Pressed == WXK_SPACE)
|
||||
{
|
||||
SaveButtonMapping(ClickedButton->GetId(), -1, 0);
|
||||
SetButtonText(ClickedButton->GetId(), wxString());
|
||||
}
|
||||
else
|
||||
{
|
||||
SetButtonText(ClickedButton->GetId(),
|
||||
InputCommon::WXKeyToString(g_Pressed),
|
||||
InputCommon::WXKeymodToString(g_Modkey));
|
||||
SaveButtonMapping(ClickedButton->GetId(), g_Pressed, g_Modkey);
|
||||
}
|
||||
EndGetButtons();
|
||||
}
|
||||
}
|
||||
|
||||
// Update the textbox for the buttons
|
||||
void GCMicDialog::SetButtonText(int id, const wxString &keystr, const wxString &modkeystr)
|
||||
{
|
||||
m_Button_Hotkeys[id]->SetLabel(modkeystr + keystr);
|
||||
}
|
||||
|
||||
void GCMicDialog::DoGetButtons(int _GetId)
|
||||
{
|
||||
// Values used in this function
|
||||
const int Seconds = 4; // Seconds to wait for
|
||||
const int TimesPerSecond = 40; // How often to run the check
|
||||
|
||||
// If the Id has changed or the timer is not running we should start one
|
||||
if( GetButtonWaitingID != _GetId || !m_ButtonMappingTimer->IsRunning() )
|
||||
{
|
||||
if(m_ButtonMappingTimer->IsRunning())
|
||||
m_ButtonMappingTimer->Stop();
|
||||
|
||||
// Save the button Id
|
||||
GetButtonWaitingID = _GetId;
|
||||
GetButtonWaitingTimer = 0;
|
||||
|
||||
// Start the timer
|
||||
#if wxUSE_TIMER
|
||||
m_ButtonMappingTimer->Start(1000 / TimesPerSecond);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Process results
|
||||
// Count each time
|
||||
GetButtonWaitingTimer++;
|
||||
|
||||
// This is run every second
|
||||
if (GetButtonWaitingTimer % TimesPerSecond == 0)
|
||||
{
|
||||
// Current time
|
||||
int TmpTime = Seconds - (GetButtonWaitingTimer / TimesPerSecond);
|
||||
// Update text
|
||||
SetButtonText(_GetId, wxString::Format(wxT("[ %d ]"), TmpTime));
|
||||
}
|
||||
|
||||
// Time's up
|
||||
if (GetButtonWaitingTimer / TimesPerSecond >= Seconds)
|
||||
{
|
||||
// Revert back to old label
|
||||
SetButtonText(_GetId, OldLabel);
|
||||
EndGetButtons();
|
||||
}
|
||||
}
|
||||
|
||||
// Input button clicked
|
||||
void GCMicDialog::OnButtonClick(wxCommandEvent& event)
|
||||
{
|
||||
event.Skip();
|
||||
|
||||
if (m_ButtonMappingTimer->IsRunning())
|
||||
return;
|
||||
|
||||
wxTheApp->Bind(wxEVT_KEY_DOWN, &GCMicDialog::OnKeyDown, this);
|
||||
|
||||
// Get the button
|
||||
ClickedButton = (wxButton *)event.GetEventObject();
|
||||
SetEscapeId(wxID_CANCEL);
|
||||
// Save old label so we can revert back
|
||||
OldLabel = ClickedButton->GetLabel();
|
||||
ClickedButton->SetWindowStyle(wxWANTS_CHARS);
|
||||
ClickedButton->SetLabel(_("<Press Key>"));
|
||||
DoGetButtons(ClickedButton->GetId());
|
||||
}
|
||||
|
||||
#define HOTKEY_NUM_COLUMNS 2
|
||||
|
||||
void GCMicDialog::CreateHotkeyGUIControls(void)
|
||||
{
|
||||
const wxString pageNames[] =
|
||||
{
|
||||
_("General"),
|
||||
_("State Saves")
|
||||
};
|
||||
|
||||
const wxString hkText[] =
|
||||
{
|
||||
_("Open"),
|
||||
_("Change Disc"),
|
||||
_("Refresh List"),
|
||||
|
||||
_("Play/Pause"),
|
||||
_("Stop"),
|
||||
_("Reset"),
|
||||
_("Frame Advance"),
|
||||
|
||||
_("Start Recording"),
|
||||
_("Play Recording"),
|
||||
_("Export Recording"),
|
||||
_("Read-only mode"),
|
||||
|
||||
_("Toggle Fullscreen"),
|
||||
_("Take Screenshot"),
|
||||
|
||||
_("Connect Wiimote 1"),
|
||||
_("Connect Wiimote 2"),
|
||||
_("Connect Wiimote 3"),
|
||||
_("Connect Wiimote 4"),
|
||||
|
||||
_("Load State Slot 1"),
|
||||
_("Load State Slot 2"),
|
||||
_("Load State Slot 3"),
|
||||
_("Load State Slot 4"),
|
||||
_("Load State Slot 5"),
|
||||
_("Load State Slot 6"),
|
||||
_("Load State Slot 7"),
|
||||
_("Load State Slot 8"),
|
||||
|
||||
_("Save State Slot 1"),
|
||||
_("Save State Slot 2"),
|
||||
_("Save State Slot 3"),
|
||||
_("Save State Slot 4"),
|
||||
_("Save State Slot 5"),
|
||||
_("Save State Slot 6"),
|
||||
_("Save State Slot 7"),
|
||||
_("Save State Slot 8")
|
||||
};
|
||||
|
||||
const int page_breaks[3] = {HK_OPEN, HK_LOAD_STATE_SLOT_1, NUM_HOTKEYS};
|
||||
|
||||
// Configuration controls sizes
|
||||
wxSize size(100,20);
|
||||
// A small type font
|
||||
wxFont m_SmallFont(7, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
|
||||
|
||||
wxNotebook *Notebook = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize);
|
||||
|
||||
for (int j = 0; j < 2; j++)
|
||||
{
|
||||
wxPanel *Page = new wxPanel(Notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize);
|
||||
Notebook->AddPage(Page, pageNames[j]);
|
||||
|
||||
wxGridBagSizer *sHotkeys = new wxGridBagSizer();
|
||||
|
||||
// Header line
|
||||
for (int i = 0; i < HOTKEY_NUM_COLUMNS; i++)
|
||||
{
|
||||
wxBoxSizer *HeaderSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
wxStaticText *StaticTextHeader = new wxStaticText(Page, wxID_ANY, _("Action"));
|
||||
HeaderSizer->Add(StaticTextHeader, 1, wxALL, 2);
|
||||
StaticTextHeader = new wxStaticText(Page, wxID_ANY, _("Key"), wxDefaultPosition, size);
|
||||
HeaderSizer->Add(StaticTextHeader, 0, wxALL, 2);
|
||||
sHotkeys->Add(HeaderSizer, wxGBPosition(0, i), wxDefaultSpan, wxEXPAND | wxLEFT, (i > 0) ? 30 : 1);
|
||||
}
|
||||
|
||||
int column_break = (page_breaks[j+1] + page_breaks[j] + 1) / 2;
|
||||
|
||||
for (int i = page_breaks[j]; i < page_breaks[j+1]; i++)
|
||||
{
|
||||
// Text for the action
|
||||
wxStaticText *stHotkeys = new wxStaticText(Page, wxID_ANY, hkText[i]);
|
||||
|
||||
// Key selection button
|
||||
m_Button_Hotkeys[i] = new wxButton(Page, i, wxEmptyString,
|
||||
wxDefaultPosition, size);
|
||||
m_Button_Hotkeys[i]->SetFont(m_SmallFont);
|
||||
m_Button_Hotkeys[i]->SetToolTip(_("Left click to detect hotkeys.\nEnter space to clear."));
|
||||
SetButtonText(i,
|
||||
InputCommon::WXKeyToString(SConfig::GetInstance().m_LocalCoreStartupParameter.iHotkey[i]),
|
||||
InputCommon::WXKeymodToString(
|
||||
SConfig::GetInstance().m_LocalCoreStartupParameter.iHotkeyModifier[i]));
|
||||
|
||||
wxBoxSizer *sHotkey = new wxBoxSizer(wxHORIZONTAL);
|
||||
sHotkey->Add(stHotkeys, 1, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 2);
|
||||
sHotkey->Add(m_Button_Hotkeys[i], 0, wxALL, 2);
|
||||
sHotkeys->Add(sHotkey,
|
||||
wxGBPosition((i < column_break) ? i - page_breaks[j] + 1 : i - column_break + 1,
|
||||
(i < column_break) ? 0 : 1),
|
||||
wxDefaultSpan, wxEXPAND | wxLEFT, (i < column_break) ? 1 : 30);
|
||||
}
|
||||
|
||||
wxStaticBoxSizer *sHotkeyBox = new wxStaticBoxSizer(wxVERTICAL, Page, _("Hotkeys"));
|
||||
sHotkeyBox->Add(sHotkeys);
|
||||
|
||||
wxBoxSizer* const sPage = new wxBoxSizer(wxVERTICAL);
|
||||
sPage->Add(sHotkeyBox, 0, wxEXPAND | wxALL, 5);
|
||||
Page->SetSizer(sPage);
|
||||
}
|
||||
|
||||
wxBoxSizer *sMainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
sMainSizer->Add(Notebook, 0, wxEXPAND | wxALL, 5);
|
||||
sMainSizer->Add(CreateButtonSizer(wxOK), 0, wxEXPAND | wxLEFT | wxRIGHT | wxDOWN, 5);
|
||||
SetSizerAndFit(sMainSizer);
|
||||
SetFocus();
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
// Copyright 2013 Dolphin Emulator Project
|
||||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#ifndef __GCMICDIALOG_h__
|
||||
#define __GCMICDIALOG_h__
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/textctrl.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/combobox.h>
|
||||
#include <wx/checkbox.h>
|
||||
#include <wx/gbsizer.h>
|
||||
|
||||
#include "Common.h"
|
||||
#include "CoreParameter.h"
|
||||
#include "WXInputBase.h"
|
||||
|
||||
#if defined(HAVE_X11) && HAVE_X11
|
||||
#include "X11InputBase.h"
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/keysym.h>
|
||||
#endif
|
||||
|
||||
class GCMicDialog : public wxDialog
|
||||
{
|
||||
public:
|
||||
GCMicDialog(wxWindow *parent,
|
||||
wxWindowID id = 1,
|
||||
const wxString &title = _("GCMic Configuration"),
|
||||
const wxPoint& pos = wxDefaultPosition,
|
||||
const wxSize& size = wxDefaultSize,
|
||||
long style = wxDEFAULT_DIALOG_STYLE);
|
||||
virtual ~GCMicDialog();
|
||||
|
||||
private:
|
||||
DECLARE_EVENT_TABLE();
|
||||
|
||||
wxString OldLabel;
|
||||
|
||||
wxButton *ClickedButton,
|
||||
*m_Button_Hotkeys[NUM_HOTKEYS];
|
||||
|
||||
wxTimer *m_ButtonMappingTimer;
|
||||
|
||||
void OnButtonTimer(wxTimerEvent& WXUNUSED(event)) { DoGetButtons(GetButtonWaitingID); }
|
||||
void OnButtonClick(wxCommandEvent& event);
|
||||
void OnKeyDown(wxKeyEvent& event);
|
||||
void SaveButtonMapping(int Id, int Key, int Modkey);
|
||||
void CreateHotkeyGUIControls(void);
|
||||
|
||||
void SetButtonText(int id, const wxString &keystr, const wxString &modkeystr = wxString());
|
||||
|
||||
void DoGetButtons(int id);
|
||||
void EndGetButtons(void);
|
||||
|
||||
int GetButtonWaitingID, GetButtonWaitingTimer, g_Pressed, g_Modkey;
|
||||
};
|
||||
#endif
|
||||
|
|
@ -132,17 +132,6 @@ bool cInterfaceEGL::Create(void *&window_handle)
|
|||
exit(1);
|
||||
}
|
||||
|
||||
if (!eglMakeCurrent(GLWin.egl_dpy, GLWin.egl_surf, GLWin.egl_surf, GLWin.egl_ctx)) {
|
||||
|
||||
INFO_LOG(VIDEO, "Error: eglMakeCurrent() failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO_LOG(VIDEO, "GL_VENDOR: %s\n", glGetString(GL_VENDOR));
|
||||
INFO_LOG(VIDEO, "GL_RENDERER: %s\n", glGetString(GL_RENDERER));
|
||||
INFO_LOG(VIDEO, "GL_VERSION: %s\n", glGetString(GL_VERSION));
|
||||
INFO_LOG(VIDEO, "GL_EXTENSIONS: %s\n", glGetString(GL_EXTENSIONS));
|
||||
|
||||
Platform.ToggleFullscreen(SConfig::GetInstance().m_LocalCoreStartupParameter.bFullscreen);
|
||||
|
||||
window_handle = (void *)GLWin.native_window;
|
||||
|
|
|
@ -38,6 +38,8 @@ enum
|
|||
IDM_SAVESLOT6,
|
||||
IDM_SAVESLOT7,
|
||||
IDM_SAVESLOT8,
|
||||
IDM_SAVESLOT9,
|
||||
IDM_SAVESLOT10,
|
||||
IDM_LOADSLOT1,
|
||||
IDM_LOADSLOT2,
|
||||
IDM_LOADSLOT3,
|
||||
|
@ -46,6 +48,8 @@ enum
|
|||
IDM_LOADSLOT6,
|
||||
IDM_LOADSLOT7,
|
||||
IDM_LOADSLOT8,
|
||||
IDM_LOADSLOT9,
|
||||
IDM_LOADSLOT10,
|
||||
IDM_LOADLAST1,
|
||||
IDM_LOADLAST2,
|
||||
IDM_LOADLAST3,
|
||||
|
|
|
@ -187,6 +187,13 @@ void HotkeyConfigDialog::CreateHotkeyGUIControls(void)
|
|||
_("Connect Wiimote 4"),
|
||||
_("Connect Balance Board"),
|
||||
|
||||
_("Toggle IR"),
|
||||
_("Toggle Aspect Ratio"),
|
||||
_("Toggle EFB Copies"),
|
||||
_("Toggle Fog"),
|
||||
_("Increase Frame limit"),
|
||||
_("Decrease Frame limit"),
|
||||
|
||||
_("Load State Slot 1"),
|
||||
_("Load State Slot 2"),
|
||||
_("Load State Slot 3"),
|
||||
|
@ -195,6 +202,8 @@ void HotkeyConfigDialog::CreateHotkeyGUIControls(void)
|
|||
_("Load State Slot 6"),
|
||||
_("Load State Slot 7"),
|
||||
_("Load State Slot 8"),
|
||||
_("Load State Slot 9"),
|
||||
_("Load State Slot 10"),
|
||||
|
||||
_("Save State Slot 1"),
|
||||
_("Save State Slot 2"),
|
||||
|
@ -204,6 +213,8 @@ void HotkeyConfigDialog::CreateHotkeyGUIControls(void)
|
|||
_("Save State Slot 6"),
|
||||
_("Save State Slot 7"),
|
||||
_("Save State Slot 8"),
|
||||
_("Save State Slot 9"),
|
||||
_("Save State Slot 10"),
|
||||
|
||||
_("Load State Last 1"),
|
||||
_("Load State Last 2"),
|
||||
|
|
|
@ -263,6 +263,9 @@ bool DolphinApp::OnInit()
|
|||
File::CreateFullPath(File::GetUserPath(D_SCREENSHOTS_IDX));
|
||||
File::CreateFullPath(File::GetUserPath(D_STATESAVES_IDX));
|
||||
File::CreateFullPath(File::GetUserPath(D_MAILLOGS_IDX));
|
||||
File::CreateFullPath(File::GetUserPath(D_GCUSER_IDX) + USA_DIR DIR_SEP);
|
||||
File::CreateFullPath(File::GetUserPath(D_GCUSER_IDX) + EUR_DIR DIR_SEP);
|
||||
File::CreateFullPath(File::GetUserPath(D_GCUSER_IDX) + JAP_DIR DIR_SEP);
|
||||
|
||||
LogManager::Init();
|
||||
SConfig::Init();
|
||||
|
|
|
@ -47,6 +47,9 @@
|
|||
#include <android/native_window_jni.h>
|
||||
ANativeWindow* surf;
|
||||
int g_width, g_height;
|
||||
std::string g_filename;
|
||||
static std::thread g_run_thread;
|
||||
|
||||
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "Dolphinemu", __VA_ARGS__))
|
||||
|
||||
void Host_NotifyMapLoaded() {}
|
||||
|
@ -66,7 +69,10 @@ void* Host_GetRenderHandle()
|
|||
|
||||
void* Host_GetInstance() { return NULL; }
|
||||
|
||||
void Host_UpdateTitle(const char* title){};
|
||||
void Host_UpdateTitle(const char* title)
|
||||
{
|
||||
LOGI(title);
|
||||
};
|
||||
|
||||
void Host_UpdateLogDisplay(){}
|
||||
|
||||
|
@ -297,12 +303,23 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetConfig(JN
|
|||
env->ReleaseStringUTFChars(jDefault, Default);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run(JNIEnv *env, jobject obj, jstring jFile, jobject _surf, jint _width, jint _height)
|
||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetFilename(JNIEnv *env, jobject obj, jstring jFile)
|
||||
{
|
||||
const char *File = env->GetStringUTFChars(jFile, NULL);
|
||||
|
||||
g_filename = std::string(File);
|
||||
|
||||
env->ReleaseStringUTFChars(jFile, File);
|
||||
}
|
||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetDimensions(JNIEnv *env, jobject obj, jint _width, jint _height)
|
||||
{
|
||||
surf = ANativeWindow_fromSurface(env, _surf);
|
||||
g_width = (int)_width;
|
||||
g_height = (int)_height;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run(JNIEnv *env, jobject obj, jobject _surf)
|
||||
{
|
||||
surf = ANativeWindow_fromSurface(env, _surf);
|
||||
// Install our callbacks
|
||||
OSD::AddCallback(OSD::OSD_INIT, OSDCallbacks, 0);
|
||||
OSD::AddCallback(OSD::OSD_SHUTDOWN, OSDCallbacks, 2);
|
||||
|
@ -322,9 +339,8 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run(JNIEnv *
|
|||
if (onscreencontrols)
|
||||
OSD::AddCallback(OSD::OSD_ONFRAME, OSDCallbacks, 1);
|
||||
|
||||
const char *File = env->GetStringUTFChars(jFile, NULL);
|
||||
// No use running the loop when booting fails
|
||||
if ( BootManager::BootCore( File ) )
|
||||
if ( BootManager::BootCore( g_filename.c_str() ) )
|
||||
while (PowerPC::GetState() != PowerPC::CPU_POWERDOWN)
|
||||
updateMainFrameEvent.Wait();
|
||||
|
||||
|
@ -333,7 +349,8 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run(JNIEnv *
|
|||
SConfig::Shutdown();
|
||||
LogManager::Shutdown();
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -6,21 +6,25 @@
|
|||
#include <IniFile.h>
|
||||
|
||||
#include "WxUtils.h"
|
||||
#include "NetPlay.h"
|
||||
#include "NetPlayClient.h"
|
||||
#include "NetPlayServer.h"
|
||||
#include "NetWindow.h"
|
||||
#include "Frame.h"
|
||||
#include "Core.h"
|
||||
#include "ConfigManager.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#define NETPLAY_TITLEBAR "Dolphin NetPlay"
|
||||
#define INITIAL_PAD_BUFFER_SIZE 20
|
||||
|
||||
BEGIN_EVENT_TABLE(NetPlayDiag, wxFrame)
|
||||
EVT_COMMAND(wxID_ANY, wxEVT_THREAD, NetPlayDiag::OnThread)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
static NetPlay* netplay_ptr = NULL;
|
||||
static NetPlayServer* netplay_server = NULL;
|
||||
static NetPlayClient* netplay_client = NULL;
|
||||
extern CFrame* main_frame;
|
||||
NetPlayDiag *NetPlayDiag::npd = NULL;
|
||||
|
||||
|
@ -200,6 +204,28 @@ NetPlaySetupDiag::~NetPlaySetupDiag()
|
|||
main_frame->g_NetPlaySetupDiag = NULL;
|
||||
}
|
||||
|
||||
void NetPlaySetupDiag::MakeNetPlayDiag(int port, const std::string &game, bool is_hosting)
|
||||
{
|
||||
NetPlayDiag *&npd = NetPlayDiag::GetInstance();
|
||||
std::string ip;
|
||||
npd = new NetPlayDiag(m_parent, m_game_list, game, is_hosting);
|
||||
if (is_hosting)
|
||||
ip = "127.0.0.1";
|
||||
else
|
||||
ip = WxStrToStr(m_connect_ip_text->GetValue());
|
||||
|
||||
netplay_client = new NetPlayClient(ip, (u16)port, npd, WxStrToStr(m_nickname_text->GetValue()));
|
||||
if (netplay_client->is_connected)
|
||||
{
|
||||
npd->Show();
|
||||
Destroy();
|
||||
}
|
||||
else
|
||||
{
|
||||
npd->Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void NetPlaySetupDiag::OnHost(wxCommandEvent&)
|
||||
{
|
||||
NetPlayDiag *&npd = NetPlayDiag::GetInstance();
|
||||
|
@ -217,25 +243,20 @@ void NetPlaySetupDiag::OnHost(wxCommandEvent&)
|
|||
|
||||
std::string game(WxStrToStr(m_game_lbox->GetStringSelection()));
|
||||
|
||||
npd = new NetPlayDiag(m_parent, m_game_list, game, true);
|
||||
unsigned long port = 0;
|
||||
m_host_port_text->GetValue().ToULong(&port);
|
||||
netplay_ptr = new NetPlayServer(u16(port), WxStrToStr(m_nickname_text->GetValue()), npd);
|
||||
netplay_ptr->ChangeGame(game);
|
||||
if (netplay_ptr->is_connected)
|
||||
netplay_server = new NetPlayServer(u16(port));
|
||||
netplay_server->ChangeGame(game);
|
||||
netplay_server->AdjustPadBufferSize(INITIAL_PAD_BUFFER_SIZE);
|
||||
if (netplay_server->is_connected)
|
||||
{
|
||||
#ifdef USE_UPNP
|
||||
if(m_upnp_chk->GetValue())
|
||||
((NetPlayServer*)netplay_ptr)->TryPortmapping(port);
|
||||
netplay_server->TryPortmapping(port);
|
||||
#endif
|
||||
npd->Show();
|
||||
Destroy();
|
||||
}
|
||||
else
|
||||
{
|
||||
PanicAlertT("Failed to Listen!!");
|
||||
npd->Destroy();
|
||||
}
|
||||
|
||||
MakeNetPlayDiag(port, game, true);
|
||||
}
|
||||
|
||||
void NetPlaySetupDiag::OnJoin(wxCommandEvent&)
|
||||
|
@ -247,20 +268,9 @@ void NetPlaySetupDiag::OnJoin(wxCommandEvent&)
|
|||
return;
|
||||
}
|
||||
|
||||
npd = new NetPlayDiag(m_parent, m_game_list, "");
|
||||
unsigned long port = 0;
|
||||
m_connect_port_text->GetValue().ToULong(&port);
|
||||
netplay_ptr = new NetPlayClient(WxStrToStr(m_connect_ip_text->GetValue())
|
||||
, (u16)port, npd, WxStrToStr(m_nickname_text->GetValue()));
|
||||
if (netplay_ptr->is_connected)
|
||||
{
|
||||
npd->Show();
|
||||
Destroy();
|
||||
}
|
||||
else
|
||||
{
|
||||
npd->Destroy();
|
||||
}
|
||||
MakeNetPlayDiag(port, "", false);
|
||||
}
|
||||
|
||||
void NetPlaySetupDiag::OnQuit(wxCommandEvent&)
|
||||
|
@ -272,6 +282,7 @@ NetPlayDiag::NetPlayDiag(wxWindow* const parent, const CGameListCtrl* const game
|
|||
const std::string& game, const bool is_hosting)
|
||||
: wxFrame(parent, wxID_ANY, wxT(NETPLAY_TITLEBAR), wxDefaultPosition, wxDefaultSize)
|
||||
, m_selected_game(game)
|
||||
, m_start_btn(NULL)
|
||||
, m_game_list(game_list)
|
||||
{
|
||||
wxPanel* const panel = new wxPanel(this);
|
||||
|
@ -330,22 +341,19 @@ NetPlayDiag::NetPlayDiag(wxWindow* const parent, const CGameListCtrl* const game
|
|||
wxBoxSizer* const bottom_szr = new wxBoxSizer(wxHORIZONTAL);
|
||||
if (is_hosting)
|
||||
{
|
||||
wxButton* const start_btn = new wxButton(panel, wxID_ANY, _("Start"));
|
||||
start_btn->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &NetPlayDiag::OnStart, this);
|
||||
bottom_szr->Add(start_btn);
|
||||
wxButton* const stop_btn = new wxButton(panel, wxID_ANY, _("Stop"));
|
||||
stop_btn->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &NetPlayDiag::OnStop, this);
|
||||
bottom_szr->Add(stop_btn);
|
||||
m_start_btn = new wxButton(panel, wxID_ANY, _("Start"));
|
||||
m_start_btn->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &NetPlayDiag::OnStart, this);
|
||||
bottom_szr->Add(m_start_btn);
|
||||
|
||||
bottom_szr->Add(new wxStaticText(panel, wxID_ANY, _("Buffer:")), 0, wxLEFT | wxCENTER, 5 );
|
||||
wxSpinCtrl* const padbuf_spin = new wxSpinCtrl(panel, wxID_ANY, wxT("20")
|
||||
, wxDefaultPosition, wxSize(64, -1), wxSP_ARROW_KEYS, 0, 200, 20);
|
||||
, wxDefaultPosition, wxSize(64, -1), wxSP_ARROW_KEYS, 0, 200, INITIAL_PAD_BUFFER_SIZE);
|
||||
padbuf_spin->Bind(wxEVT_COMMAND_SPINCTRL_UPDATED, &NetPlayDiag::OnAdjustBuffer, this);
|
||||
bottom_szr->Add(padbuf_spin, 0, wxCENTER);
|
||||
}
|
||||
|
||||
m_memcard_write = new wxCheckBox(panel, wxID_ANY, _("Write memcards (GC)"));
|
||||
m_memcard_write->Bind(wxEVT_COMMAND_CHECKBOX_CLICKED, &NetPlayDiag::OnMemcardWriteCheck, this);
|
||||
bottom_szr->Add(m_memcard_write, 0, wxCENTER);
|
||||
m_memcard_write = new wxCheckBox(panel, wxID_ANY, _("Write memcards (GC)"));
|
||||
bottom_szr->Add(m_memcard_write, 0, wxCENTER);
|
||||
}
|
||||
|
||||
bottom_szr->AddStretchSpacer(1);
|
||||
bottom_szr->Add(quit_btn);
|
||||
|
@ -366,10 +374,15 @@ NetPlayDiag::NetPlayDiag(wxWindow* const parent, const CGameListCtrl* const game
|
|||
|
||||
NetPlayDiag::~NetPlayDiag()
|
||||
{
|
||||
if (netplay_ptr)
|
||||
if (netplay_client)
|
||||
{
|
||||
delete netplay_ptr;
|
||||
netplay_ptr = NULL;
|
||||
delete netplay_client;
|
||||
netplay_client = NULL;
|
||||
}
|
||||
if (netplay_server)
|
||||
{
|
||||
delete netplay_server;
|
||||
netplay_server = NULL;
|
||||
}
|
||||
npd = NULL;
|
||||
}
|
||||
|
@ -380,37 +393,45 @@ void NetPlayDiag::OnChat(wxCommandEvent&)
|
|||
|
||||
if (s.Length())
|
||||
{
|
||||
netplay_ptr->SendChatMessage(WxStrToStr(s));
|
||||
netplay_client->SendChatMessage(WxStrToStr(s));
|
||||
m_chat_text->AppendText(s.Prepend(wxT(" >> ")).Append(wxT('\n')));
|
||||
m_chat_msg_text->Clear();
|
||||
}
|
||||
}
|
||||
|
||||
void NetPlayDiag::OnStart(wxCommandEvent&)
|
||||
void NetPlayDiag::GetNetSettings(NetSettings &settings)
|
||||
{
|
||||
SConfig &instance = SConfig::GetInstance();
|
||||
settings.m_DSPHLE = instance.m_LocalCoreStartupParameter.bDSPHLE;
|
||||
settings.m_DSPEnableJIT = instance.m_EnableJIT;
|
||||
settings.m_WriteToMemcard = m_memcard_write->GetValue();
|
||||
|
||||
for (unsigned int i = 0; i < 4; ++i)
|
||||
settings.m_Controllers[i] = SConfig::GetInstance().m_SIDevice[i];
|
||||
}
|
||||
|
||||
std::string NetPlayDiag::FindGame()
|
||||
{
|
||||
// find path for selected game, sloppy..
|
||||
for (u32 i = 0 ; auto game = m_game_list->GetISO(i); ++i)
|
||||
{
|
||||
if (m_selected_game == BuildGameName(*game))
|
||||
{
|
||||
netplay_ptr->StartGame(game->GetFileName());
|
||||
return;
|
||||
}
|
||||
}
|
||||
return game->GetFileName();
|
||||
|
||||
PanicAlertT("Game not found!");
|
||||
return "";
|
||||
}
|
||||
|
||||
void NetPlayDiag::OnStop(wxCommandEvent&)
|
||||
void NetPlayDiag::OnStart(wxCommandEvent&)
|
||||
{
|
||||
netplay_ptr->StopGame();
|
||||
NetSettings settings;
|
||||
GetNetSettings(settings);
|
||||
netplay_server->SetNetSettings(settings);
|
||||
netplay_server->StartGame(FindGame());
|
||||
}
|
||||
|
||||
void NetPlayDiag::BootGame(const std::string& filename)
|
||||
{
|
||||
main_frame->BootGame(filename);
|
||||
|
||||
Core::g_CoreStartupParameter.bEnableMemcardSaving = m_memcard_write->GetValue();
|
||||
}
|
||||
|
||||
void NetPlayDiag::StopGame()
|
||||
|
@ -444,27 +465,26 @@ void NetPlayDiag::OnMsgStartGame()
|
|||
{
|
||||
wxCommandEvent evt(wxEVT_THREAD, NP_GUI_EVT_START_GAME);
|
||||
GetEventHandler()->AddPendingEvent(evt);
|
||||
if (m_start_btn)
|
||||
m_start_btn->Disable();
|
||||
}
|
||||
|
||||
void NetPlayDiag::OnMsgStopGame()
|
||||
{
|
||||
wxCommandEvent evt(wxEVT_THREAD, NP_GUI_EVT_STOP_GAME);
|
||||
GetEventHandler()->AddPendingEvent(evt);
|
||||
}
|
||||
|
||||
void NetPlayDiag::OnMemcardWriteCheck(wxCommandEvent &event)
|
||||
{
|
||||
netplay_ptr->SetMemcardWriteEnabled(m_memcard_write->GetValue());
|
||||
if (m_start_btn)
|
||||
m_start_btn->Enable();
|
||||
}
|
||||
|
||||
void NetPlayDiag::OnAdjustBuffer(wxCommandEvent& event)
|
||||
{
|
||||
const int val = ((wxSpinCtrl*)event.GetEventObject())->GetValue();
|
||||
((NetPlayServer*)netplay_ptr)->AdjustPadBufferSize(val);
|
||||
netplay_server->AdjustPadBufferSize(val);
|
||||
|
||||
std::ostringstream ss;
|
||||
ss << "< Pad Buffer: " << val << " >";
|
||||
netplay_ptr->SendChatMessage(ss.str());
|
||||
netplay_client->SendChatMessage(ss.str());
|
||||
m_chat_text->AppendText(StrToWxStr(ss.str()).Append(wxT('\n')));
|
||||
}
|
||||
|
||||
|
@ -479,7 +499,7 @@ void NetPlayDiag::OnThread(wxCommandEvent& event)
|
|||
// player list
|
||||
m_playerids.clear();
|
||||
std::string tmps;
|
||||
netplay_ptr->GetPlayerList(tmps, m_playerids);
|
||||
netplay_client->GetPlayerList(tmps, m_playerids);
|
||||
|
||||
const int selection = m_player_lbox->GetSelection();
|
||||
|
||||
|
@ -502,15 +522,13 @@ void NetPlayDiag::OnThread(wxCommandEvent& event)
|
|||
case NP_GUI_EVT_START_GAME :
|
||||
// client start game :/
|
||||
{
|
||||
wxCommandEvent evt;
|
||||
OnStart(evt);
|
||||
netplay_client->StartGame(FindGame());
|
||||
}
|
||||
break;
|
||||
case NP_GUI_EVT_STOP_GAME :
|
||||
// client stop game
|
||||
{
|
||||
wxCommandEvent evt;
|
||||
OnStop(evt);
|
||||
netplay_client->StopGame();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -534,7 +552,7 @@ void NetPlayDiag::OnChangeGame(wxCommandEvent&)
|
|||
if (game_name.length())
|
||||
{
|
||||
m_selected_game = WxStrToStr(game_name);
|
||||
netplay_ptr->ChangeGame(m_selected_game);
|
||||
netplay_server->ChangeGame(m_selected_game);
|
||||
m_game_btn->SetLabel(game_name.Prepend(_(" Game : ")));
|
||||
}
|
||||
}
|
||||
|
@ -549,13 +567,13 @@ void NetPlayDiag::OnConfigPads(wxCommandEvent&)
|
|||
return;
|
||||
pid = m_playerids.at(pid);
|
||||
|
||||
if (false == ((NetPlayServer*)netplay_ptr)->GetPadMapping(pid, mapping))
|
||||
if (false == netplay_server->GetPadMapping(pid, mapping))
|
||||
return;
|
||||
|
||||
PadMapDiag pmd(this, mapping);
|
||||
pmd.ShowModal();
|
||||
|
||||
if (false == ((NetPlayServer*)netplay_ptr)->SetPadMapping(pid, mapping))
|
||||
if (false == netplay_server->SetPadMapping(pid, mapping))
|
||||
PanicAlertT("Could not set pads. The player left or the game is currently running!\n"
|
||||
"(setting pads while the game is running is not yet supported)");
|
||||
}
|
||||
|
@ -641,3 +659,11 @@ void PadMapDiag::OnAdjust(wxCommandEvent& event)
|
|||
for (unsigned int i=0; i<4; ++i)
|
||||
m_mapping[i] = m_map_cbox[i]->GetSelection() - 1;
|
||||
}
|
||||
|
||||
void NetPlay::StopGame()
|
||||
{
|
||||
if (netplay_server != NULL)
|
||||
netplay_server->StopGame();
|
||||
|
||||
// TODO: allow non-hosting clients to close the window
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
#include "FifoQueue.h"
|
||||
|
||||
#include "NetPlay.h"
|
||||
#include "NetPlayClient.h"
|
||||
|
||||
enum
|
||||
{
|
||||
|
@ -42,6 +42,8 @@ private:
|
|||
void OnHost(wxCommandEvent& event);
|
||||
void OnQuit(wxCommandEvent& event);
|
||||
|
||||
void MakeNetPlayDiag(int port, const std::string &game, bool is_hosting);
|
||||
|
||||
wxTextCtrl *m_nickname_text,
|
||||
*m_host_port_text,
|
||||
*m_connect_port_text,
|
||||
|
@ -65,7 +67,6 @@ public:
|
|||
Common::FifoQueue<std::string> chat_msgs;
|
||||
|
||||
void OnStart(wxCommandEvent& event);
|
||||
void OnStop(wxCommandEvent& event);
|
||||
|
||||
// implementation of NetPlayUI methods
|
||||
void BootGame(const std::string& filename);
|
||||
|
@ -85,11 +86,12 @@ private:
|
|||
|
||||
void OnChat(wxCommandEvent& event);
|
||||
void OnQuit(wxCommandEvent& event);
|
||||
void OnMemcardWriteCheck(wxCommandEvent& event);
|
||||
void OnThread(wxCommandEvent& event);
|
||||
void OnChangeGame(wxCommandEvent& event);
|
||||
void OnAdjustBuffer(wxCommandEvent& event);
|
||||
void OnConfigPads(wxCommandEvent& event);
|
||||
void GetNetSettings(NetSettings &settings);
|
||||
std::string FindGame();
|
||||
|
||||
wxListBox* m_player_lbox;
|
||||
wxTextCtrl* m_chat_text;
|
||||
|
@ -98,6 +100,7 @@ private:
|
|||
|
||||
std::string m_selected_game;
|
||||
wxButton* m_game_btn;
|
||||
wxButton* m_start_btn;
|
||||
|
||||
std::vector<int> m_playerids;
|
||||
|
||||
|
@ -130,5 +133,10 @@ private:
|
|||
int* const m_mapping;
|
||||
};
|
||||
|
||||
namespace NetPlay
|
||||
{
|
||||
void StopGame();
|
||||
}
|
||||
|
||||
#endif // _NETWINDOW_H_
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ wxString af_desc = wxTRANSLATE("Enable anisotropic filtering.\nEnhances visual q
|
|||
wxString aa_desc = wxTRANSLATE("Reduces the amount of aliasing caused by rasterizing 3D graphics.\nThis makes the rendered picture look less blocky.\nHeavily decreases emulation speed and sometimes causes issues.\n\nIf unsure, select None.");
|
||||
wxString scaled_efb_copy_desc = wxTRANSLATE("Greatly increases quality of textures generated using render to texture effects.\nRaising the internal resolution will improve the effect of this setting.\nSlightly decreases performance and possibly causes issues (although unlikely).\n\nIf unsure, leave this checked.");
|
||||
wxString pixel_lighting_desc = wxTRANSLATE("Calculate lighting of 3D graphics per-pixel rather than per vertex.\nDecreases emulation speed by some percent (depending on your GPU).\nThis usually is a safe enhancement, but might cause issues sometimes.\n\nIf unsure, leave this unchecked.");
|
||||
wxString hacked_buffer_upload_desc = wxTRANSLATE("Use a hacked upload strategy to stream vertices.\nThis usually speed up, but is forbidden by OpenGL specification and may causes heavy glitches.\n\nIf unsure, leave this unchecked.");
|
||||
wxString hacked_buffer_upload_desc = wxTRANSLATE("Speed up vertex streaming by using unsafe OpenGL code. Enabling this option might cause heavy glitches or even crash the emulator.\n\nIf unsure, leave this unchecked.");
|
||||
wxString fast_depth_calc_desc = wxTRANSLATE("Use a less accurate algorithm to calculate depth values.\nCauses issues in a few games but might give a decent speedup.\n\nIf unsure, leave this checked.");
|
||||
wxString force_filtering_desc = wxTRANSLATE("Force texture filtering even if the emulated game explicitly disabled it.\nImproves texture quality slightly but causes glitches in some games.\n\nIf unsure, leave this unchecked.");
|
||||
wxString _3d_vision_desc = wxTRANSLATE("Enable 3D effects via stereoscopy using Nvidia 3D Vision technology if it's supported by your GPU.\nPossibly causes issues.\nRequires fullscreen to work.\n\nIf unsure, leave this unchecked.");
|
||||
|
@ -121,7 +121,6 @@ wxString crop_desc = wxTRANSLATE("Crop the picture from 4:3 to 5:4 or from 16:9
|
|||
wxString opencl_desc = wxTRANSLATE("[EXPERIMENTAL]\nAims to speed up emulation by offloading texture decoding to the GPU using the OpenCL framework.\nHowever, right now it's known to cause texture defects in various games. Also it's slower than regular CPU texture decoding in most cases.\n\nIf unsure, leave this unchecked.");
|
||||
wxString dlc_desc = wxTRANSLATE("[EXPERIMENTAL]\nSpeeds up emulation a bit by caching display lists.\nPossibly causes issues though.\n\nIf unsure, leave this unchecked.");
|
||||
wxString omp_desc = wxTRANSLATE("Use multiple threads to decode textures.\nMight result in a speedup (especially on CPUs with more than two cores).\n\nIf unsure, leave this unchecked.");
|
||||
wxString hotkeys_desc = wxTRANSLATE("Allows toggling certain options via the hotkeys 3 (Internal Resolution), 4 (Aspect Ratio), 5 (Copy EFB) and 6 (Fog) within the emulation window.\n\nIf unsure, leave this unchecked.");
|
||||
wxString ppshader_desc = wxTRANSLATE("Apply a post-processing effect after finishing a frame.\n\nIf unsure, select (off).");
|
||||
wxString cache_efb_copies_desc = wxTRANSLATE("Slightly speeds up EFB to RAM copies by sacrificing emulation accuracy.\nSometimes also increases visual quality.\nIf you're experiencing any issues, try raising texture cache accuracy or disable this option.\n\nIf unsure, leave this unchecked.");
|
||||
wxString shader_errors_desc = wxTRANSLATE("Usually if shader compilation fails, an error message is displayed.\nHowever, one may skip the popups to allow interruption free gameplay by checking this option.\n\nIf unsure, leave this unchecked.");
|
||||
|
@ -503,7 +502,7 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con
|
|||
szr_other->Add(CreateCheckBox(page_hacks, _("OpenCL Texture Decoder"), wxGetTranslation(opencl_desc), vconfig.bEnableOpenCL));
|
||||
szr_other->Add(CreateCheckBox(page_hacks, _("OpenMP Texture Decoder"), wxGetTranslation(omp_desc), vconfig.bOMPDecoder));
|
||||
szr_other->Add(CreateCheckBox(page_hacks, _("Fast Depth Calculation"), wxGetTranslation(fast_depth_calc_desc), vconfig.bFastDepthCalc));
|
||||
szr_other->Add(hacked_buffer_upload = CreateCheckBox(page_hacks, _("Hacked Buffer Upload"), wxGetTranslation(hacked_buffer_upload_desc), vconfig.bHackedBufferUpload));
|
||||
szr_other->Add(hacked_buffer_upload = CreateCheckBox(page_hacks, _("Vertex Streaming Hack"), wxGetTranslation(hacked_buffer_upload_desc), vconfig.bHackedBufferUpload));
|
||||
|
||||
wxStaticBoxSizer* const group_other = new wxStaticBoxSizer(wxVERTICAL, page_hacks, _("Other"));
|
||||
group_other->Add(szr_other, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5);
|
||||
|
@ -559,7 +558,6 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con
|
|||
|
||||
szr_misc->Add(CreateCheckBox(page_advanced, _("Show Input Display"), wxGetTranslation(show_input_display_desc), vconfig.bShowInputDisplay));
|
||||
szr_misc->Add(CreateCheckBox(page_advanced, _("Crop"), wxGetTranslation(crop_desc), vconfig.bCrop));
|
||||
szr_misc->Add(CreateCheckBox(page_advanced, _("Enable Hotkeys"), wxGetTranslation(hotkeys_desc), vconfig.bOSDHotKey));
|
||||
|
||||
// Progressive Scan
|
||||
{
|
||||
|
|
|
@ -131,12 +131,14 @@ Joystick::Joystick(SDL_Joystick* const joystick, const int sdl_index, const unsi
|
|||
AddOutput(new SineEffect(m_state_out.back()));
|
||||
}
|
||||
|
||||
#ifdef SDL_HAPTIC_SQUARE
|
||||
// square effect
|
||||
if (supported_effects & SDL_HAPTIC_SQUARE)
|
||||
{
|
||||
m_state_out.push_back(EffectIDState());
|
||||
AddOutput(new SquareEffect(m_state_out.back()));
|
||||
}
|
||||
#endif // defined(SDL_HAPTIC_SQUARE)
|
||||
|
||||
// triangle effect
|
||||
if (supported_effects & SDL_HAPTIC_TRIANGLE)
|
||||
|
@ -187,10 +189,12 @@ std::string Joystick::SineEffect::GetName() const
|
|||
return "Sine";
|
||||
}
|
||||
|
||||
#ifdef SDL_HAPTIC_SQUARE
|
||||
std::string Joystick::SquareEffect::GetName() const
|
||||
{
|
||||
return "Square";
|
||||
}
|
||||
#endif // defined(SDL_HAPTIC_SQUARE)
|
||||
|
||||
std::string Joystick::TriangleEffect::GetName() const
|
||||
{
|
||||
|
@ -255,6 +259,7 @@ void Joystick::SineEffect::SetState(const ControlState state)
|
|||
m_effect.changed = true;
|
||||
}
|
||||
|
||||
#ifdef SDL_HAPTIC_SQUARE
|
||||
void Joystick::SquareEffect::SetState(const ControlState state)
|
||||
{
|
||||
if (state)
|
||||
|
@ -276,6 +281,7 @@ void Joystick::SquareEffect::SetState(const ControlState state)
|
|||
if (old != m_effect.effect.periodic.magnitude)
|
||||
m_effect.changed = true;
|
||||
}
|
||||
#endif // defined(SDL_HAPTIC_SQUARE)
|
||||
|
||||
void Joystick::TriangleEffect::SetState(const ControlState state)
|
||||
{
|
||||
|
|
|
@ -106,6 +106,7 @@ private:
|
|||
EffectIDState& m_effect;
|
||||
};
|
||||
|
||||
#ifdef SDL_HAPTIC_SQUARE
|
||||
class SquareEffect : public Output
|
||||
{
|
||||
public:
|
||||
|
@ -115,6 +116,7 @@ private:
|
|||
private:
|
||||
EffectIDState& m_effect;
|
||||
};
|
||||
#endif // defined(SDL_HAPTIC_SQUARE)
|
||||
|
||||
class TriangleEffect : public Output
|
||||
{
|
||||
|
|
|
@ -33,6 +33,7 @@ set(SRCS Src/BPFunctions.cpp
|
|||
Src/VertexManagerBase.cpp
|
||||
Src/VertexShaderGen.cpp
|
||||
Src/VertexShaderManager.cpp
|
||||
Src/VideoBackendBase.cpp
|
||||
Src/VideoConfig.cpp
|
||||
Src/VideoState.cpp
|
||||
Src/XFMemory.cpp
|
||||
|
|
|
@ -299,38 +299,3 @@ void GetBPRegInfo(const u8* data, char* name, size_t name_size, char* desc, size
|
|||
#undef SetRegName
|
||||
}
|
||||
}
|
||||
|
||||
AlphaTest::TEST_RESULT AlphaTest::TestResult()
|
||||
{
|
||||
switch(logic)
|
||||
{
|
||||
case 0: // AND
|
||||
if (comp0 == ALPHACMP_ALWAYS && comp1 == ALPHACMP_ALWAYS)
|
||||
return PASS;
|
||||
if (comp0 == ALPHACMP_NEVER || comp1 == ALPHACMP_NEVER)
|
||||
return FAIL;
|
||||
break;
|
||||
|
||||
case 1: // OR
|
||||
if (comp0 == ALPHACMP_ALWAYS || comp1 == ALPHACMP_ALWAYS)
|
||||
return PASS;
|
||||
if (comp0 == ALPHACMP_NEVER && comp1 == ALPHACMP_NEVER)
|
||||
return FAIL;
|
||||
break;
|
||||
|
||||
case 2: // XOR
|
||||
if ((comp0 == ALPHACMP_ALWAYS && comp1 == ALPHACMP_NEVER) || (comp0 == ALPHACMP_NEVER && comp1 == ALPHACMP_ALWAYS))
|
||||
return PASS;
|
||||
if ((comp0 == ALPHACMP_ALWAYS && comp1 == ALPHACMP_ALWAYS) || (comp0 == ALPHACMP_NEVER && comp1 == ALPHACMP_NEVER))
|
||||
return FAIL;
|
||||
break;
|
||||
|
||||
case 3: // XNOR
|
||||
if ((comp0 == ALPHACMP_ALWAYS && comp1 == ALPHACMP_NEVER) || (comp0 == ALPHACMP_NEVER && comp1 == ALPHACMP_ALWAYS))
|
||||
return FAIL;
|
||||
if ((comp0 == ALPHACMP_ALWAYS && comp1 == ALPHACMP_ALWAYS) || (comp0 == ALPHACMP_NEVER && comp1 == ALPHACMP_NEVER))
|
||||
return PASS;
|
||||
break;
|
||||
}
|
||||
return UNDETERMINED;
|
||||
}
|
||||
|
|
|
@ -805,6 +805,7 @@ union PE_CONTROL
|
|||
u32 unused : 17;
|
||||
u32 rid : 8;
|
||||
};
|
||||
|
||||
u32 hex;
|
||||
};
|
||||
|
||||
|
@ -884,7 +885,40 @@ union AlphaTest
|
|||
PASS = 2,
|
||||
};
|
||||
|
||||
TEST_RESULT TestResult();
|
||||
inline TEST_RESULT TestResult() const
|
||||
{
|
||||
switch(logic)
|
||||
{
|
||||
case 0: // AND
|
||||
if (comp0 == ALPHACMP_ALWAYS && comp1 == ALPHACMP_ALWAYS)
|
||||
return PASS;
|
||||
if (comp0 == ALPHACMP_NEVER || comp1 == ALPHACMP_NEVER)
|
||||
return FAIL;
|
||||
break;
|
||||
|
||||
case 1: // OR
|
||||
if (comp0 == ALPHACMP_ALWAYS || comp1 == ALPHACMP_ALWAYS)
|
||||
return PASS;
|
||||
if (comp0 == ALPHACMP_NEVER && comp1 == ALPHACMP_NEVER)
|
||||
return FAIL;
|
||||
break;
|
||||
|
||||
case 2: // XOR
|
||||
if ((comp0 == ALPHACMP_ALWAYS && comp1 == ALPHACMP_NEVER) || (comp0 == ALPHACMP_NEVER && comp1 == ALPHACMP_ALWAYS))
|
||||
return PASS;
|
||||
if ((comp0 == ALPHACMP_ALWAYS && comp1 == ALPHACMP_ALWAYS) || (comp0 == ALPHACMP_NEVER && comp1 == ALPHACMP_NEVER))
|
||||
return FAIL;
|
||||
break;
|
||||
|
||||
case 3: // XNOR
|
||||
if ((comp0 == ALPHACMP_ALWAYS && comp1 == ALPHACMP_NEVER) || (comp0 == ALPHACMP_NEVER && comp1 == ALPHACMP_ALWAYS))
|
||||
return FAIL;
|
||||
if ((comp0 == ALPHACMP_ALWAYS && comp1 == ALPHACMP_ALWAYS) || (comp0 == ALPHACMP_NEVER && comp1 == ALPHACMP_NEVER))
|
||||
return PASS;
|
||||
break;
|
||||
}
|
||||
return UNDETERMINED;
|
||||
}
|
||||
};
|
||||
|
||||
union UPE_Copy
|
||||
|
@ -1005,6 +1039,9 @@ struct BPMemory
|
|||
TevKSel tevksel[8];//0xf6,0xf7,f8,f9,fa,fb,fc,fd
|
||||
u32 bpMask; //0xFE
|
||||
u32 unknown18; //ff
|
||||
|
||||
bool UseEarlyDepthTest() const { return zcontrol.early_ztest && zmode.testenable; }
|
||||
bool UseLateDepthTest() const { return !zcontrol.early_ztest && zmode.testenable; }
|
||||
};
|
||||
|
||||
#pragma pack()
|
||||
|
|
|
@ -153,7 +153,10 @@ void BPWritten(const BPCmd& bp)
|
|||
bpmem.genMode.numtexgens, bpmem.genMode.numcolchans,
|
||||
bpmem.genMode.multisampling, bpmem.genMode.numtevstages+1, bpmem.genMode.cullmode,
|
||||
bpmem.genMode.numindstages, bpmem.genMode.zfreeze);
|
||||
SetGenerationMode();
|
||||
|
||||
// Only call SetGenerationMode when cull mode changes.
|
||||
if (bp.changes & 0xC000)
|
||||
SetGenerationMode();
|
||||
break;
|
||||
}
|
||||
case BPMEM_IND_MTXA: // Index Matrix Changed
|
||||
|
|
|
@ -20,6 +20,7 @@ namespace DriverDetails
|
|||
|
||||
// Local members
|
||||
Vendor m_vendor = VENDOR_UNKNOWN;
|
||||
Driver m_driver = DRIVER_UNKNOWN;
|
||||
u32 m_devfamily = 0;
|
||||
double m_version = 0.0;
|
||||
|
||||
|
@ -29,10 +30,6 @@ namespace DriverDetails
|
|||
{BUG_NODYNUBOACCESS, 300, 14.0, -1.0},
|
||||
{BUG_BROKENCENTROID, 300, 14.0, -1.0},
|
||||
{BUG_BROKENINFOLOG, 300, -1.0, -1.0},
|
||||
{BUG_BROKENBUFFERS, 300, 14.0, -1.0},
|
||||
};
|
||||
BugInfo m_armbugs[] = {
|
||||
{BUG_MALIBROKENBUFFERS, 600, -1.0, -1.0},
|
||||
};
|
||||
|
||||
std::map<std::pair<Vendor, Bug>, BugInfo> m_bugs;
|
||||
|
@ -40,26 +37,52 @@ namespace DriverDetails
|
|||
// Private function
|
||||
void InitBugMap()
|
||||
{
|
||||
switch(m_vendor)
|
||||
switch(m_driver)
|
||||
{
|
||||
case VENDOR_QUALCOMM:
|
||||
case DRIVER_QUALCOMM:
|
||||
for (unsigned int a = 0; a < (sizeof(m_qualcommbugs) / sizeof(BugInfo)); ++a)
|
||||
m_bugs[std::make_pair(m_vendor, m_qualcommbugs[a].m_bug)] = m_qualcommbugs[a];
|
||||
break;
|
||||
case VENDOR_ARM:
|
||||
for (unsigned int a = 0; a < (sizeof(m_armbugs) / sizeof(BugInfo)); ++a)
|
||||
m_bugs[std::make_pair(m_vendor, m_armbugs[a].m_bug)] = m_armbugs[a];
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Init(Vendor vendor, const u32 devfamily, const double version)
|
||||
void Init(Vendor vendor, Driver driver, const u32 devfamily, const double version)
|
||||
{
|
||||
m_vendor = vendor;
|
||||
m_driver = driver;
|
||||
m_devfamily = devfamily;
|
||||
m_version = version;
|
||||
InitBugMap();
|
||||
InitBugMap();
|
||||
if (driver == DRIVER_UNKNOWN)
|
||||
switch(vendor)
|
||||
{
|
||||
case VENDOR_NVIDIA:
|
||||
case VENDOR_TEGRA:
|
||||
m_driver = DRIVER_NVIDIA;
|
||||
break;
|
||||
case VENDOR_ATI:
|
||||
m_driver = DRIVER_ATI;
|
||||
break;
|
||||
case VENDOR_INTEL:
|
||||
m_driver = DRIVER_INTEL;
|
||||
break;
|
||||
case VENDOR_ARM:
|
||||
m_driver = DRIVER_ARM;
|
||||
break;
|
||||
case VENDOR_QUALCOMM:
|
||||
m_driver = DRIVER_QUALCOMM;
|
||||
break;
|
||||
case VENDOR_IMGTEC:
|
||||
m_driver = DRIVER_IMGTEC;
|
||||
break;
|
||||
case VENDOR_VIVANTE:
|
||||
m_driver = DRIVER_VIVANTE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
for (auto it = m_bugs.begin(); it != m_bugs.end(); ++it)
|
||||
if (it->second.m_devfamily == m_devfamily)
|
||||
|
|
|
@ -21,6 +21,23 @@ namespace DriverDetails
|
|||
VENDOR_UNKNOWN
|
||||
};
|
||||
|
||||
// Enum of known drivers
|
||||
enum Driver
|
||||
{
|
||||
DRIVER_NVIDIA = 0, // Official Nvidia, including mobile GPU
|
||||
DRIVER_NOUVEAU, // OSS nouveau
|
||||
DRIVER_ATI, // Official ATI
|
||||
DRIVER_RADEONHD, // OSS Radeon
|
||||
DRIVER_INTEL, // Official Intel
|
||||
DRIVER_ARM, // Official Mali driver
|
||||
DRIVER_LIMA, // OSS Mali driver
|
||||
DRIVER_QUALCOMM, // Official Adreno driver
|
||||
DRIVER_FREEDRENO, // OSS Adreno driver
|
||||
DRIVER_IMGTEC, // OSS PowerVR driver
|
||||
DRIVER_VIVANTE, // Official vivante driver
|
||||
DRIVER_UNKNOWN // Unknown driver, default to official hardware driver
|
||||
};
|
||||
|
||||
// Enum of known bugs
|
||||
// These can be vendor specific, but we put them all in here
|
||||
// For putting a new bug in here, make sure to put a detailed comment above the enum
|
||||
|
@ -52,30 +69,10 @@ namespace DriverDetails
|
|||
// Adreno devices /always/ return 0 when querying GL_INFO_LOG_LENGTH
|
||||
// They also max out at 1024 bytes(1023 characters + null terminator) for the log
|
||||
BUG_BROKENINFOLOG,
|
||||
// Bug: Uploading data with rendering causes issues
|
||||
// Affected devices: Qualcomm/Adreno
|
||||
// Started Version: 14
|
||||
// Ended Version: -1
|
||||
// When drawing our elements, the instruction buffer on Adreno devices
|
||||
// becomes too long, causing the device to quickly run out of RAM
|
||||
// I've watched the kernel module go up to ~700MB of RAM in a few seconds
|
||||
// The "workaround" is calling swapbuffers every single time we flush
|
||||
// This causes flickering, but it is the only known way to work around it
|
||||
BUG_BROKENBUFFERS,
|
||||
// Bug: Uploading data without swapping causes issues
|
||||
// Affected devices: Mali-T6xx
|
||||
// Started Version: -1
|
||||
// Ended Version: -1
|
||||
// This is similar to the Adreno rendering bug where uploading the data
|
||||
// to the GPU causes the device to quickly run out of RAM.
|
||||
// Unlike the Adreno workaround though, this can be fixed by calling
|
||||
// either glFlush() or glFinish() after flushing.
|
||||
// glFlush tends to take 0-1Ms on each call
|
||||
BUG_MALIBROKENBUFFERS,
|
||||
};
|
||||
|
||||
// Initializes our internal vendor, device family, and driver version
|
||||
void Init(Vendor vendor, const u32 devfamily, const double version);
|
||||
void Init(Vendor vendor, Driver driver, const u32 devfamily, const double version);
|
||||
|
||||
// Once Vendor and driver version is set, this will return if it has the applicable bug passed to it.
|
||||
bool HasBug(Bug bug);
|
||||
|
|
|
@ -151,9 +151,6 @@ void RunGpuLoop()
|
|||
// check if we are able to run this buffer
|
||||
while (GpuRunningState && !CommandProcessor::interruptWaiting && fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance && !AtBreakpoint())
|
||||
{
|
||||
if (!GpuRunningState)
|
||||
break;
|
||||
|
||||
fifo.isGpuReadingData = true;
|
||||
CommandProcessor::isPossibleWaitingSetDrawDone = fifo.bFF_GPLinkEnable ? true : false;
|
||||
|
||||
|
|
|
@ -9,40 +9,22 @@
|
|||
#include "NativeVertexFormat.h"
|
||||
#include "XFMemory.h"
|
||||
|
||||
static const char* LightCol(const char* lightsName, unsigned int index, const char* swizzle)
|
||||
{
|
||||
static char result[32];
|
||||
snprintf(result, sizeof(result), "%s[5*%d].%s", lightsName, index, swizzle);
|
||||
return result;
|
||||
}
|
||||
|
||||
static const char* LightCosAtt(const char* lightsName, unsigned int index)
|
||||
{
|
||||
static char result[32];
|
||||
snprintf(result, sizeof(result), "%s[5*%d+1]", lightsName, index);
|
||||
return result;
|
||||
}
|
||||
#define LIGHT_COL "%s[5*%d].%s"
|
||||
#define LIGHT_COL_PARAMS(lightsName, index, swizzle) (lightsName), (index), (swizzle)
|
||||
|
||||
static const char* LightDistAtt(const char* lightsName, unsigned int index)
|
||||
{
|
||||
static char result[32];
|
||||
snprintf(result, sizeof(result), "%s[5*%d+2]", lightsName, index);
|
||||
return result;
|
||||
}
|
||||
#define LIGHT_COSATT "%s[5*%d+1]"
|
||||
#define LIGHT_COSATT_PARAMS(lightsName, index) (lightsName), (index)
|
||||
|
||||
static const char* LightPos(const char* lightsName, unsigned int index)
|
||||
{
|
||||
static char result[32];
|
||||
snprintf(result, sizeof(result), "%s[5*%d+3]", lightsName, index);
|
||||
return result;
|
||||
}
|
||||
#define LIGHT_DISTATT "%s[5*%d+2]"
|
||||
#define LIGHT_DISTATT_PARAMS(lightsName, index) (lightsName), (index)
|
||||
|
||||
#define LIGHT_POS "%s[5*%d+3]"
|
||||
#define LIGHT_POS_PARAMS(lightsName, index) (lightsName), (index)
|
||||
|
||||
#define LIGHT_DIR "%s[5*%d+4]"
|
||||
#define LIGHT_DIR_PARAMS(lightsName, index) (lightsName), (index)
|
||||
|
||||
static const char* LightDir(const char* lightsName, unsigned int index)
|
||||
{
|
||||
static char result[32];
|
||||
snprintf(result, sizeof(result), "%s[5*%d+4]", lightsName, index);
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static void GenerateLightShader(T& object, LightingUidData& uid_data, int index, int litchan_index, const char* lightsName, int coloralpha)
|
||||
|
@ -62,13 +44,13 @@ static void GenerateLightShader(T& object, LightingUidData& uid_data, int index,
|
|||
switch (chan.diffusefunc)
|
||||
{
|
||||
case LIGHTDIF_NONE:
|
||||
object.Write("lacc.%s += %s;\n", swizzle, LightCol(lightsName, index, swizzle));
|
||||
object.Write("lacc.%s += " LIGHT_COL";\n", swizzle, LIGHT_COL_PARAMS(lightsName, index, swizzle));
|
||||
break;
|
||||
case LIGHTDIF_SIGN:
|
||||
case LIGHTDIF_CLAMP:
|
||||
object.Write("ldir = normalize(%s.xyz - pos.xyz);\n", LightPos(lightsName, index));
|
||||
object.Write("lacc.%s += %sdot(ldir, _norm0)) * %s;\n",
|
||||
swizzle, chan.diffusefunc != LIGHTDIF_SIGN ? "max(0.0f," :"(", LightCol(lightsName, index, swizzle));
|
||||
object.Write("ldir = normalize(" LIGHT_POS".xyz - pos.xyz);\n", LIGHT_POS_PARAMS(lightsName, index));
|
||||
object.Write("lacc.%s += %sdot(ldir, _norm0)) * " LIGHT_COL";\n",
|
||||
swizzle, chan.diffusefunc != LIGHTDIF_SIGN ? "max(0.0f," :"(", LIGHT_COL_PARAMS(lightsName, index, swizzle));
|
||||
break;
|
||||
default: _assert_(0);
|
||||
}
|
||||
|
@ -77,31 +59,34 @@ static void GenerateLightShader(T& object, LightingUidData& uid_data, int index,
|
|||
{
|
||||
if (chan.attnfunc == 3)
|
||||
{ // spot
|
||||
object.Write("ldir = %s.xyz - pos.xyz;\n", LightPos(lightsName, index));
|
||||
object.Write("ldir = " LIGHT_POS".xyz - pos.xyz;\n", LIGHT_POS_PARAMS(lightsName, index));
|
||||
object.Write("dist2 = dot(ldir, ldir);\n"
|
||||
"dist = sqrt(dist2);\n"
|
||||
"ldir = ldir / dist;\n"
|
||||
"attn = max(0.0f, dot(ldir, %s.xyz));\n", LightDir(lightsName, index));
|
||||
object.Write("attn = max(0.0f, dot(%s.xyz, float3(1.0f, attn, attn*attn))) / dot(%s.xyz, float3(1.0f,dist,dist2));\n", LightCosAtt(lightsName, index), LightDistAtt(lightsName, index));
|
||||
"attn = max(0.0f, dot(ldir, " LIGHT_DIR".xyz));\n",
|
||||
LIGHT_DIR_PARAMS(lightsName, index));
|
||||
object.Write("attn = max(0.0f, dot(" LIGHT_COSATT".xyz, float3(1.0f, attn, attn*attn))) / dot(" LIGHT_DISTATT".xyz, float3(1.0f,dist,dist2));\n",
|
||||
LIGHT_COSATT_PARAMS(lightsName, index), LIGHT_DISTATT_PARAMS(lightsName, index));
|
||||
}
|
||||
else if (chan.attnfunc == 1)
|
||||
{ // specular
|
||||
object.Write("ldir = normalize(%s.xyz);\n", LightPos(lightsName, index));
|
||||
object.Write("attn = (dot(_norm0,ldir) >= 0.0f) ? max(0.0f, dot(_norm0, %s.xyz)) : 0.0f;\n", LightDir(lightsName, index));
|
||||
object.Write("attn = max(0.0f, dot(%s.xyz, float3(1,attn,attn*attn))) / dot(%s.xyz, float3(1,attn,attn*attn));\n", LightCosAtt(lightsName, index), LightDistAtt(lightsName, index));
|
||||
object.Write("ldir = normalize(" LIGHT_POS".xyz);\n", LIGHT_POS_PARAMS(lightsName, index));
|
||||
object.Write("attn = (dot(_norm0,ldir) >= 0.0f) ? max(0.0f, dot(_norm0, " LIGHT_DIR".xyz)) : 0.0f;\n", LIGHT_DIR_PARAMS(lightsName, index));
|
||||
object.Write("attn = max(0.0f, dot(" LIGHT_COSATT".xyz, float3(1,attn,attn*attn))) / dot(" LIGHT_DISTATT".xyz, float3(1,attn,attn*attn));\n",
|
||||
LIGHT_COSATT_PARAMS(lightsName, index), LIGHT_DISTATT_PARAMS(lightsName, index));
|
||||
}
|
||||
|
||||
switch (chan.diffusefunc)
|
||||
{
|
||||
case LIGHTDIF_NONE:
|
||||
object.Write("lacc.%s += attn * %s;\n", swizzle, LightCol(lightsName, index, swizzle));
|
||||
object.Write("lacc.%s += attn * " LIGHT_COL";\n", swizzle, LIGHT_COL_PARAMS(lightsName, index, swizzle));
|
||||
break;
|
||||
case LIGHTDIF_SIGN:
|
||||
case LIGHTDIF_CLAMP:
|
||||
object.Write("lacc.%s += attn * %sdot(ldir, _norm0)) * %s;\n",
|
||||
object.Write("lacc.%s += attn * %sdot(ldir, _norm0)) * " LIGHT_COL";\n",
|
||||
swizzle,
|
||||
chan.diffusefunc != LIGHTDIF_SIGN ? "max(0.0f," :"(",
|
||||
LightCol(lightsName, index, swizzle));
|
||||
LIGHT_COL_PARAMS(lightsName, index, swizzle));
|
||||
break;
|
||||
default: _assert_(0);
|
||||
}
|
||||
|
@ -150,7 +135,10 @@ static void GenerateLightingShader(T& object, LightingUidData& uid_data, int com
|
|||
else if (components & VB_HAS_COL0 )
|
||||
object.Write("lacc = %s0;\n", inColorName);
|
||||
else
|
||||
object.Write("lacc = float4(0.0f, 0.0f, 0.0f, 0.0f);\n");
|
||||
// TODO: this isn't verified. Here we want to read the ambient from the vertex,
|
||||
// but the vertex itself has no color. So we don't know which value to read.
|
||||
// Returing 1.0 is the same as disabled lightning, so this could be fine
|
||||
object.Write("lacc = float4(1.0f, 1.0f, 1.0f, 1.0f);\n");
|
||||
}
|
||||
else // from color
|
||||
{
|
||||
|
@ -191,7 +179,8 @@ static void GenerateLightingShader(T& object, LightingUidData& uid_data, int com
|
|||
else if (components & VB_HAS_COL0 )
|
||||
object.Write("lacc.w = %s0.w;\n", inColorName);
|
||||
else
|
||||
object.Write("lacc.w = 0.0f;\n");
|
||||
// TODO: The same for alpha: We want to read from vertex, but the vertex has no color
|
||||
object.Write("lacc.w = 1.0f;\n");
|
||||
}
|
||||
else // from color
|
||||
{
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
#include "PerfQueryBase.h"
|
||||
#include "VideoConfig.h"
|
||||
|
||||
PerfQueryBase* g_perf_query = 0;
|
||||
|
||||
bool PerfQueryBase::ShouldEmulate() const
|
||||
{
|
||||
return g_ActiveConfig.bPerfQueriesEnable;
|
||||
}
|
||||
|
|
|
@ -25,9 +25,12 @@ enum PerfQueryGroup
|
|||
class PerfQueryBase
|
||||
{
|
||||
public:
|
||||
PerfQueryBase() {};
|
||||
PerfQueryBase() {}
|
||||
virtual ~PerfQueryBase() {}
|
||||
|
||||
// Checks if performance queries are enabled in the gameini configuration.
|
||||
bool ShouldEmulate() const;
|
||||
|
||||
// Begin querying the specified value for the following host GPU commands
|
||||
virtual void EnableQuery(PerfQueryGroup type) {}
|
||||
|
||||
|
|
|
@ -216,7 +216,7 @@ static char swapModeTable[4][5];
|
|||
|
||||
static char text[16384];
|
||||
|
||||
static void BuildSwapModeTable()
|
||||
static inline void BuildSwapModeTable()
|
||||
{
|
||||
static const char *swapColors = "rgba";
|
||||
for (int i = 0; i < 4; i++)
|
||||
|
@ -229,13 +229,13 @@ static void BuildSwapModeTable()
|
|||
}
|
||||
}
|
||||
|
||||
template<class T> static void WriteStage(T& out, pixel_shader_uid_data& uid_data, int n, API_TYPE ApiType, RegisterState RegisterStates[4]);
|
||||
template<class T> static void SampleTexture(T& out, const char *destination, const char *texcoords, const char *texswap, int texmap, API_TYPE ApiType);
|
||||
template<class T> static void WriteAlphaTest(T& out, pixel_shader_uid_data& uid_data, API_TYPE ApiType,DSTALPHA_MODE dstAlphaMode, bool per_pixel_depth);
|
||||
template<class T> static void WriteFog(T& out, pixel_shader_uid_data& uid_data);
|
||||
template<class T> static inline void WriteStage(T& out, pixel_shader_uid_data& uid_data, int n, API_TYPE ApiType, RegisterState RegisterStates[4]);
|
||||
template<class T> static inline void SampleTexture(T& out, const char *texcoords, const char *texswap, int texmap, API_TYPE ApiType);
|
||||
template<class T> static inline void WriteAlphaTest(T& out, pixel_shader_uid_data& uid_data, API_TYPE ApiType,DSTALPHA_MODE dstAlphaMode, bool per_pixel_depth);
|
||||
template<class T> static inline void WriteFog(T& out, pixel_shader_uid_data& uid_data);
|
||||
|
||||
template<class T>
|
||||
static void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_TYPE ApiType, u32 components)
|
||||
static inline void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_TYPE ApiType, u32 components)
|
||||
{
|
||||
// Non-uid template parameters will write to the dummy data (=> gets optimized out)
|
||||
pixel_shader_uid_data dummy_data;
|
||||
|
@ -243,23 +243,25 @@ static void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_TYPE Api
|
|||
? out.template GetUidData<pixel_shader_uid_data>() : dummy_data;
|
||||
|
||||
out.SetBuffer(text);
|
||||
const bool is_writing_shadercode = (out.GetBuffer() != NULL);
|
||||
#ifndef ANDROID
|
||||
locale_t locale;
|
||||
locale_t old_locale;
|
||||
if (out.GetBuffer() != NULL)
|
||||
if (is_writing_shadercode)
|
||||
{
|
||||
locale = newlocale(LC_NUMERIC_MASK, "C", NULL); // New locale for compilation
|
||||
old_locale = uselocale(locale); // Apply the locale for this thread
|
||||
}
|
||||
#endif
|
||||
|
||||
text[sizeof(text) - 1] = 0x7C; // canary
|
||||
if (is_writing_shadercode)
|
||||
text[sizeof(text) - 1] = 0x7C; // canary
|
||||
|
||||
unsigned int numStages = bpmem.genMode.numtevstages + 1;
|
||||
unsigned int numTexgen = bpmem.genMode.numtexgens;
|
||||
|
||||
const bool forced_early_z = g_ActiveConfig.backend_info.bSupportsEarlyZ && bpmem.zcontrol.early_ztest && (g_ActiveConfig.bFastDepthCalc || bpmem.alpha_test.TestResult() == AlphaTest::UNDETERMINED);
|
||||
const bool per_pixel_depth = (bpmem.ztex2.op != ZTEXTURE_DISABLE && !bpmem.zcontrol.early_ztest && bpmem.zmode.testenable) || (!g_ActiveConfig.bFastDepthCalc && !forced_early_z);
|
||||
const bool forced_early_z = g_ActiveConfig.backend_info.bSupportsEarlyZ && bpmem.UseEarlyDepthTest() && (g_ActiveConfig.bFastDepthCalc || bpmem.alpha_test.TestResult() == AlphaTest::UNDETERMINED);
|
||||
const bool per_pixel_depth = (bpmem.ztex2.op != ZTEXTURE_DISABLE && bpmem.UseLateDepthTest()) || (!g_ActiveConfig.bFastDepthCalc && bpmem.zmode.testenable && !forced_early_z);
|
||||
|
||||
out.Write("//Pixel Shader for TEV stages\n");
|
||||
out.Write("//%i TEV stages, %i texgens, %i IND stages\n",
|
||||
|
@ -331,7 +333,7 @@ static void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_TYPE Api
|
|||
|
||||
out.Write("VARYIN float4 colors_02;\n");
|
||||
out.Write("VARYIN float4 colors_12;\n");
|
||||
|
||||
|
||||
// compute window position if needed because binding semantic WPOS is not widely supported
|
||||
// Let's set up attributes
|
||||
if (xfregs.numTexGen.numTexGens < 7)
|
||||
|
@ -365,18 +367,37 @@ static void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_TYPE Api
|
|||
}
|
||||
out.Write("float4 clipPos;\n");
|
||||
}
|
||||
|
||||
|
||||
if (forced_early_z)
|
||||
{
|
||||
// HACK: This doesn't force the driver to write to depth buffer if alpha test fails.
|
||||
// It just allows it, but it seems that all drivers do.
|
||||
out.Write("layout(early_fragment_tests) in;\n");
|
||||
}
|
||||
|
||||
else if (bpmem.UseEarlyDepthTest() && (g_ActiveConfig.bFastDepthCalc || bpmem.alpha_test.TestResult() == AlphaTest::UNDETERMINED) && is_writing_shadercode)
|
||||
{
|
||||
static bool warn_once = true;
|
||||
if (warn_once)
|
||||
WARN_LOG(VIDEO, "Early z test enabled but not possible to emulate with current configuration. Make sure to use the D3D11 or OpenGL backend and enable fast depth calculations. If this message still shows up your hardware isn't able to emulate the feature properly (a GPU which supports D3D 11.0 / OGL 4.2 is required).");
|
||||
warn_once = false;
|
||||
}
|
||||
|
||||
out.Write("void main()\n{\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (forced_early_z)
|
||||
{
|
||||
out.Write("[earlydepthstencil]\n");
|
||||
}
|
||||
else if (bpmem.UseEarlyDepthTest() && (g_ActiveConfig.bFastDepthCalc || bpmem.alpha_test.TestResult() == AlphaTest::UNDETERMINED) && is_writing_shadercode)
|
||||
{
|
||||
static bool warn_once = true;
|
||||
if (warn_once)
|
||||
WARN_LOG(VIDEO, "Early z test enabled but not possible to emulate with current configuration. Make sure to use the D3D11 or OpenGL backend and enable fast depth calculations. If this message still shows up your hardware isn't able to emulate the feature properly (a GPU which supports D3D 11.0 / OGL 4.2 is required).");
|
||||
warn_once = false;
|
||||
}
|
||||
|
||||
out.Write("void main(\n");
|
||||
if(ApiType != API_D3D11)
|
||||
{
|
||||
|
@ -481,7 +502,6 @@ static void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_TYPE Api
|
|||
|
||||
if (g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting)
|
||||
{
|
||||
uid_data.xfregs_numTexGen_numTexGens = xfregs.numTexGen.numTexGens;
|
||||
if (xfregs.numTexGen.numTexGens < 7)
|
||||
{
|
||||
out.Write("\tfloat3 _norm0 = normalize(Normal.xyz);\n\n");
|
||||
|
@ -558,9 +578,8 @@ static void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_TYPE Api
|
|||
else
|
||||
out.Write("\ttempcoord = float2(0.0f, 0.0f);\n");
|
||||
|
||||
char buffer[32];
|
||||
sprintf(buffer, "float3 indtex%d", i);
|
||||
SampleTexture<T>(out, buffer, "tempcoord", "abg", texmap, ApiType);
|
||||
out.Write("float3 indtex%d = ", i);
|
||||
SampleTexture<T>(out, "tempcoord", "abg", texmap, ApiType);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -608,7 +627,10 @@ static void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_TYPE Api
|
|||
|
||||
AlphaTest::TEST_RESULT Pretest = bpmem.alpha_test.TestResult();
|
||||
uid_data.Pretest = Pretest;
|
||||
if (Pretest == AlphaTest::UNDETERMINED)
|
||||
|
||||
// NOTE: Fragment may not be discarded if alpha test always fails and early depth test is enabled
|
||||
// (in this case we need to write a depth value if depth test passes regardless of the alpha testing result)
|
||||
if (Pretest == AlphaTest::UNDETERMINED || (Pretest == AlphaTest::FAIL && bpmem.UseLateDepthTest()))
|
||||
WriteAlphaTest<T>(out, uid_data, ApiType, dstAlphaMode, per_pixel_depth);
|
||||
|
||||
|
||||
|
@ -631,11 +653,11 @@ static void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_TYPE Api
|
|||
uid_data.per_pixel_depth = per_pixel_depth;
|
||||
uid_data.forced_early_z = forced_early_z;
|
||||
uid_data.fast_depth_calc = g_ActiveConfig.bFastDepthCalc;
|
||||
uid_data.early_ztest = bpmem.zcontrol.early_ztest;
|
||||
uid_data.early_ztest = bpmem.UseEarlyDepthTest();
|
||||
uid_data.fog_fsel = bpmem.fog.c_proj_fsel.fsel;
|
||||
|
||||
// Note: z-textures are not written to depth buffer if early depth test is used
|
||||
if (per_pixel_depth && bpmem.zcontrol.early_ztest)
|
||||
if (per_pixel_depth && bpmem.UseEarlyDepthTest())
|
||||
out.Write("depth = zCoord;\n");
|
||||
|
||||
// Note: depth texture output is only written to depth buffer if late depth test is used
|
||||
|
@ -653,7 +675,7 @@ static void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_TYPE Api
|
|||
out.Write("zCoord = zCoord * (16777216.0f/16777215.0f);\n");
|
||||
}
|
||||
|
||||
if (per_pixel_depth && !bpmem.zcontrol.early_ztest)
|
||||
if (per_pixel_depth && bpmem.UseLateDepthTest())
|
||||
out.Write("depth = zCoord;\n");
|
||||
|
||||
if (dstAlphaMode == DSTALPHA_ALPHA_PASS)
|
||||
|
@ -688,16 +710,16 @@ static void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_TYPE Api
|
|||
|
||||
out.Write("}\n");
|
||||
|
||||
if (text[sizeof(text) - 1] != 0x7C)
|
||||
PanicAlert("PixelShader generator - buffer too small, canary has been eaten!");
|
||||
if (is_writing_shadercode)
|
||||
{
|
||||
if (text[sizeof(text) - 1] != 0x7C)
|
||||
PanicAlert("PixelShader generator - buffer too small, canary has been eaten!");
|
||||
|
||||
#ifndef ANDROID
|
||||
if (out.GetBuffer() != NULL)
|
||||
{
|
||||
uselocale(old_locale); // restore locale
|
||||
freelocale(locale);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -745,7 +767,7 @@ static const char *TEVCMPAlphaOPTable[16] =
|
|||
};
|
||||
|
||||
template<class T>
|
||||
static void WriteStage(T& out, pixel_shader_uid_data& uid_data, int n, API_TYPE ApiType, RegisterState RegisterStates[4])
|
||||
static inline void WriteStage(T& out, pixel_shader_uid_data& uid_data, int n, API_TYPE ApiType, RegisterState RegisterStates[4])
|
||||
{
|
||||
int texcoord = bpmem.tevorders[n/2].getTexCoord(n&1);
|
||||
bool bHasTexCoord = (u32)texcoord < bpmem.genMode.numtexgens;
|
||||
|
@ -888,7 +910,9 @@ static void WriteStage(T& out, pixel_shader_uid_data& uid_data, int n, API_TYPE
|
|||
char *texswap = swapModeTable[bpmem.combiners[n].alphaC.tswap];
|
||||
int texmap = bpmem.tevorders[n/2].getTexMap(n&1);
|
||||
uid_data.SetTevindrefTexmap(i, texmap);
|
||||
SampleTexture<T>(out, "textemp", "tevcoord", texswap, texmap, ApiType);
|
||||
|
||||
out.Write("textemp = ");
|
||||
SampleTexture<T>(out, "tevcoord", texswap, texmap, ApiType);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1110,13 +1134,14 @@ static void WriteStage(T& out, pixel_shader_uid_data& uid_data, int n, API_TYPE
|
|||
}
|
||||
|
||||
template<class T>
|
||||
void SampleTexture(T& out, const char *destination, const char *texcoords, const char *texswap, int texmap, API_TYPE ApiType)
|
||||
void SampleTexture(T& out, const char *texcoords, const char *texswap, int texmap, API_TYPE ApiType)
|
||||
{
|
||||
out.SetConstantsUsed(C_TEXDIMS+texmap,C_TEXDIMS+texmap);
|
||||
|
||||
if (ApiType == API_D3D11)
|
||||
out.Write("%s=Tex%d.Sample(samp%d,%s.xy * " I_TEXDIMS"[%d].xy).%s;\n", destination, texmap,texmap, texcoords, texmap, texswap);
|
||||
out.Write("Tex%d.Sample(samp%d,%s.xy * " I_TEXDIMS"[%d].xy).%s;\n", texmap,texmap, texcoords, texmap, texswap);
|
||||
else
|
||||
out.Write("%s=%s(samp%d,%s.xy * " I_TEXDIMS"[%d].xy).%s;\n", destination, ApiType == API_OPENGL ? "texture" : "tex2D", texmap, texcoords, texmap, texswap);
|
||||
out.Write("%s(samp%d,%s.xy * " I_TEXDIMS"[%d].xy).%s;\n", ApiType == API_OPENGL ? "texture" : "tex2D", texmap, texcoords, texmap, texswap);
|
||||
}
|
||||
|
||||
static const char *tevAlphaFuncsTable[] =
|
||||
|
@ -1140,7 +1165,7 @@ static const char *tevAlphaFunclogicTable[] =
|
|||
};
|
||||
|
||||
template<class T>
|
||||
static void WriteAlphaTest(T& out, pixel_shader_uid_data& uid_data, API_TYPE ApiType, DSTALPHA_MODE dstAlphaMode, bool per_pixel_depth)
|
||||
static inline void WriteAlphaTest(T& out, pixel_shader_uid_data& uid_data, API_TYPE ApiType, DSTALPHA_MODE dstAlphaMode, bool per_pixel_depth)
|
||||
{
|
||||
static const char *alphaRef[2] =
|
||||
{
|
||||
|
@ -1183,11 +1208,11 @@ static void WriteAlphaTest(T& out, pixel_shader_uid_data& uid_data, API_TYPE Api
|
|||
// We implement "depth test before texturing" by disabling alpha test when early-z is in use.
|
||||
// It seems to be less buggy than not to update the depth buffer if alpha test fails,
|
||||
// but both ways wouldn't be accurate.
|
||||
|
||||
|
||||
// OpenGL 4.2 has a flag which allows the driver to still update the depth buffer
|
||||
// if alpha test fails. The driver doesn't have to, but I assume they all do because
|
||||
// it's the much faster code path for the GPU.
|
||||
uid_data.alpha_test_use_zcomploc_hack = bpmem.zcontrol.early_ztest && bpmem.zmode.updateenable && !g_ActiveConfig.backend_info.bSupportsEarlyZ;
|
||||
uid_data.alpha_test_use_zcomploc_hack = bpmem.UseEarlyDepthTest() && bpmem.zmode.updateenable && !g_ActiveConfig.backend_info.bSupportsEarlyZ;
|
||||
if (!uid_data.alpha_test_use_zcomploc_hack)
|
||||
{
|
||||
out.Write("\t\tdiscard;\n");
|
||||
|
@ -1211,7 +1236,7 @@ static const char *tevFogFuncsTable[] =
|
|||
};
|
||||
|
||||
template<class T>
|
||||
static void WriteFog(T& out, pixel_shader_uid_data& uid_data)
|
||||
static inline void WriteFog(T& out, pixel_shader_uid_data& uid_data)
|
||||
{
|
||||
uid_data.fog_fsel = bpmem.fog.c_proj_fsel.fsel;
|
||||
if(bpmem.fog.c_proj_fsel.fsel == 0)
|
||||
|
@ -1253,7 +1278,7 @@ static void WriteFog(T& out, pixel_shader_uid_data& uid_data)
|
|||
}
|
||||
else
|
||||
{
|
||||
if (bpmem.fog.c_proj_fsel.fsel != 2)
|
||||
if (bpmem.fog.c_proj_fsel.fsel != 2 && out.GetBuffer() != NULL)
|
||||
WARN_LOG(VIDEO, "Unknown Fog Type! %08x", bpmem.fog.c_proj_fsel.fsel);
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue