// Copyright (c) 2012- 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/. #include <string> #include <cstring> #ifndef _MSC_VER #include <strings.h> #endif #include "Common/StringUtils.h" #include "Core/CoreTiming.h" #include "Core/Debugger/Breakpoints.h" #include "Core/Debugger/SymbolMap.h" #include "Core/Debugger/DebugInterface.h" #include "Core/MIPS/MIPSDebugInterface.h" #include "Core/MIPS/MIPSVFPUUtils.h" #include "Core/HLE/sceKernelThread.h" #include "Core/MemMap.h" #include "Core/MIPS/MIPSTables.h" #include "Core/Core.h" #include "Core/System.h" enum ReferenceIndexType { REF_INDEX_PC = 32, REF_INDEX_HI = 33, REF_INDEX_LO = 34, REF_INDEX_FPU = 0x1000, REF_INDEX_FPU_INT = 0x2000, REF_INDEX_VFPU = 0x4000, REF_INDEX_VFPU_INT = 0x8000, REF_INDEX_IS_FLOAT = REF_INDEX_FPU | REF_INDEX_VFPU, REF_INDEX_HLE = 0x10000, REF_INDEX_THREAD = REF_INDEX_HLE | 0, REF_INDEX_MODULE = REF_INDEX_HLE | 1, REF_INDEX_USEC = REF_INDEX_HLE | 2, REF_INDEX_TICKS = REF_INDEX_HLE | 3, }; class MipsExpressionFunctions : public IExpressionFunctions { public: MipsExpressionFunctions(const DebugInterface *_cpu): cpu(_cpu) {} bool parseReference(char* str, uint32_t& referenceIndex) override { for (int i = 0; i < 32; i++) { char reg[8]; snprintf(reg, sizeof(reg), "r%d", i); if (strcasecmp(str, reg) == 0 || strcasecmp(str, MIPSDebugInterface::GetRegName(0, i).c_str()) == 0) { referenceIndex = i; return true; } else if (strcasecmp(str, MIPSDebugInterface::GetRegName(1, i).c_str()) == 0) { referenceIndex = REF_INDEX_FPU | i; return true; } snprintf(reg, sizeof(reg), "fi%d", i); if (strcasecmp(str, reg) == 0) { referenceIndex = REF_INDEX_FPU_INT | i; return true; } } for (int i = 0; i < 128; i++) { if (strcasecmp(str, MIPSDebugInterface::GetRegName(2, i).c_str()) == 0) { referenceIndex = REF_INDEX_VFPU | i; return true; } char reg[8]; snprintf(reg, sizeof(reg), "vi%d", i); if (strcasecmp(str, reg) == 0) { referenceIndex = REF_INDEX_VFPU_INT | i; return true; } } if (strcasecmp(str, "pc") == 0) { referenceIndex = REF_INDEX_PC; return true; } if (strcasecmp(str, "hi") == 0) { referenceIndex = REF_INDEX_HI; return true; } if (strcasecmp(str, "lo") == 0) { referenceIndex = REF_INDEX_LO; return true; } if (strcasecmp(str, "threadid") == 0) { referenceIndex = REF_INDEX_THREAD; return true; } if (strcasecmp(str, "moduleid") == 0) { referenceIndex = REF_INDEX_MODULE; return true; } if (strcasecmp(str, "usec") == 0) { referenceIndex = REF_INDEX_USEC; return true; } if (strcasecmp(str, "ticks") == 0) { referenceIndex = REF_INDEX_TICKS; return true; } return false; } bool parseSymbol(char* str, uint32_t& symbolValue) override { return g_symbolMap->GetLabelValue(str,symbolValue); } uint32_t getReferenceValue(uint32_t referenceIndex) override { if (referenceIndex < 32) return cpu->GetRegValue(0, referenceIndex); if (referenceIndex == REF_INDEX_PC) return cpu->GetPC(); if (referenceIndex == REF_INDEX_HI) return cpu->GetHi(); if (referenceIndex == REF_INDEX_LO) return cpu->GetLo(); if (referenceIndex == REF_INDEX_THREAD) return __KernelGetCurThread(); if (referenceIndex == REF_INDEX_MODULE) return __KernelGetCurThreadModuleId(); if (referenceIndex == REF_INDEX_USEC) return (uint32_t)CoreTiming::GetGlobalTimeUs(); // Loses information if (referenceIndex == REF_INDEX_TICKS) return (uint32_t)CoreTiming::GetTicks(); if ((referenceIndex & ~(REF_INDEX_FPU | REF_INDEX_FPU_INT)) < 32) return cpu->GetRegValue(1, referenceIndex & ~(REF_INDEX_FPU | REF_INDEX_FPU_INT)); if ((referenceIndex & ~(REF_INDEX_VFPU | REF_INDEX_VFPU_INT)) < 128) return cpu->GetRegValue(2, referenceIndex & ~(REF_INDEX_VFPU | REF_INDEX_VFPU_INT)); return -1; } ExpressionType getReferenceType(uint32_t referenceIndex) override { if (referenceIndex & REF_INDEX_IS_FLOAT) { return EXPR_TYPE_FLOAT; } return EXPR_TYPE_UINT; } bool getMemoryValue(uint32_t address, int size, uint32_t& dest, std::string *error) override { // We allow, but ignore, bad access. // If we didn't, log/condition statements that reference registers couldn't be configured. uint32_t valid = Memory::ValidSize(address, size); uint8_t buf[4]{}; if (valid != 0) memcpy(buf, Memory::GetPointerUnchecked(address), valid); switch (size) { case 1: dest = buf[0]; return true; case 2: dest = (buf[1] << 8) | buf[0]; return true; case 4: dest = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; return true; } *error = StringFromFormat("Unexpected memory access size %d", size); return false; } private: const DebugInterface *cpu; }; unsigned int MIPSDebugInterface::readMemory(unsigned int address) { if (Memory::IsValidRange(address, 4)) return Memory::ReadUnchecked_Instruction(address).encoding; return 0; } bool MIPSDebugInterface::isAlive() { return PSP_IsInited() && coreState != CORE_BOOT_ERROR && coreState != CORE_RUNTIME_ERROR && coreState != CORE_POWERDOWN; } bool MIPSDebugInterface::isBreakpoint(unsigned int address) { return g_breakpoints.IsAddressBreakPoint(address); } void MIPSDebugInterface::setBreakpoint(unsigned int address) { g_breakpoints.AddBreakPoint(address); } void MIPSDebugInterface::clearBreakpoint(unsigned int address) { g_breakpoints.RemoveBreakPoint(address); } void MIPSDebugInterface::clearAllBreakpoints() {} void MIPSDebugInterface::toggleBreakpoint(unsigned int address) { if (g_breakpoints.IsAddressBreakPoint(address)) { g_breakpoints.RemoveBreakPoint(address); } else { g_breakpoints.AddBreakPoint(address); } } int MIPSDebugInterface::getColor(unsigned int address, bool darkMode) const { uint32_t colors[6] = { 0xFFe0FFFF, 0xFFFFe0e0, 0xFFe8e8FF, 0xFFFFe0FF, 0xFFe0FFe0, 0xFFFFFFe0 }; uint32_t colorsDark[6] = { 0xFF301010, 0xFF103030, 0xFF403010, 0xFF103000, 0xFF301030, 0xFF101030 }; int n = g_symbolMap->GetFunctionNum(address); if (n == -1) { return darkMode ? 0xFF101010 : 0xFFFFFFFF; } else if (darkMode) { return colorsDark[n % ARRAY_SIZE(colorsDark)]; } else { return colors[n % ARRAY_SIZE(colors)]; } } std::string MIPSDebugInterface::getDescription(unsigned int address) { return g_symbolMap->GetDescription(address); } std::string MIPSDebugInterface::GetRegName(int cat, int index) { static const char * const regName[32] = { "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra" }; static const char * const fpRegName[32] = { "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", }; if (cat == 0 && (unsigned)index < ARRAY_SIZE(regName)) { return regName[index]; } else if (cat == 1 && (unsigned)index < ARRAY_SIZE(fpRegName)) { return fpRegName[index]; } else if (cat == 2) { return GetVectorNotation(index, V_Single); } return "???"; } bool initExpression(const DebugInterface *debug, const char* exp, PostfixExpression& dest) { MipsExpressionFunctions funcs(debug); return initPostfixExpression(exp, &funcs, dest); } bool parseExpression(const DebugInterface *debug, PostfixExpression& exp, u32& dest) { MipsExpressionFunctions funcs(debug); return parsePostfixExpression(exp, &funcs, dest); } void DisAsm(u32 pc, char *out, size_t outSize) { if (Memory::IsValidAddress(pc)) MIPSDisAsm(Memory::Read_Opcode_JIT(pc), pc, out, outSize); else truncate_cpy(out, outSize, "-"); }