summaryrefslogtreecommitdiff
path: root/Source/Core/Common
diff options
context:
space:
mode:
authorMatthew Parlane <parlane@gmail.com>2013-08-23 00:51:12 +1200
committerMatthew Parlane <parlane@gmail.com>2013-08-23 00:51:12 +1200
commitb6e054a2beef3236822e27d95dfe38a4ccee2eb1 (patch)
tree6f744593b14def9c2fbc7be9e509df5507835cc4 /Source/Core/Common
parentf274b3b074fb8bb0477b0e9f4e88db0e08bf4f2c (diff)
parent41c25d0c90a2461c3e34bf9659dcf1da5e1e8580 (diff)
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
Diffstat (limited to 'Source/Core/Common')
-rw-r--r--Source/Core/Common/CMakeLists.txt1
-rw-r--r--Source/Core/Common/Common.vcxproj4
-rw-r--r--Source/Core/Common/Common.vcxproj.filters4
-rw-r--r--Source/Core/Common/Src/ArmCPUDetect.cpp95
-rw-r--r--Source/Core/Common/Src/ArmEmitter.cpp241
-rw-r--r--Source/Core/Common/Src/ArmEmitter.h36
-rw-r--r--Source/Core/Common/Src/ExtendedTrace.cpp3
-rw-r--r--Source/Core/Common/Src/IniFile.cpp54
-rw-r--r--Source/Core/Common/Src/IniFile.h3
-rw-r--r--Source/Core/Common/Src/MemArena.cpp50
-rw-r--r--Source/Core/Common/Src/StringUtil.cpp37
-rw-r--r--Source/Core/Common/Src/VideoBackendBase.cpp80
-rw-r--r--Source/Core/Common/Src/VideoBackendBase.h185
-rw-r--r--Source/Core/Common/Src/x64CPUDetect.cpp19
-rw-r--r--Source/Core/Common/make_scmrev.h.js4
15 files changed, 374 insertions, 442 deletions
diff --git a/Source/Core/Common/CMakeLists.txt b/Source/Core/Common/CMakeLists.txt
index 4891f3c090..6e230c7fa7 100644
--- a/Source/Core/Common/CMakeLists.txt
+++ b/Source/Core/Common/CMakeLists.txt
@@ -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
diff --git a/Source/Core/Common/Common.vcxproj b/Source/Core/Common/Common.vcxproj
index 9c21b2a992..4066febcca 100644
--- a/Source/Core/Common/Common.vcxproj
+++ b/Source/Core/Common/Common.vcxproj
@@ -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> \ No newline at end of file
+</Project>
diff --git a/Source/Core/Common/Common.vcxproj.filters b/Source/Core/Common/Common.vcxproj.filters
index f16b437f54..a912cc3d8a 100644
--- a/Source/Core/Common/Common.vcxproj.filters
+++ b/Source/Core/Common/Common.vcxproj.filters
@@ -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> \ No newline at end of file
+</Project>
diff --git a/Source/Core/Common/Src/ArmCPUDetect.cpp b/Source/Core/Common/Src/ArmCPUDetect.cpp
index ca277ade02..ef49d6efb1 100644
--- a/Source/Core/Common/Src/ArmCPUDetect.cpp
+++ b/Source/Core/Common/Src/ArmCPUDetect.cpp
@@ -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";
diff --git a/Source/Core/Common/Src/ArmEmitter.cpp b/Source/Core/Common/Src/ArmEmitter.cpp
index 1b20a1784e..2788760c6d 100644
--- a/Source/Core/Common/Src/ArmEmitter.cpp
+++ b/Source/Core/Common/Src/ArmEmitter.cpp
@@ -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 {
diff --git a/Source/Core/Common/Src/ArmEmitter.h b/Source/Core/Common/Src/ArmEmitter.h
index b3fab39c40..1592da7d4f 100644
--- a/Source/Core/Common/Src/ArmEmitter.h
+++ b/Source/Core/Common/Src/ArmEmitter.h
@@ -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,7 +650,23 @@ 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
diff --git a/Source/Core/Common/Src/ExtendedTrace.cpp b/Source/Core/Common/Src/ExtendedTrace.cpp
index 85057639a5..08740c3c95 100644
--- a/Source/Core/Common/Src/ExtendedTrace.cpp
+++ b/Source/Core/Common/Src/ExtendedTrace.cpp
@@ -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
{
diff --git a/Source/Core/Common/Src/IniFile.cpp b/Source/Core/Common/Src/IniFile.cpp
index 0b265543de..2eca8df929 100644
--- a/Source/Core/Common/Src/IniFile.cpp
+++ b/Source/Core/Common/Src/IniFile.cpp
@@ -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)
{
- 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;
+ if (line[0] == '#')
+ return;
- // Allow preservation of spacing before comment
- if (FirstCommentChar > 0)
- {
- while (line[FirstCommentChar - 1] == ' ' || line[FirstCommentChar - 1] == 9) // 9 == tab
- {
- FirstCommentChar--;
- }
- }
+ int FirstEquals = (int)line.find("=", 0);
- 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)
diff --git a/Source/Core/Common/Src/IniFile.h b/Source/Core/Common/Src/IniFile.h
index 10e1cfdbb9..bad26ca090 100644
--- a/Source/Core/Common/Src/IniFile.h
+++ b/Source/Core/Common/Src/IniFile.h
@@ -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);
diff --git a/Source/Core/Common/Src/MemArena.cpp b/Source/Core/Common/Src/MemArena.cpp
index 487788dc45..b7400a978e 100644
--- a/Source/Core/Common/Src/MemArena.cpp
+++ b/Source/Core/Common/Src/MemArena.cpp
@@ -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;
+ }
+ }
}
}
diff --git a/Source/Core/Common/Src/StringUtil.cpp b/Source/Core/Common/Src/StringUtil.cpp
index 99bc2ae6ca..25885552ef 100644
--- a/Source/Core/Common/Src/StringUtil.cpp
+++ b/Source/Core/Common/Src/StringUtil.cpp
@@ -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
diff --git a/Source/Core/Common/Src/VideoBackendBase.cpp b/Source/Core/Common/Src/VideoBackendBase.cpp
deleted file mode 100644
index 99bce01156..0000000000
--- a/Source/Core/Common/Src/VideoBackendBase.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright 2013 Dolphin Emulator Project
-// Licensed under GPLv2
-// Refer to the license.txt file included.
-
-#include "VideoBackendBase.h"
-
-// TODO: ugly
-#ifdef _WIN32
-#include "../../../Plugins/Plugin_VideoDX9/Src/VideoBackend.h"
-#include "../../../Plugins/Plugin_VideoDX11/Src/VideoBackend.h"
-#endif
-#if !defined(USE_GLES) || USE_GLES3
-#include "../../../Plugins/Plugin_VideoOGL/Src/VideoBackend.h"
-#endif
-#include "../../../Plugins/Plugin_VideoSoftware/Src/VideoBackend.h"
-
-std::vector<VideoBackend*> g_available_video_backends;
-VideoBackend* g_video_backend = NULL;
-static VideoBackend* s_default_backend = NULL;
-
-#ifdef _WIN32
-// http://msdn.microsoft.com/en-us/library/ms725491.aspx
-static bool IsGteVista()
-{
- OSVERSIONINFOEX osvi;
- DWORDLONG dwlConditionMask = 0;
-
- ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
- osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
- osvi.dwMajorVersion = 6;
-
- VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
-
- return VerifyVersionInfo(&osvi, VER_MAJORVERSION, dwlConditionMask) != FALSE;
-}
-#endif
-
-void VideoBackend::PopulateList()
-{
- VideoBackend* backends[4] = { NULL };
-
- // D3D11 > OGL > D3D9 > SW
-#ifdef _WIN32
- g_available_video_backends.push_back(backends[2] = new DX9::VideoBackend);
- if (IsGteVista())
- g_available_video_backends.push_back(backends[0] = new DX11::VideoBackend);
-#endif
-#if !defined(USE_GLES) || USE_GLES3
- g_available_video_backends.push_back(backends[1] = new OGL::VideoBackend);
-#endif
- g_available_video_backends.push_back(backends[3] = new SW::VideoSoftware);
-
- for (int i = 0; i < 4; ++i)
- {
- if (backends[i])
- {
- s_default_backend = g_video_backend = backends[i];
- break;
- }
- }
-}
-
-void VideoBackend::ClearList()
-{
- while (!g_available_video_backends.empty())
- {
- delete g_available_video_backends.back();
- g_available_video_backends.pop_back();
- }
-}
-
-void VideoBackend::ActivateBackend(const std::string& name)
-{
- if (name.length() == 0) // If NULL, set it to the default backend (expected behavior)
- g_video_backend = s_default_backend;
-
- for (std::vector<VideoBackend*>::const_iterator it = g_available_video_backends.begin(); it != g_available_video_backends.end(); ++it)
- if (name == (*it)->GetName())
- g_video_backend = *it;
-}
diff --git a/Source/Core/Common/Src/VideoBackendBase.h b/Source/Core/Common/Src/VideoBackendBase.h
deleted file mode 100644
index 9c591d05a5..0000000000
--- a/Source/Core/Common/Src/VideoBackendBase.h
+++ /dev/null
@@ -1,185 +0,0 @@
-// Copyright 2013 Dolphin Emulator Project
-// Licensed under GPLv2
-// Refer to the license.txt file included.
-
-#ifndef VIDEO_BACKEND_H_
-#define VIDEO_BACKEND_H_
-
-#include <string>
-#include <vector>
-
-#include "ChunkFile.h"
-#include "../../VideoCommon/Src/PerfQueryBase.h"
-
-typedef void (*writeFn16)(const u16,const u32);
-typedef void (*writeFn32)(const u32,const u32);
-typedef void (*readFn16)(u16&, const u32);
-
-
-enum FieldType
-{
- FIELD_PROGRESSIVE = 0,
- FIELD_UPPER,
- FIELD_LOWER
-};
-
-enum EFBAccessType
-{
- PEEK_Z = 0,
- POKE_Z,
- PEEK_COLOR,
- POKE_COLOR
-};
-
-struct SCPFifoStruct
-{
- // fifo registers
- volatile u32 CPBase;
- volatile u32 CPEnd;
- u32 CPHiWatermark;
- u32 CPLoWatermark;
- volatile u32 CPReadWriteDistance;
- volatile u32 CPWritePointer;
- volatile u32 CPReadPointer;
- volatile u32 CPBreakpoint;
- volatile u32 SafeCPReadPointer;
- // Super Monkey Ball Adventure require this.
- // Because the read&check-PEToken-loop stays in its JITed block I suppose.
- // So no possiblity to ack the Token irq by the scheduler until some sort of PPC watchdog do its mess.
- volatile u16 PEToken;
-
- volatile u32 bFF_GPLinkEnable;
- volatile u32 bFF_GPReadEnable;
- volatile u32 bFF_BPEnable;
- volatile u32 bFF_BPInt;
- volatile u32 bFF_Breakpoint;
-
- volatile u32 CPCmdIdle;
- volatile u32 CPReadIdle;
-
- volatile u32 bFF_LoWatermarkInt;
- volatile u32 bFF_HiWatermarkInt;
-
- volatile u32 bFF_LoWatermark;
- volatile u32 bFF_HiWatermark;
-
- // for GP watchdog hack
- volatile u32 Fake_GPWDToken; // cicular incrementer
- volatile u32 isGpuReadingData;
-};
-
-class VideoBackend
-{
-public:
- virtual ~VideoBackend() {}
-
- virtual void EmuStateChange(EMUSTATE_CHANGE) = 0;
-
- virtual void UpdateFPSDisplay(const char*) = 0;
-
- virtual unsigned int PeekMessages() = 0;
-
- virtual bool Initialize(void *&) = 0;
- virtual void Shutdown() = 0;
- virtual void RunLoop(bool enable) = 0;
-
- virtual std::string GetName() = 0;
- virtual std::string GetDisplayName() { return GetName(); }
-
- virtual void ShowConfig(void*) {}
-
- virtual void Video_Prepare() = 0;
- virtual void Video_EnterLoop() = 0;
- virtual void Video_ExitLoop() = 0;
- virtual void Video_Cleanup() = 0; // called from gl/d3d thread
-
- virtual void Video_BeginField(u32, FieldType, u32, u32) = 0;
- virtual void Video_EndField() = 0;
-
- virtual u32 Video_AccessEFB(EFBAccessType, u32, u32, u32) = 0;
- virtual u32 Video_GetQueryResult(PerfQueryType type) = 0;
-
- virtual void Video_AddMessage(const char* pstr, unsigned int milliseconds) = 0;
- virtual void Video_ClearMessages() = 0;
- virtual bool Video_Screenshot(const char* filename) = 0;
-
- virtual void Video_SetRendering(bool bEnabled) = 0;
-
- virtual void Video_GatherPipeBursted() = 0;
-
- virtual bool Video_IsPossibleWaitingSetDrawDone() = 0;
- virtual bool Video_IsHiWatermarkActive() = 0;
- virtual void Video_AbortFrame() = 0;
-
- virtual readFn16 Video_CPRead16() = 0;
- virtual writeFn16 Video_CPWrite16() = 0;
- virtual readFn16 Video_PERead16() = 0;
- virtual writeFn16 Video_PEWrite16() = 0;
- virtual writeFn32 Video_PEWrite32() = 0;
-
- static void PopulateList();
- static void ClearList();
- static void ActivateBackend(const std::string& name);
-
- // waits until is paused and fully idle, and acquires a lock on that state.
- // or, if doLock is false, releases a lock on that state and optionally unpauses.
- // calls must be balanced and non-recursive (once with doLock true, then once with doLock false).
- virtual void PauseAndLock(bool doLock, bool unpauseOnUnlock=true) = 0;
-
- // the implementation needs not do synchronization logic, because calls to it are surrounded by PauseAndLock now
- virtual void DoState(PointerWrap &p) = 0;
-
- virtual void CheckInvalidState() = 0;
-};
-
-extern std::vector<VideoBackend*> g_available_video_backends;
-extern VideoBackend* g_video_backend;
-
-// inherited by dx9/dx11/ogl backends
-class VideoBackendHardware : public VideoBackend
-{
- void RunLoop(bool enable);
- bool Initialize(void *&) { InitializeShared(); return true; }
-
- void EmuStateChange(EMUSTATE_CHANGE);
-
- void Video_EnterLoop();
- void Video_ExitLoop();
- void Video_BeginField(u32, FieldType, u32, u32);
- void Video_EndField();
-
- u32 Video_AccessEFB(EFBAccessType, u32, u32, u32);
- u32 Video_GetQueryResult(PerfQueryType type);
-
- void Video_AddMessage(const char* pstr, unsigned int milliseconds);
- void Video_ClearMessages();
- bool Video_Screenshot(const char* filename);
-
- void Video_SetRendering(bool bEnabled);
-
- void Video_GatherPipeBursted();
-
- bool Video_IsPossibleWaitingSetDrawDone();
- bool Video_IsHiWatermarkActive();
- void Video_AbortFrame();
-
- readFn16 Video_CPRead16();
- writeFn16 Video_CPWrite16();
- readFn16 Video_PERead16();
- writeFn16 Video_PEWrite16();
- writeFn32 Video_PEWrite32();
-
- void PauseAndLock(bool doLock, bool unpauseOnUnlock=true);
- void DoState(PointerWrap &p);
-
- bool m_invalid;
-
-public:
- void CheckInvalidState();
-
-protected:
- void InitializeShared();
- void InvalidState();
-};
-
-#endif
diff --git a/Source/Core/Common/Src/x64CPUDetect.cpp b/Source/Core/Common/Src/x64CPUDetect.cpp
index d225262f81..e4eac54ef1 100644
--- a/Source/Core/Common/Src/x64CPUDetect.cpp
+++ b/Source/Core/Common/Src/x64CPUDetect.cpp
@@ -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
diff --git a/Source/Core/Common/make_scmrev.h.js b/Source/Core/Common/make_scmrev.h.js
index 82f6394c2b..f2978987dc 100644
--- a/Source/Core/Common/make_scmrev.h.js
+++ b/Source/Core/Common/make_scmrev.h.js
@@ -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" +