diff --git a/src/core/debug.cc b/src/core/debug.cc index 0f4c7b3d7..a2c27bc7e 100644 --- a/src/core/debug.cc +++ b/src/core/debug.cc @@ -28,6 +28,7 @@ #include "core/psxemulator.h" #include "core/psxmem.h" #include "core/r3000a.h" +#include "supportpsx/memory.h" enum { MAP_EXEC = 1, @@ -40,80 +41,134 @@ enum { MAP_EXEC_JAL = 128, }; +PCSX::Debug::Debug() : m_listener(g_system->m_eventBus) { + m_listener.listen([this](auto&) { + m_checkKernel = false; + clearMaps(); + }); +} + uint32_t PCSX::Debug::normalizeAddress(uint32_t address) { - uint32_t base = (address >> 20) & 0xffc; - uint32_t real = address & 0x7fffff; + PSXAddress addr(address); const bool ramExpansion = PCSX::g_emulator->settings.get(); - if (!ramExpansion && ((base == 0x000) || (base == 0x800) || (base == 0xa00))) { - return address & ~0x00600000; + if (!ramExpansion && (addr.type == PSXAddress::Type::RAM)) { + addr.physical &= ~0x00600000; } - return address; + return addr.toVirtual().value_or(0xffffffff); +} + +bool PCSX::Debug::isInKernel(uint32_t address, bool biosIsKernel) { + PSXAddress addr(address); + const bool ramExpansion = PCSX::g_emulator->settings.get(); + if (addr.type == PSXAddress::Type::ROM) return biosIsKernel; + if (addr.type != PSXAddress::Type::RAM) return false; + if (!ramExpansion) addr.physical &= ~0x00600000; + return addr.physical < 0x10000; } void PCSX::Debug::markMap(uint32_t address, int mask) { - address = normalizeAddress(address); - uint32_t base = (address >> 20) & 0xffc; - uint32_t real = address & 0x7fffff; - uint32_t shortReal = address & 0x3fffff; - if (((base == 0x000) || (base == 0x800) || (base == 0xa00)) && (real < sizeof(m_mainMemoryMap))) { - m_mainMemoryMap[real] |= mask; - } else if ((base == 0x1f8) && (real < sizeof(m_scratchPadMap))) { - m_scratchPadMap[real] |= mask; - } else if ((base == 0xbfc) && (shortReal < sizeof(m_biosMemoryMap))) { - m_biosMemoryMap[shortReal] |= mask; + PSXAddress addr(normalizeAddress(address)); + + switch (addr.type) { + case PSXAddress::Type::RAM: + if (addr.physical < sizeof(m_mainMemoryMap)) { + m_mainMemoryMap[addr.physical] |= mask; + } + break; + case PSXAddress::Type::ScratchPad: + if (addr.physical < sizeof(m_scratchPadMap)) { + m_scratchPadMap[addr.physical] |= mask; + } + break; + case PSXAddress::Type::ROM: + if (addr.physical < sizeof(m_biosMemoryMap)) { + m_biosMemoryMap[addr.physical] |= mask; + } + break; } } bool PCSX::Debug::isMapMarked(uint32_t address, int mask) { - address = normalizeAddress(address); - uint32_t base = (address >> 20) & 0xffc; - uint32_t real = address & 0x7fffff; - uint32_t shortReal = address & 0x3fffff; - if (((base == 0x000) || (base == 0x800) || (base == 0xa00)) && (real < sizeof(m_mainMemoryMap))) { - return m_mainMemoryMap[real] & mask; - } else if ((base == 0x1f8) && (real < sizeof(m_scratchPadMap))) { - return m_scratchPadMap[real] & mask; - } else if ((base == 0xbfc) && (shortReal < sizeof(m_biosMemoryMap))) { - return m_biosMemoryMap[shortReal] & mask; + PSXAddress addr(normalizeAddress(address)); + + switch (addr.type) { + case PSXAddress::Type::RAM: + if (addr.physical < sizeof(m_mainMemoryMap)) { + return m_mainMemoryMap[addr.physical] & mask; + } + break; + case PSXAddress::Type::ScratchPad: + if (addr.physical < sizeof(m_scratchPadMap)) { + return m_scratchPadMap[addr.physical] & mask; + } + break; + case PSXAddress::Type::ROM: + if (addr.physical < sizeof(m_biosMemoryMap)) { + return m_biosMemoryMap[addr.physical] & mask; + } + break; } return false; } void PCSX::Debug::process(uint32_t oldPC, uint32_t newPC, uint32_t oldCode, uint32_t newCode, bool linked) { + const auto& regs = g_emulator->m_cpu->m_regs; const uint32_t basic = newCode >> 26; const bool isAnyLoadOrStore = (basic >= 0x20) && (basic < 0x3b); - const auto& regs = g_emulator->m_cpu->m_regs; + const bool isJAL = basic == 3; + const bool isJR = (basic == 0) && ((newCode & 0x3f) == 8); + const bool isJALR = (basic == 0) && ((newCode & 0x3f) == 9); + const bool isLB = basic == 0x20; + const bool isLH = basic == 0x21; + const bool isLWL = basic == 0x22; + const bool isLW = basic == 0x23; + const bool isLBU = basic == 0x24; + const bool isLHU = basic == 0x25; + const bool isLWR = basic == 0x26; + const bool isSB = basic == 0x28; + const bool isSH = basic == 0x29; + const bool isSWL = basic == 0x2a; + const bool isSW = basic == 0x2b; + const bool isSWR = basic == 0x2e; + const bool isLWC2 = basic == 0x32; + const bool isSWC2 = basic == 0x3a; + const bool isLoad = isLB || isLBU || isLH || isLHU || isLW || isLWL || isLWR || isLWC2; + const bool isStore = isSB || isSH || isSW || isSWL || isSWR || isSWC2; + const bool wasInKernel = isInKernel(oldPC); + const bool isInKernelNow = isInKernel(newPC); + const uint32_t target = (newCode & 0x03ffffff) * 4 + (newPC & 0xf0000000); + const bool isTargetInKernel = isInKernel(target); + const uint32_t rd = (newCode >> 11) & 0x1f; + uint32_t offset = regs.GPR.r[(newCode >> 21) & 0x1f] + int16_t(newCode); + const bool offsetIsInKernel = isInKernel(offset, false); + const bool isJRToRA = isJR && (rd == 31); + const uint32_t oldPCBase = normalizeAddress(oldPC) & ~0xe0000000; + const uint32_t newPCBase = normalizeAddress(newPC) & ~0xe0000000; + const uint32_t targetBase = normalizeAddress(target) & ~0xe0000000; checkBP(newPC, BreakpointType::Exec, 4); if (m_breakmp_e && !isMapMarked(newPC, MAP_EXEC)) { triggerBP(nullptr, newPC, 4, _("Execution map")); } if (m_mapping_e) { - const bool isJAL = basic == 3; - const bool isJALR = (basic == 0) && ((newCode & 0x3F) == 9); - const uint32_t target = (newCode & 0x03ffffff) * 4 + (newPC & 0xf0000000); - const uint32_t rd = (newCode >> 11) & 0x1f; markMap(newPC, MAP_EXEC); if (isJAL) markMap(target, MAP_EXEC_JAL); if (isJALR) markMap(regs.GPR.r[rd], MAP_EXEC_JAL); } + // Are we jumping from a non-kernel address to a kernel address which: + // - is not a jr to $ra (aka a return from a callback) + // - is not a jump to 0xa0 / 0xb0 / 0xc0 (aka the syscall gates) + // - is not going to the break or exception handler + if ((isJR || isJALR) && !wasInKernel && isTargetInKernel && !isJRToRA && (targetBase != 0x40) && + (targetBase != 0x80) && (targetBase != 0xa0) && (targetBase != 0xb0) && (targetBase != 0xc0)) { + if (m_checkKernel) { + g_system->printf(_("Kernel checker: Jump from 0x%08x to 0x%08x\n"), oldPC, targetBase); + g_system->pause(); + } + } + if (isAnyLoadOrStore) { - const bool isLB = basic == 0x20; - const bool isLH = basic == 0x21; - const bool isLWL = basic == 0x22; - const bool isLW = (basic == 0x23) || (basic == 0x32); - const bool isLBU = basic == 0x24; - const bool isLHU = basic == 0x25; - const bool isLWR = basic == 0x26; - const bool isSB = basic == 0x28; - const bool isSH = basic == 0x29; - const bool isSWL = basic == 0x2a; - const bool isSW = (basic == 0x2b) || (basic == 0x3a); - const bool isSWR = basic == 0x2e; - const bool isLWC2 = basic == 0x32; - const bool isSWC2 = basic == 0x3a; - uint32_t offset = regs.GPR.r[(newCode >> 21) & 0x1f] + int16_t(newCode); if (isLWL || isLWR || isSWR || isSWL) offset &= ~3; if (isLB || isLBU) { checkBP(offset, BreakpointType::Read, 1); @@ -157,6 +212,19 @@ void PCSX::Debug::process(uint32_t oldPC, uint32_t newPC, uint32_t oldCode, uint } if (m_mapping_w32) markMap(offset, MAP_W32); } + // Are we accessing a kernel address from a non-kernel address, while not in IRQ? + if (!g_emulator->m_cpu->m_inISR && offsetIsInKernel && !wasInKernel) { + if (m_checkKernel) { + if (isLoad) { + g_system->printf(_("Kernel checker: Reading %08x from %08x\n"), offset, oldPC); + g_system->pause(); + } else { + g_system->printf(_("Kernel checker: Writing to %08x from %08x\n"), offset, oldPC); + g_system->pause(); + } + g_system->pause(); + } + } } if (m_step == STEP_NONE) return; diff --git a/src/core/debug.h b/src/core/debug.h index 6039511cf..105e4053e 100644 --- a/src/core/debug.h +++ b/src/core/debug.h @@ -32,7 +32,9 @@ namespace PCSX { class Debug { public: - uint32_t normalizeAddress(uint32_t address); + Debug(); + static uint32_t normalizeAddress(uint32_t address); + static bool isInKernel(uint32_t address, bool biosIsKernel = true); static inline std::function s_breakpoint_type_names[] = {l_("Exec"), l_("Read"), l_("Write")}; enum class BreakpointType { Exec, Read, Write }; @@ -118,6 +120,7 @@ class Debug { bool m_breakmp_e = false; bool m_breakmp_r8 = false, m_breakmp_r16 = false, m_breakmp_r32 = false; bool m_breakmp_w8 = false, m_breakmp_w16 = false, m_breakmp_w32 = false; + bool m_checkKernel = false; void clearMaps() { memset(m_mainMemoryMap, 0, sizeof(m_mainMemoryMap)); @@ -179,6 +182,7 @@ class Debug { bool m_wasInISR = false; Breakpoint* m_lastBP = nullptr; std::optional> m_scheduledCop0; + EventBus::Listener m_listener; }; } // namespace PCSX diff --git a/src/core/psxhw.cc b/src/core/psxhw.cc index 700db0bf3..0e523ad81 100644 --- a/src/core/psxhw.cc +++ b/src/core/psxhw.cc @@ -22,6 +22,7 @@ #include #include "core/cdrom.h" +#include "core/debug.h" #include "core/gpu.h" #include "core/logger.h" #include "core/mdec.h" @@ -117,6 +118,9 @@ uint8_t PCSX::HW::read8(uint32_t add) { case 0x1f802083: hard = 0x58; break; + case 0x1f802088: + hard = g_emulator->m_debug->m_checkKernel; + break; default: hard = g_emulator->m_mem->m_hard[hwadd & 0xffff]; PSXHW_LOG("*Unknown 8bit read at address %x\n", add); @@ -441,6 +445,9 @@ void PCSX::HW::write8(uint32_t add, uint32_t rawvalue) { while (top != L.gettop()) L.pop(); } break; + case 0x1f802088: + g_emulator->m_debug->m_checkKernel = value; + break; default: if (addressInRegisterSpace(hwadd)) { diff --git a/src/mips/common/hardware/pcsxhw.h b/src/mips/common/hardware/pcsxhw.h index 0fb58ccd8..33f67830f 100644 --- a/src/mips/common/hardware/pcsxhw.h +++ b/src/mips/common/hardware/pcsxhw.h @@ -33,5 +33,7 @@ static __inline__ void pcsx_debugbreak() { *((volatile char* const)0x1f802081) = static __inline__ void pcsx_execSlot(uint8_t slot) { *((volatile uint8_t* const)0x1f802081) = slot; } static __inline__ void pcsx_exit(int code) { *((volatile int16_t* const)0x1f802082) = code; } static __inline__ void pcsx_message(const char* msg) { *((volatile const char** const)0x1f802084) = msg; } +static __inline__ void pcsx_checkKernel(int enable) { *((volatile char*)0x1f802088) = enable; } +static __inline__ int pcsx_isCheckingKernel() { return *((volatile char* const)0x1f802088) != 0; } static __inline__ int pcsx_present() { return *((volatile uint32_t* const)0x1f802080) == 0x58534350; } diff --git a/src/mips/psyqo/src/kernel.cpp b/src/mips/psyqo/src/kernel.cpp index e5b59a1e6..7cce07358 100644 --- a/src/mips/psyqo/src/kernel.cpp +++ b/src/mips/psyqo/src/kernel.cpp @@ -319,6 +319,7 @@ void psyqo::Kernel::Internal::prepare(Application& application) { KernelData* const events = reinterpret_cast(0x120); __builtin_memset(handlers->data, 0, handlers->size); __builtin_memset(events->data, 0, events->size); + pcsx_checkKernel(1); syscall_setDefaultExceptionJmpBuf(); syscall_enqueueSyscallHandler(0); syscall_enqueueIrqHandler(3); diff --git a/src/supportpsx/memory.h b/src/supportpsx/memory.h new file mode 100644 index 000000000..a5b38bb79 --- /dev/null +++ b/src/supportpsx/memory.h @@ -0,0 +1,130 @@ +/*************************************************************************** + * Copyright (C) 2025 PCSX-Redux authors * + * * + * 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; either version 2 of the License, or * + * (at your option) any later version. * + * * + * 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 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * + ***************************************************************************/ + +#include + +#include + +namespace PCSX { + +struct PSXAddress { + explicit PSXAddress(uint32_t virt) { + physical = virt & 0x1fffffff; + if (virt == 0xfffe0130) { + segment = Segment::Internal; + type = Type::Internal; + return; + } + uint32_t top = virt >> 29; + switch (top) { + case 0: + segment = Segment::KUSEG; + break; + case 4: + segment = Segment::KSEG0; + break; + case 5: + segment = Segment::KSEG1; + break; + default: + segment = Segment::Invalid; + return; + break; + } + if (physical < 0x1f000000) { + type = Type::RAM; + } else if (physical < 0x1f800000) { + type = Type::EXP1; + physical -= 0x1f000000; + } else if (physical < 0x1f801000) { + type = Type::ScratchPad; + physical -= 0x1f800000; + } else if (physical < 0x1f802000) { + type = Type::HardwareRegisters; + physical -= 0x1f801000; + } else if (physical < 0x1fa00000) { + type = Type::EXP2; + physical -= 0x1f802000; + } else if (physical < 0x1fc00000) { + physical -= 0x1fa00000; + type = Type::EXP3; + } else { + physical -= 0x1fc00000; + type = Type::ROM; + } + } + std::optional toVirtual() const { + if ((segment == Segment::Internal) && (type == Type::Internal)) { + return 0xfffe0130; + } + if (segment == Segment::Invalid) return {}; + uint32_t ret = physical; + switch (type) { + case Type::RAM: + break; + case Type::EXP1: + ret += 0x1f000000; + break; + case Type::ScratchPad: + ret += 0x1f800000; + break; + case Type::HardwareRegisters: + ret += 0x1f801000; + break; + case Type::EXP2: + ret += 0x1f802000; + break; + case Type::EXP3: + ret += 0x1fa00000; + break; + case Type::ROM: + ret += 0x1fc00000; + break; + } + switch (segment) { + case Segment::KUSEG: + return ret; + case Segment::KSEG0: + return ret | 0x80000000; + case Segment::KSEG1: + return ret | 0xa0000000; + } + } + + uint32_t physical = 0; + enum class Type { + RAM, + EXP1, + ScratchPad, + HardwareRegisters, + EXP2, + EXP3, + ROM, + Internal, + } type = Type::RAM; + enum class Segment { + KUSEG, + KSEG0, + KSEG1, + Internal, + Invalid, + } segment = Segment::Invalid; +}; + +} // namespace PCSX diff --git a/vsprojects/supportpsx/supportpsx.vcxproj b/vsprojects/supportpsx/supportpsx.vcxproj index 2edc60e3e..44ee06a97 100644 --- a/vsprojects/supportpsx/supportpsx.vcxproj +++ b/vsprojects/supportpsx/supportpsx.vcxproj @@ -172,6 +172,7 @@ + diff --git a/vsprojects/supportpsx/supportpsx.vcxproj.filters b/vsprojects/supportpsx/supportpsx.vcxproj.filters index 36bdcbdcd..3ac301b0c 100644 --- a/vsprojects/supportpsx/supportpsx.vcxproj.filters +++ b/vsprojects/supportpsx/supportpsx.vcxproj.filters @@ -104,5 +104,8 @@ Header Files + + Header Files + \ No newline at end of file