diff options
| author | 2022-06-01 00:19:49 -0400 | |
|---|---|---|
| committer | 2022-06-01 00:19:49 -0400 | |
| commit | de2f2e5140eb85311e0fd844c580d6726adf7e03 (patch) | |
| tree | 10f0e96a0689b2edb823f5833ff388ac9c645229 /src/core/debugger/gdbstub_arch.cpp | |
| parent | Merge pull request #8401 from zhaobot/tx-update-20220601034505 (diff) | |
| parent | core/debugger: Implement new GDB stub debugger (diff) | |
| download | yuzu-de2f2e5140eb85311e0fd844c580d6726adf7e03.tar.gz yuzu-de2f2e5140eb85311e0fd844c580d6726adf7e03.tar.xz yuzu-de2f2e5140eb85311e0fd844c580d6726adf7e03.zip | |
Merge pull request #8394 from liamwhite/debugger
core/debugger: Implement new GDB stub debugger
Diffstat (limited to 'src/core/debugger/gdbstub_arch.cpp')
| -rw-r--r-- | src/core/debugger/gdbstub_arch.cpp | 406 |
1 files changed, 406 insertions, 0 deletions
diff --git a/src/core/debugger/gdbstub_arch.cpp b/src/core/debugger/gdbstub_arch.cpp new file mode 100644 index 000000000..99e3893a9 --- /dev/null +++ b/src/core/debugger/gdbstub_arch.cpp | |||
| @@ -0,0 +1,406 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/hex_util.h" | ||
| 5 | #include "core/debugger/gdbstub_arch.h" | ||
| 6 | #include "core/hle/kernel/k_thread.h" | ||
| 7 | |||
| 8 | namespace Core { | ||
| 9 | |||
| 10 | template <typename T> | ||
| 11 | static T HexToValue(std::string_view hex) { | ||
| 12 | static_assert(std::is_trivially_copyable_v<T>); | ||
| 13 | T value{}; | ||
| 14 | const auto mem{Common::HexStringToVector(hex, false)}; | ||
| 15 | std::memcpy(&value, mem.data(), std::min(mem.size(), sizeof(T))); | ||
| 16 | return value; | ||
| 17 | } | ||
| 18 | |||
| 19 | template <typename T> | ||
| 20 | static std::string ValueToHex(const T value) { | ||
| 21 | static_assert(std::is_trivially_copyable_v<T>); | ||
| 22 | std::array<u8, sizeof(T)> mem{}; | ||
| 23 | std::memcpy(mem.data(), &value, sizeof(T)); | ||
| 24 | return Common::HexToString(mem); | ||
| 25 | } | ||
| 26 | |||
| 27 | template <typename T> | ||
| 28 | static T GetSIMDRegister(const std::array<u32, 64>& simd_regs, size_t offset) { | ||
| 29 | static_assert(std::is_trivially_copyable_v<T>); | ||
| 30 | T value{}; | ||
| 31 | std::memcpy(&value, reinterpret_cast<const u8*>(simd_regs.data()) + sizeof(T) * offset, | ||
| 32 | sizeof(T)); | ||
| 33 | return value; | ||
| 34 | } | ||
| 35 | |||
| 36 | template <typename T> | ||
| 37 | static void PutSIMDRegister(std::array<u32, 64>& simd_regs, size_t offset, const T value) { | ||
| 38 | static_assert(std::is_trivially_copyable_v<T>); | ||
| 39 | std::memcpy(reinterpret_cast<u8*>(simd_regs.data()) + sizeof(T) * offset, &value, sizeof(T)); | ||
| 40 | } | ||
| 41 | |||
| 42 | // For sample XML files see the GDB source /gdb/features | ||
| 43 | // This XML defines what the registers are for this specific ARM device | ||
| 44 | std::string GDBStubA64::GetTargetXML() const { | ||
| 45 | constexpr const char* target_xml = | ||
| 46 | R"(<?xml version="1.0"?> | ||
| 47 | <!DOCTYPE target SYSTEM "gdb-target.dtd"> | ||
| 48 | <target version="1.0"> | ||
| 49 | <feature name="org.gnu.gdb.aarch64.core"> | ||
| 50 | <reg name="x0" bitsize="64"/> | ||
| 51 | <reg name="x1" bitsize="64"/> | ||
| 52 | <reg name="x2" bitsize="64"/> | ||
| 53 | <reg name="x3" bitsize="64"/> | ||
| 54 | <reg name="x4" bitsize="64"/> | ||
| 55 | <reg name="x5" bitsize="64"/> | ||
| 56 | <reg name="x6" bitsize="64"/> | ||
| 57 | <reg name="x7" bitsize="64"/> | ||
| 58 | <reg name="x8" bitsize="64"/> | ||
| 59 | <reg name="x9" bitsize="64"/> | ||
| 60 | <reg name="x10" bitsize="64"/> | ||
| 61 | <reg name="x11" bitsize="64"/> | ||
| 62 | <reg name="x12" bitsize="64"/> | ||
| 63 | <reg name="x13" bitsize="64"/> | ||
| 64 | <reg name="x14" bitsize="64"/> | ||
| 65 | <reg name="x15" bitsize="64"/> | ||
| 66 | <reg name="x16" bitsize="64"/> | ||
| 67 | <reg name="x17" bitsize="64"/> | ||
| 68 | <reg name="x18" bitsize="64"/> | ||
| 69 | <reg name="x19" bitsize="64"/> | ||
| 70 | <reg name="x20" bitsize="64"/> | ||
| 71 | <reg name="x21" bitsize="64"/> | ||
| 72 | <reg name="x22" bitsize="64"/> | ||
| 73 | <reg name="x23" bitsize="64"/> | ||
| 74 | <reg name="x24" bitsize="64"/> | ||
| 75 | <reg name="x25" bitsize="64"/> | ||
| 76 | <reg name="x26" bitsize="64"/> | ||
| 77 | <reg name="x27" bitsize="64"/> | ||
| 78 | <reg name="x28" bitsize="64"/> | ||
| 79 | <reg name="x29" bitsize="64"/> | ||
| 80 | <reg name="x30" bitsize="64"/> | ||
| 81 | <reg name="sp" bitsize="64" type="data_ptr"/> | ||
| 82 | <reg name="pc" bitsize="64" type="code_ptr"/> | ||
| 83 | <flags id="pstate_flags" size="4"> | ||
| 84 | <field name="SP" start="0" end="0"/> | ||
| 85 | <field name="" start="1" end="1"/> | ||
| 86 | <field name="EL" start="2" end="3"/> | ||
| 87 | <field name="nRW" start="4" end="4"/> | ||
| 88 | <field name="" start="5" end="5"/> | ||
| 89 | <field name="F" start="6" end="6"/> | ||
| 90 | <field name="I" start="7" end="7"/> | ||
| 91 | <field name="A" start="8" end="8"/> | ||
| 92 | <field name="D" start="9" end="9"/> | ||
| 93 | <field name="IL" start="20" end="20"/> | ||
| 94 | <field name="SS" start="21" end="21"/> | ||
| 95 | <field name="V" start="28" end="28"/> | ||
| 96 | <field name="C" start="29" end="29"/> | ||
| 97 | <field name="Z" start="30" end="30"/> | ||
| 98 | <field name="N" start="31" end="31"/> | ||
| 99 | </flags> | ||
| 100 | <reg name="pstate" bitsize="32" type="pstate_flags"/> | ||
| 101 | </feature> | ||
| 102 | <feature name="org.gnu.gdb.aarch64.fpu"> | ||
| 103 | </feature> | ||
| 104 | </target>)"; | ||
| 105 | |||
| 106 | return target_xml; | ||
| 107 | } | ||
| 108 | |||
| 109 | std::string GDBStubA64::RegRead(const Kernel::KThread* thread, size_t id) const { | ||
| 110 | if (!thread) { | ||
| 111 | return ""; | ||
| 112 | } | ||
| 113 | |||
| 114 | const auto& context{thread->GetContext64()}; | ||
| 115 | const auto& gprs{context.cpu_registers}; | ||
| 116 | const auto& fprs{context.vector_registers}; | ||
| 117 | |||
| 118 | if (id <= SP_REGISTER) { | ||
| 119 | return ValueToHex(gprs[id]); | ||
| 120 | } else if (id == PC_REGISTER) { | ||
| 121 | return ValueToHex(context.pc); | ||
| 122 | } else if (id == PSTATE_REGISTER) { | ||
| 123 | return ValueToHex(context.pstate); | ||
| 124 | } else if (id >= Q0_REGISTER && id < FPCR_REGISTER) { | ||
| 125 | return ValueToHex(fprs[id - Q0_REGISTER]); | ||
| 126 | } else if (id == FPCR_REGISTER) { | ||
| 127 | return ValueToHex(context.fpcr); | ||
| 128 | } else if (id == FPSR_REGISTER) { | ||
| 129 | return ValueToHex(context.fpsr); | ||
| 130 | } else { | ||
| 131 | return ""; | ||
| 132 | } | ||
| 133 | } | ||
| 134 | |||
| 135 | void GDBStubA64::RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const { | ||
| 136 | if (!thread) { | ||
| 137 | return; | ||
| 138 | } | ||
| 139 | |||
| 140 | auto& context{thread->GetContext64()}; | ||
| 141 | |||
| 142 | if (id <= SP_REGISTER) { | ||
| 143 | context.cpu_registers[id] = HexToValue<u64>(value); | ||
| 144 | } else if (id == PC_REGISTER) { | ||
| 145 | context.pc = HexToValue<u64>(value); | ||
| 146 | } else if (id == PSTATE_REGISTER) { | ||
| 147 | context.pstate = HexToValue<u32>(value); | ||
| 148 | } else if (id >= Q0_REGISTER && id < FPCR_REGISTER) { | ||
| 149 | context.vector_registers[id - Q0_REGISTER] = HexToValue<u128>(value); | ||
| 150 | } else if (id == FPCR_REGISTER) { | ||
| 151 | context.fpcr = HexToValue<u32>(value); | ||
| 152 | } else if (id == FPSR_REGISTER) { | ||
| 153 | context.fpsr = HexToValue<u32>(value); | ||
| 154 | } | ||
| 155 | } | ||
| 156 | |||
| 157 | std::string GDBStubA64::ReadRegisters(const Kernel::KThread* thread) const { | ||
| 158 | std::string output; | ||
| 159 | |||
| 160 | for (size_t reg = 0; reg <= FPCR_REGISTER; reg++) { | ||
| 161 | output += RegRead(thread, reg); | ||
| 162 | } | ||
| 163 | |||
| 164 | return output; | ||
| 165 | } | ||
| 166 | |||
| 167 | void GDBStubA64::WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const { | ||
| 168 | for (size_t i = 0, reg = 0; reg <= FPCR_REGISTER; reg++) { | ||
| 169 | if (reg <= SP_REGISTER || reg == PC_REGISTER) { | ||
| 170 | RegWrite(thread, reg, register_data.substr(i, 16)); | ||
| 171 | i += 16; | ||
| 172 | } else if (reg == PSTATE_REGISTER || reg == FPCR_REGISTER || reg == FPSR_REGISTER) { | ||
| 173 | RegWrite(thread, reg, register_data.substr(i, 8)); | ||
| 174 | i += 8; | ||
| 175 | } else if (reg >= Q0_REGISTER && reg < FPCR_REGISTER) { | ||
| 176 | RegWrite(thread, reg, register_data.substr(i, 32)); | ||
| 177 | i += 32; | ||
| 178 | } | ||
| 179 | } | ||
| 180 | } | ||
| 181 | |||
| 182 | std::string GDBStubA64::ThreadStatus(const Kernel::KThread* thread, u8 signal) const { | ||
| 183 | return fmt::format("T{:02x}{:02x}:{};{:02x}:{};{:02x}:{};thread:{:x};", signal, PC_REGISTER, | ||
| 184 | RegRead(thread, PC_REGISTER), SP_REGISTER, RegRead(thread, SP_REGISTER), | ||
| 185 | LR_REGISTER, RegRead(thread, LR_REGISTER), thread->GetThreadID()); | ||
| 186 | } | ||
| 187 | |||
| 188 | u32 GDBStubA64::BreakpointInstruction() const { | ||
| 189 | // A64: brk #0 | ||
| 190 | return 0xd4200000; | ||
| 191 | } | ||
| 192 | |||
| 193 | std::string GDBStubA32::GetTargetXML() const { | ||
| 194 | constexpr const char* target_xml = | ||
| 195 | R"(<?xml version="1.0"?> | ||
| 196 | <!DOCTYPE target SYSTEM "gdb-target.dtd"> | ||
| 197 | <target version="1.0"> | ||
| 198 | <feature name="org.gnu.gdb.arm.core"> | ||
| 199 | <reg name="r0" bitsize="32" type="uint32"/> | ||
| 200 | <reg name="r1" bitsize="32" type="uint32"/> | ||
| 201 | <reg name="r2" bitsize="32" type="uint32"/> | ||
| 202 | <reg name="r3" bitsize="32" type="uint32"/> | ||
| 203 | <reg name="r4" bitsize="32" type="uint32"/> | ||
| 204 | <reg name="r5" bitsize="32" type="uint32"/> | ||
| 205 | <reg name="r6" bitsize="32" type="uint32"/> | ||
| 206 | <reg name="r7" bitsize="32" type="uint32"/> | ||
| 207 | <reg name="r8" bitsize="32" type="uint32"/> | ||
| 208 | <reg name="r9" bitsize="32" type="uint32"/> | ||
| 209 | <reg name="r10" bitsize="32" type="uint32"/> | ||
| 210 | <reg name="r11" bitsize="32" type="uint32"/> | ||
| 211 | <reg name="r12" bitsize="32" type="uint32"/> | ||
| 212 | <reg name="sp" bitsize="32" type="data_ptr"/> | ||
| 213 | <reg name="lr" bitsize="32" type="code_ptr"/> | ||
| 214 | <reg name="pc" bitsize="32" type="code_ptr"/> | ||
| 215 | <!-- The CPSR is register 25, rather than register 16, because | ||
| 216 | the FPA registers historically were placed between the PC | ||
| 217 | and the CPSR in the "g" packet. --> | ||
| 218 | <reg name="cpsr" bitsize="32" regnum="25"/> | ||
| 219 | </feature> | ||
| 220 | <feature name="org.gnu.gdb.arm.vfp"> | ||
| 221 | <vector id="neon_uint8x8" type="uint8" count="8"/> | ||
| 222 | <vector id="neon_uint16x4" type="uint16" count="4"/> | ||
| 223 | <vector id="neon_uint32x2" type="uint32" count="2"/> | ||
| 224 | <vector id="neon_float32x2" type="ieee_single" count="2"/> | ||
| 225 | <union id="neon_d"> | ||
| 226 | <field name="u8" type="neon_uint8x8"/> | ||
| 227 | <field name="u16" type="neon_uint16x4"/> | ||
| 228 | <field name="u32" type="neon_uint32x2"/> | ||
| 229 | <field name="u64" type="uint64"/> | ||
| 230 | <field name="f32" type="neon_float32x2"/> | ||
| 231 | <field name="f64" type="ieee_double"/> | ||
| 232 | </union> | ||
| 233 | <vector id="neon_uint8x16" type="uint8" count="16"/> | ||
| 234 | <vector id="neon_uint16x8" type="uint16" count="8"/> | ||
| 235 | <vector id="neon_uint32x4" type="uint32" count="4"/> | ||
| 236 | <vector id="neon_uint64x2" type="uint64" count="2"/> | ||
| 237 | <vector id="neon_float32x4" type="ieee_single" count="4"/> | ||
| 238 | <vector id="neon_float64x2" type="ieee_double" count="2"/> | ||
| 239 | <union id="neon_q"> | ||
| 240 | <field name="u8" type="neon_uint8x16"/> | ||
| 241 | <field name="u16" type="neon_uint16x8"/> | ||
| 242 | <field name="u32" type="neon_uint32x4"/> | ||
| 243 | <field name="u64" type="neon_uint64x2"/> | ||
| 244 | <field name="f32" type="neon_float32x4"/> | ||
| 245 | <field name="f64" type="neon_float64x2"/> | ||
| 246 | </union> | ||
| 247 | <reg name="d0" bitsize="64" type="neon_d" regnum="32"/> | ||
| 248 | <reg name="d1" bitsize="64" type="neon_d"/> | ||
| 249 | <reg name="d2" bitsize="64" type="neon_d"/> | ||
| 250 | <reg name="d3" bitsize="64" type="neon_d"/> | ||
| 251 | <reg name="d4" bitsize="64" type="neon_d"/> | ||
| 252 | <reg name="d5" bitsize="64" type="neon_d"/> | ||
| 253 | <reg name="d6" bitsize="64" type="neon_d"/> | ||
| 254 | <reg name="d7" bitsize="64" type="neon_d"/> | ||
| 255 | <reg name="d8" bitsize="64" type="neon_d"/> | ||
| 256 | <reg name="d9" bitsize="64" type="neon_d"/> | ||
| 257 | <reg name="d10" bitsize="64" type="neon_d"/> | ||
| 258 | <reg name="d11" bitsize="64" type="neon_d"/> | ||
| 259 | <reg name="d12" bitsize="64" type="neon_d"/> | ||
| 260 | <reg name="d13" bitsize="64" type="neon_d"/> | ||
| 261 | <reg name="d14" bitsize="64" type="neon_d"/> | ||
| 262 | <reg name="d15" bitsize="64" type="neon_d"/> | ||
| 263 | <reg name="d16" bitsize="64" type="neon_d"/> | ||
| 264 | <reg name="d17" bitsize="64" type="neon_d"/> | ||
| 265 | <reg name="d18" bitsize="64" type="neon_d"/> | ||
| 266 | <reg name="d19" bitsize="64" type="neon_d"/> | ||
| 267 | <reg name="d20" bitsize="64" type="neon_d"/> | ||
| 268 | <reg name="d21" bitsize="64" type="neon_d"/> | ||
| 269 | <reg name="d22" bitsize="64" type="neon_d"/> | ||
| 270 | <reg name="d23" bitsize="64" type="neon_d"/> | ||
| 271 | <reg name="d24" bitsize="64" type="neon_d"/> | ||
| 272 | <reg name="d25" bitsize="64" type="neon_d"/> | ||
| 273 | <reg name="d26" bitsize="64" type="neon_d"/> | ||
| 274 | <reg name="d27" bitsize="64" type="neon_d"/> | ||
| 275 | <reg name="d28" bitsize="64" type="neon_d"/> | ||
| 276 | <reg name="d29" bitsize="64" type="neon_d"/> | ||
| 277 | <reg name="d30" bitsize="64" type="neon_d"/> | ||
| 278 | <reg name="d31" bitsize="64" type="neon_d"/> | ||
| 279 | |||
| 280 | <reg name="q0" bitsize="128" type="neon_q" regnum="64"/> | ||
| 281 | <reg name="q1" bitsize="128" type="neon_q"/> | ||
| 282 | <reg name="q2" bitsize="128" type="neon_q"/> | ||
| 283 | <reg name="q3" bitsize="128" type="neon_q"/> | ||
| 284 | <reg name="q4" bitsize="128" type="neon_q"/> | ||
| 285 | <reg name="q5" bitsize="128" type="neon_q"/> | ||
| 286 | <reg name="q6" bitsize="128" type="neon_q"/> | ||
| 287 | <reg name="q7" bitsize="128" type="neon_q"/> | ||
| 288 | <reg name="q8" bitsize="128" type="neon_q"/> | ||
| 289 | <reg name="q9" bitsize="128" type="neon_q"/> | ||
| 290 | <reg name="q10" bitsize="128" type="neon_q"/> | ||
| 291 | <reg name="q10" bitsize="128" type="neon_q"/> | ||
| 292 | <reg name="q12" bitsize="128" type="neon_q"/> | ||
| 293 | <reg name="q13" bitsize="128" type="neon_q"/> | ||
| 294 | <reg name="q14" bitsize="128" type="neon_q"/> | ||
| 295 | <reg name="q15" bitsize="128" type="neon_q"/> | ||
| 296 | |||
| 297 | <reg name="fpscr" bitsize="32" type="int" group="float" regnum="80"/> | ||
| 298 | </feature> | ||
| 299 | </target>)"; | ||
| 300 | |||
| 301 | return target_xml; | ||
| 302 | } | ||
| 303 | |||
| 304 | std::string GDBStubA32::RegRead(const Kernel::KThread* thread, size_t id) const { | ||
| 305 | if (!thread) { | ||
| 306 | return ""; | ||
| 307 | } | ||
| 308 | |||
| 309 | const auto& context{thread->GetContext32()}; | ||
| 310 | const auto& gprs{context.cpu_registers}; | ||
| 311 | const auto& fprs{context.extension_registers}; | ||
| 312 | |||
| 313 | if (id <= PC_REGISTER) { | ||
| 314 | return ValueToHex(gprs[id]); | ||
| 315 | } else if (id == CPSR_REGISTER) { | ||
| 316 | return ValueToHex(context.cpsr); | ||
| 317 | } else if (id >= D0_REGISTER && id < Q0_REGISTER) { | ||
| 318 | const u64 dN{GetSIMDRegister<u64>(fprs, id - D0_REGISTER)}; | ||
| 319 | return ValueToHex(dN); | ||
| 320 | } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) { | ||
| 321 | const u128 qN{GetSIMDRegister<u128>(fprs, id - Q0_REGISTER)}; | ||
| 322 | return ValueToHex(qN); | ||
| 323 | } else if (id == FPSCR_REGISTER) { | ||
| 324 | return ValueToHex(context.fpscr); | ||
| 325 | } else { | ||
| 326 | return ""; | ||
| 327 | } | ||
| 328 | } | ||
| 329 | |||
| 330 | void GDBStubA32::RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const { | ||
| 331 | if (!thread) { | ||
| 332 | return; | ||
| 333 | } | ||
| 334 | |||
| 335 | auto& context{thread->GetContext32()}; | ||
| 336 | auto& fprs{context.extension_registers}; | ||
| 337 | |||
| 338 | if (id <= PC_REGISTER) { | ||
| 339 | context.cpu_registers[id] = HexToValue<u32>(value); | ||
| 340 | } else if (id == CPSR_REGISTER) { | ||
| 341 | context.cpsr = HexToValue<u32>(value); | ||
| 342 | } else if (id >= D0_REGISTER && id < Q0_REGISTER) { | ||
| 343 | PutSIMDRegister(fprs, id - D0_REGISTER, HexToValue<u64>(value)); | ||
| 344 | } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) { | ||
| 345 | PutSIMDRegister(fprs, id - Q0_REGISTER, HexToValue<u128>(value)); | ||
| 346 | } else if (id == FPSCR_REGISTER) { | ||
| 347 | context.fpscr = HexToValue<u32>(value); | ||
| 348 | } | ||
| 349 | } | ||
| 350 | |||
| 351 | std::string GDBStubA32::ReadRegisters(const Kernel::KThread* thread) const { | ||
| 352 | std::string output; | ||
| 353 | |||
| 354 | for (size_t reg = 0; reg <= FPSCR_REGISTER; reg++) { | ||
| 355 | const bool gpr{reg <= PC_REGISTER}; | ||
| 356 | const bool dfpr{reg >= D0_REGISTER && reg < Q0_REGISTER}; | ||
| 357 | const bool qfpr{reg >= Q0_REGISTER && reg < FPSCR_REGISTER}; | ||
| 358 | |||
| 359 | if (!(gpr || dfpr || qfpr || reg == CPSR_REGISTER || reg == FPSCR_REGISTER)) { | ||
| 360 | continue; | ||
| 361 | } | ||
| 362 | |||
| 363 | output += RegRead(thread, reg); | ||
| 364 | } | ||
| 365 | |||
| 366 | return output; | ||
| 367 | } | ||
| 368 | |||
| 369 | void GDBStubA32::WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const { | ||
| 370 | for (size_t i = 0, reg = 0; reg <= FPSCR_REGISTER; reg++) { | ||
| 371 | const bool gpr{reg <= PC_REGISTER}; | ||
| 372 | const bool dfpr{reg >= D0_REGISTER && reg < Q0_REGISTER}; | ||
| 373 | const bool qfpr{reg >= Q0_REGISTER && reg < FPSCR_REGISTER}; | ||
| 374 | |||
| 375 | if (gpr || reg == CPSR_REGISTER || reg == FPSCR_REGISTER) { | ||
| 376 | RegWrite(thread, reg, register_data.substr(i, 8)); | ||
| 377 | i += 8; | ||
| 378 | } else if (dfpr) { | ||
| 379 | RegWrite(thread, reg, register_data.substr(i, 16)); | ||
| 380 | i += 16; | ||
| 381 | } else if (qfpr) { | ||
| 382 | RegWrite(thread, reg, register_data.substr(i, 32)); | ||
| 383 | i += 32; | ||
| 384 | } | ||
| 385 | |||
| 386 | if (reg == PC_REGISTER) { | ||
| 387 | reg = CPSR_REGISTER - 1; | ||
| 388 | } else if (reg == CPSR_REGISTER) { | ||
| 389 | reg = D0_REGISTER - 1; | ||
| 390 | } | ||
| 391 | } | ||
| 392 | } | ||
| 393 | |||
| 394 | std::string GDBStubA32::ThreadStatus(const Kernel::KThread* thread, u8 signal) const { | ||
| 395 | return fmt::format("T{:02x}{:02x}:{};{:02x}:{};{:02x}:{};thread:{:x};", signal, PC_REGISTER, | ||
| 396 | RegRead(thread, PC_REGISTER), SP_REGISTER, RegRead(thread, SP_REGISTER), | ||
| 397 | LR_REGISTER, RegRead(thread, LR_REGISTER), thread->GetThreadID()); | ||
| 398 | } | ||
| 399 | |||
| 400 | u32 GDBStubA32::BreakpointInstruction() const { | ||
| 401 | // A32: trap | ||
| 402 | // T32: trap + b #4 | ||
| 403 | return 0xe7ffdefe; | ||
| 404 | } | ||
| 405 | |||
| 406 | } // namespace Core | ||