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