diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj
index c1681d104..bc90969b8 100644
--- a/Core/Core.vcxproj
+++ b/Core/Core.vcxproj
@@ -451,6 +451,7 @@
true
+
@@ -685,6 +686,7 @@
true
+
diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters
index 0069834c3..a06ce6581 100644
--- a/Core/Core.vcxproj.filters
+++ b/Core/Core.vcxproj.filters
@@ -567,6 +567,7 @@
+
@@ -1078,6 +1079,7 @@
+
diff --git a/Core/MIPS/ARM64/Arm64Asm.cpp b/Core/MIPS/ARM64/Arm64Asm.cpp
index f7642ffed..d70912d83 100644
--- a/Core/MIPS/ARM64/Arm64Asm.cpp
+++ b/Core/MIPS/ARM64/Arm64Asm.cpp
@@ -73,7 +73,6 @@ using namespace Arm64JitConstants;
void Arm64Jit::GenerateFixedCode()
{
-
// Uncomment if you want to see the output...
// INFO_LOG(JIT, "THE DISASM ========================");
// DisassembleArm(enterCode, GetCodePtr() - enterCode);
diff --git a/Core/Util/DisArm64.cpp b/Core/Util/DisArm64.cpp
new file mode 100644
index 000000000..dcd51c622
--- /dev/null
+++ b/Core/Util/DisArm64.cpp
@@ -0,0 +1,133 @@
+// Copyright (c) 2015- PPSSPP 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 or later versions.
+
+// 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 git repository and contact information can be found at
+// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
+
+// Basic ARM64 disassembler.
+// No promises of accuracy, mostly just made to debug JIT code.
+
+#include
+
+#include "Common/StringUtils.h"
+
+struct Instruction {
+ char text[128];
+ bool undefined;
+ bool badbits;
+ bool oddbits;
+};
+
+int SignExtend26(int x) {
+ return (x & 0x02000000) ? (0xFC000000 | x) : x;
+}
+
+static void DataProcessingImmediate(uint32_t w, uint64_t addr, Instruction *instr) {
+ snprintf(instr->text, sizeof(instr->text), "(DPI %08x)", w);
+}
+
+static void BranchExceptionAndSystem(uint32_t w, uint64_t addr, Instruction *instr) {
+ if (((w >> 26) & 0x1F) == 5) {
+ // Unconditional branch / branch+link
+ int offset = SignExtend26(w & 0x03FFFFFF) << 2;
+ uint64_t target = addr + offset;
+ snprintf(instr->text, sizeof(instr->text), "b%s %08x%08x", (w >> 31) ? "l" : "", (target >> 32), (target & 0xFFFFFFFF));
+ } else {
+ snprintf(instr->text, sizeof(instr->text), "(BRX %08x)", w);
+ }
+}
+
+static void LoadStore(uint32_t w, uint64_t addr, Instruction *instr) {
+ snprintf(instr->text, sizeof(instr->text), "(LS %08x)", w);
+}
+
+static void DataProcessingRegister(uint32_t w, uint64_t addr, Instruction *instr) {
+ int rd = w & 0x1F;
+ int rn = (w >> 5) & 0x1F;
+ int rm = (w >> 16) & 0x1F;
+
+ if (((w >> 21) & 0xF) == 9) {
+ bool S = (w >> 29) & 1;
+ char r = ((w >> 31) & 1) ? 'x' : 'w';
+ bool sub = (w >> 30) & 1;
+ snprintf(instr->text, sizeof(instr->text), "%s%s %c%d, %c%d, %c%d", sub ? "sub" : "add", S ? "s" : "", r, rd, r, rn, r, rm);
+ } else {
+ snprintf(instr->text, sizeof(instr->text), "(DPR %08x)", w);
+ }
+}
+
+static void FPandASIMD1(uint32_t w, uint64_t addr, Instruction *instr) {
+ snprintf(instr->text, sizeof(instr->text), "(FP1 %08x)", w);
+}
+
+static void FPandASIMD2(uint32_t w, uint64_t addr, Instruction *instr) {
+ snprintf(instr->text, sizeof(instr->text), "(FP2 %08x)", w);
+}
+
+static void DisassembleInstruction(uint32_t w, uint64_t addr, Instruction *instr) {
+ memset(instr, 0, sizeof(*instr));
+
+ // Identify the main encoding groups. See C3.1 A64 instruction index by encoding
+ int id = (w >> 25) & 0xF;
+ switch (id) {
+ case 0: case 1: case 2: case 3: // 00xx
+ instr->undefined = true;
+ break;
+ case 8: case 9:
+ DataProcessingImmediate(w, addr, instr);
+ break;
+ case 0xA: case 0xB:
+ BranchExceptionAndSystem(w, addr, instr);
+ break;
+ case 4: case 6: case 0xC: case 0xE:
+ LoadStore(w, addr, instr);
+ break;
+ case 5: case 0xD:
+ DataProcessingRegister(w, addr, instr);
+ break;
+ case 7:
+ FPandASIMD1(w, addr, instr);
+ break;
+ case 0xF:
+ FPandASIMD2(w, addr, instr);
+ break;
+ }
+}
+
+void Arm64Dis(uint64_t addr, uint32_t w, char *output, int bufsize, bool includeWord) {
+ Instruction instr;
+ DisassembleInstruction(w, addr, &instr);
+ char temp[256];
+ if (includeWord) {
+ snprintf(output, bufsize, "%08x\t%s", w, instr.text);
+ } else {
+ snprintf(output, bufsize, "%s", instr.text);
+ }
+ if (instr.undefined || instr.badbits || instr.oddbits) {
+ if (instr.undefined) snprintf(output, bufsize, "%08x\t[undefined instr]", w);
+ if (instr.badbits) snprintf(output, bufsize, "%08x\t[illegal bits]", w);
+
+ // strcat(output, " ? (extra bits)");
+ if (instr.oddbits) {
+ snprintf(temp, sizeof(temp), " [unexpected bits %08x]", w);
+ strcat(output, temp);
+ }
+ }
+ // zap tabs
+ while (*output) {
+ if (*output == '\t')
+ *output = ' ';
+ output++;
+ }
+}
diff --git a/Core/Util/DisArm64.h b/Core/Util/DisArm64.h
new file mode 100644
index 000000000..0125aa01a
--- /dev/null
+++ b/Core/Util/DisArm64.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2015- PPSSPP 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 or later versions.
+
+// 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 git repository and contact information can be found at
+// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
+
+// Basic ARM64 disassembler.
+// No promises of accuracy, mostly just made to debug JIT code.
+
+#include
+
+void Arm64Dis(uint64_t addr, uint32_t w, char *output, int bufsize, bool includeWord);
+
diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp
index 534c403f5..84992f52f 100644
--- a/UI/DevScreens.cpp
+++ b/UI/DevScreens.cpp
@@ -614,6 +614,8 @@ void JitCompareScreen::UpdateDisasm() {
#if defined(ARM)
std::vector targetDis = DisassembleArm2(block->normalEntry, block->codeSize);
+#elif defined(ARM64)
+ std::vector targetDis = DisassembleArm64(block->normalEntry, block->codeSize);
#else
std::vector targetDis = DisassembleX86(block->normalEntry, block->codeSize);
#endif
diff --git a/unittest/TestArm64Emitter.cpp b/unittest/TestArm64Emitter.cpp
new file mode 100644
index 000000000..46e634e0f
--- /dev/null
+++ b/unittest/TestArm64Emitter.cpp
@@ -0,0 +1,39 @@
+#include "Common/ARM64Emitter.h"
+#include "Core/MIPS/JitCommon/JitState.h"
+#include "Core/MIPS/JitCommon/JitCommon.h"
+#include "Core/MIPS/MIPSVFPUUtils.h"
+#include "Core/Util/DisArm64.h"
+
+#include "UnitTest.h"
+
+static bool CheckLast(Arm64Gen::ARM64XEmitter &emit, const char *comp) {
+ u32 instr;
+ memcpy(&instr, emit.GetCodePtr() - 4, 4);
+ char disasm[512];
+ Arm64Dis(0, instr, disasm, sizeof(disasm), true);
+ EXPECT_EQ_STR(std::string(disasm), std::string(comp));
+ return true;
+}
+
+static void DisassembleARMBetween(const u8 *start, const u8 *end) {
+ while (start < end) {
+ char disasm[512];
+ uint32_t instr;
+ memcpy(&instr, start, 4);
+ Arm64Dis(0, instr, disasm, sizeof(disasm), true);
+ printf("%s\n", disasm);
+ start += 4;
+ }
+}
+
+bool TestArm64Emitter() {
+ using namespace Arm64Gen;
+
+ u32 code[512];
+ ARM64XEmitter emitter((u8 *)code);
+ emitter.ADD(X1, X2, X30);
+ RET(CheckLast(emitter, "8b3e6041 add x1, x2, x30"));
+ emitter.SUB(W1, W2, W30);
+ RET(CheckLast(emitter, "4b3e4041 sub w1, w2, w30"));
+ return true;
+}
\ No newline at end of file
diff --git a/unittest/TestArmEmitter.cpp b/unittest/TestArmEmitter.cpp
index d16ee9de6..ea8a979e7 100644
--- a/unittest/TestArmEmitter.cpp
+++ b/unittest/TestArmEmitter.cpp
@@ -7,7 +7,7 @@
#include "UnitTest.h"
-bool CheckLast(ArmGen::ARMXEmitter &emit, const char *comp) {
+static bool CheckLast(ArmGen::ARMXEmitter &emit, const char *comp) {
u32 instr;
memcpy(&instr, emit.GetCodePtr() - 4, 4);
char disasm[512];
@@ -16,7 +16,7 @@ bool CheckLast(ArmGen::ARMXEmitter &emit, const char *comp) {
return true;
}
-void DisassembleARMBetween(const u8 *start, const u8 *end) {
+static void DisassembleARMBetween(const u8 *start, const u8 *end) {
while (start < end) {
char disasm[512];
uint32_t instr;
diff --git a/unittest/UnitTest.cpp b/unittest/UnitTest.cpp
index 6a7a3139a..1d003ac2f 100644
--- a/unittest/UnitTest.cpp
+++ b/unittest/UnitTest.cpp
@@ -369,16 +369,21 @@ struct TestItem {
#define TEST_ITEM(name) { #name, &Test ##name, }
bool TestArmEmitter();
+bool TestArm64Emitter();
bool TestX64Emitter();
-
TestItem availableTests[] = {
- TEST_ITEM(Asin),
- TEST_ITEM(SinCos),
+#if defined(ARM64) || defined(_M_X64) || defined(_M_IX86)
+ TEST_ITEM(Arm64Emitter),
+#endif
+#if defined(ARM) || defined(_M_X64) || defined(_M_IX86)
TEST_ITEM(ArmEmitter),
-#ifndef ARM
+#endif
+#if defined(_M_X64) || defined(_M_IX86)
TEST_ITEM(X64Emitter),
#endif
+ TEST_ITEM(Asin),
+ TEST_ITEM(SinCos),
TEST_ITEM(VFPUSinCos),
TEST_ITEM(MathUtil),
TEST_ITEM(Parsers),
diff --git a/unittest/UnitTests.vcxproj b/unittest/UnitTests.vcxproj
index 1c1a58f56..433316498 100644
--- a/unittest/UnitTests.vcxproj
+++ b/unittest/UnitTests.vcxproj
@@ -181,6 +181,7 @@
+
diff --git a/unittest/UnitTests.vcxproj.filters b/unittest/UnitTests.vcxproj.filters
index 6ab9eba1d..6f010302f 100644
--- a/unittest/UnitTests.vcxproj.filters
+++ b/unittest/UnitTests.vcxproj.filters
@@ -1,14 +1,15 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file