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:
Matthew Parlane 2013-08-23 00:51:12 +12:00
commit b6e054a2be
1697 changed files with 64354 additions and 72384 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -86,6 +86,8 @@ private:
bool ShouldLoadBP(u8 address);
bool m_Loop;
u32 m_CurrentFrame;
u32 m_FrameRangeStart;
u32 m_FrameRangeEnd;

View file

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

View file

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

View file

@ -32,7 +32,7 @@ void Shutdown()
delete *i;
g_plugin.controllers.clear();
WiimoteReal::Shutdown();
WiimoteReal::Stop();
g_controller_interface.Shutdown();
}

View file

@ -54,6 +54,7 @@ namespace WiimoteReal
{
void Initialize(bool wait = false);
void Stop();
void Shutdown();
void Resume();
void Pause();

View file

@ -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( &reg[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( &reg, 0, WIIMOTE_REG_EXT_SIZE );
memcpy( &reg.constant_id, id, sizeof(id) );
memcpy( &reg.calibration, calibration, sizeof(calibration) );
}
}
void ControllerEmu::Extension::GetState( u8* const data, const bool focus )

View file

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

View file

@ -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(&reg[0x20], classic_calibration, sizeof(classic_calibration));
memcpy(&calibration, classic_calibration, sizeof(classic_calibration));
// id
memcpy(&reg[0xfa], classic_id, sizeof(classic_id));
memcpy(&id, classic_id, sizeof(classic_id));
}
void Classic::GetState(u8* const data, const bool focus)

View file

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

View file

@ -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(&reg[0xfa], drums_id, sizeof(drums_id));
memcpy(&id, drums_id, sizeof(drums_id));
}
void Drums::GetState(u8* const data, const bool focus)

View file

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

View file

@ -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(&reg[0xfa], guitar_id, sizeof(guitar_id));
memcpy(&id, guitar_id, sizeof(guitar_id));
}
void Guitar::GetState(u8* const data, const bool focus)

View file

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

View file

@ -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(&reg[0x20], nunchuck_calibration, sizeof(nunchuck_calibration));
memcpy(&calibration, nunchuck_calibration, sizeof(nunchuck_calibration));
// id
memcpy(&reg[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*)&reg.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*)&reg[0x20];
accel_cal* calib = (accel_cal*)&reg.calibration[0];
// tilt
EmulateTilt(&accel, m_tilt, focus);

View file

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

View file

@ -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(&reg[0xfa], turntable_id, sizeof(turntable_id));
memcpy(&id, turntable_id, sizeof(turntable_id));
}
void Turntable::GetState(u8* const data, const bool focus)

View file

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

View file

@ -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> &reg = ((WiimoteEmu::Attachment*)m_extension->attachments[m_extension->active_extension])->reg;
memset(&m_reg_ext, 0, WIIMOTE_REG_EXT_SIZE);
memcpy(&m_reg_ext, &reg[0], reg.size());
// reset register
((WiimoteEmu::Attachment*)m_extension->attachments[m_extension->active_extension])->Reset();
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View 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

View 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

View file

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

View 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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -15,7 +15,7 @@ namespace State
{
// number of states
static const u32 NUM_STATES = 8;
static const u32 NUM_STATES = 10;
struct StateHeader
{

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,3 +1,9 @@
#include "PerfQueryBase.h"
#include "VideoConfig.h"
PerfQueryBase* g_perf_query = 0;
bool PerfQueryBase::ShouldEmulate() const
{
return g_ActiveConfig.bPerfQueriesEnable;
}

View file

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

View file

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