diff --git a/core/debug/debug_agent.h b/core/debug/debug_agent.h index 36c9f0286..f43d3df4d 100644 --- a/core/debug/debug_agent.h +++ b/core/debug/debug_agent.h @@ -85,11 +85,20 @@ public: void step() { - bool restoreBreakpoint = removeMatchpoint(0, Sh4cntx.pc, 2); + bool restoreBreakpoint = removeMatchpoint(Breakpoint::Type::BP_TYPE_SOFTWARE_BREAK, Sh4cntx.pc, 2); u32 savedPc = Sh4cntx.pc; emu.step(); if (restoreBreakpoint) - insertMatchpoint(0, savedPc, 2); + insertMatchpoint(Breakpoint::Type::BP_TYPE_SOFTWARE_BREAK, savedPc, 2); + } + + void stepRange(u32 from, u32 to) + { + bool restoreBreakpoint = removeMatchpoint(Breakpoint::Type::BP_TYPE_SOFTWARE_BREAK, Sh4cntx.pc, 2); + u32 savedPc = Sh4cntx.pc; + emu.stepRange(from, to); + if (restoreBreakpoint) + insertMatchpoint(Breakpoint::Type::BP_TYPE_SOFTWARE_BREAK, savedPc, 2); } int readAllRegs(u32 **regs) @@ -200,10 +209,10 @@ public: return false; } // TODO other matchpoint types - if (breakpoints.find(addr) != breakpoints.end()) + if (breakpoints[type].find(addr) != breakpoints[type].end()) return true; - breakpoints[addr] = Breakpoint(type, addr); - breakpoints[addr].savedOp = ReadMem16_nommu(addr); + breakpoints[type][addr] = Breakpoint(type, addr); + breakpoints[type][addr].savedOp = ReadMem16_nommu(addr); WriteMem16_nommu(addr, 0xC308); // trapa #0x20 return true; } @@ -213,11 +222,11 @@ public: WARN_LOG(COMMON, "removeMatchpoint: length != 2: %d", len); return false; } - auto it = breakpoints.find(addr); - if (it == breakpoints.end()) + auto it = breakpoints[type].find(addr); + if (it == breakpoints[type].end()) return false; WriteMem16_nommu(addr, it->second.savedOp); - breakpoints.erase(it); + breakpoints[type].erase(it); return true; } @@ -319,12 +328,22 @@ public: u32 exception = 0; struct Breakpoint { + enum Type + { + BP_TYPE_SOFTWARE_BREAK, + BP_TYPE_HARDWARE_BREAK, + BP_TYPE_WRITE_WATCHPOINT, + BP_TYPE_READ_WATCHPOINT, + BP_TYPE_ACCESS_WATCHPOINT, + BP_TYPE_COUNT + }; + Breakpoint() = default; Breakpoint(u16 type, u32 addr) : addr(addr), type(type) { } u32 addr = 0; u16 type = 0; u16 savedOp = 0; }; - std::map breakpoints; + std::map breakpoints[Breakpoint::Type::BP_TYPE_COUNT]; std::vector> stack; }; diff --git a/core/debug/gdb_server.cpp b/core/debug/gdb_server.cpp index a8e9941f2..100a05698 100644 --- a/core/debug/gdb_server.cpp +++ b/core/debug/gdb_server.cpp @@ -535,9 +535,13 @@ private: sendPacket(""); } else if (pkt.rfind("qSupported", 0) == 0) + { // Tell the remote stub about features supported by GDB, // and query the stub for features it supports - sendPacket("PacketSize=10000"); + char qsupported[128]; + sprintf_s(qsupported, 128, "PacketSize=%i;vContSupported+", MAX_PACKET_LEN); + sendPacket(qsupported); + } else if (pkt.rfind("qSymbol:", 0) == 0) // Notify the target that GDB is prepared to serve symbol lookup requests sendPacket("OK"); @@ -590,11 +594,41 @@ private: if (pkt.rfind("vAttach;", 0) == 0) sendPacket("S05"); else if (pkt.rfind("vCont?", 0) == 0) - // not supported - sendPacket("vCont;s;c"); + // supported vCont actions - (c)ontinue, (C)ontinue with signal, (s)tep, (S)tep with signal, (r)ange-step + sendPacket("vCont;c;C;s;S;t;r"); else if (pkt.rfind("vCont", 0) == 0) - // not supported - WARN_LOG(COMMON, "vCont not supported %s", pkt.c_str()); + { + std::string vContCmd = pkt.substr(strlen("vCont;")); + switch (vContCmd[0]) + { + case 'c': + case 'C': + sendContinue(vContCmd); + break; + case 's': + step(EXCEPT_NONE); + break; + case 'S': + step(); + case 'r': + { + u32 from, to; + if (sscanf(vContCmd.c_str(), "r%x,%x", &from, &to) == 2) + { + stepRange(from, to); + } + else + { + WARN_LOG(COMMON, "Unsupported vCont:r format %s", pkt.c_str()); + sendContinue("c"); + } + + break; + } + default: + WARN_LOG(COMMON, "vCont action not supported %s", pkt[6], pkt.c_str()); + } + } else if (pkt.rfind("vFile:", 0) == 0) // not supported sendPacket(""); @@ -637,6 +671,13 @@ private: sendPacket("S05"); } + void stepRange(u32 from, u32 to) + { + sendPacket("OK"); + agent.stepRange(from, to); + sendPacket("S05"); + } + void insertMatchpoint(const std::string& pkt) { u32 type; @@ -647,22 +688,22 @@ private: sendPacket("E01"); } switch (type) { - case 0: // soft bp + case DebugAgent::Breakpoint::Type::BP_TYPE_SOFTWARE_BREAK: // soft bp if (agent.insertMatchpoint(0, addr, len)) sendPacket("OK"); else sendPacket("E01"); break; - case 1: // hardware bp + case DebugAgent::Breakpoint::Type::BP_TYPE_HARDWARE_BREAK: // hardware bp sendPacket(""); break; - case 2: // write watchpoint + case DebugAgent::Breakpoint::Type::BP_TYPE_WRITE_WATCHPOINT: // write watchpoint sendPacket(""); break; - case 3: // read watchpoint + case DebugAgent::Breakpoint::Type::BP_TYPE_READ_WATCHPOINT: // read watchpoint sendPacket(""); break; - case 4: // access watchpoint + case DebugAgent::Breakpoint::Type::BP_TYPE_ACCESS_WATCHPOINT: // access watchpoint sendPacket(""); break; default: diff --git a/core/emulator.cpp b/core/emulator.cpp index d31b2140f..da2bcf6c0 100644 --- a/core/emulator.cpp +++ b/core/emulator.cpp @@ -587,8 +587,16 @@ void Emulator::runInternal() { if (singleStep) { - singleStep = false; sh4_cpu.Step(); + singleStep = false; + } + else if(stepRangeTo != 0) + { + while (Sh4cntx.pc >= stepRangeFrom && Sh4cntx.pc <= stepRangeTo) + sh4_cpu.Step(); + + stepRangeFrom = 0; + stepRangeTo = 0; } else { @@ -652,7 +660,8 @@ void Emulator::stop() { rend_cancel_emu_wait(); try { auto future = threadResult; - future.get(); + if(future.valid()) + future.get(); } catch (const FlycastException& e) { WARN_LOG(COMMON, "%s", e.what()); } @@ -719,6 +728,14 @@ void Emulator::step() stop(); } +void Emulator::stepRange(u32 from, u32 to) +{ + stepRangeFrom = from; + stepRangeTo = to; + start(); + stop(); +} + void dc_loadstate(Deserializer& deser) { custom_texture.Terminate(); @@ -826,7 +843,7 @@ void Emulator::start() InitAudio(); try { - while (state == Running) + while (state == Running || singleStep || stepRangeTo != 0) { startTime = sh4_sched_now64(); renderTimeout = false; @@ -855,9 +872,13 @@ void Emulator::start() bool Emulator::checkStatus() { try { - if (threadResult.wait_for(std::chrono::seconds(0)) == std::future_status::timeout) - return true; - threadResult.get(); + if (threadResult.valid()) + { + auto result = threadResult.wait_for(std::chrono::seconds(0)); + if (result == std::future_status::timeout) + return true; + threadResult.get(); + } return false; } catch (...) { EventManager::event(Event::Pause); diff --git a/core/emulator.h b/core/emulator.h index 5df134085..a205057e2 100644 --- a/core/emulator.h +++ b/core/emulator.h @@ -130,6 +130,10 @@ public: * Execute a single instruction. */ void step(); + /** + * Execute instructions while PC is with range. + */ + void stepRange(u32 from, u32 to); /** * Return whether the emulator is currently running. */ @@ -171,6 +175,8 @@ private: bool singleStep = false; u64 startTime = 0; bool renderTimeout = false; + u32 stepRangeFrom = 0; + u32 stepRangeTo = 0; }; extern Emulator emu;