src/arm/debugger/debugger.c (view raw)
1/* Copyright (c) 2013-2016 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include <mgba/internal/arm/debugger/debugger.h>
7
8#include <mgba/core/core.h>
9#include <mgba/internal/arm/arm.h>
10#include <mgba/internal/arm/decoder.h>
11#include <mgba/internal/arm/decoder-inlines.h>
12#include <mgba/internal/arm/isa-inlines.h>
13#include <mgba/internal/arm/debugger/memory-debugger.h>
14#include <mgba/internal/debugger/parser.h>
15#include <mgba/internal/debugger/stack-trace.h>
16#include <mgba-util/math.h>
17
18#define FRAME_PRIV(FRAME) ((struct ARMRegisterFile*) FRAME->regs)->cpsr.priv
19
20DEFINE_VECTOR(ARMDebugBreakpointList, struct ARMDebugBreakpoint);
21
22static bool ARMDecodeCombined(struct ARMCore* cpu, struct ARMInstructionInfo* info) {
23 if (cpu->executionMode == MODE_ARM) {
24 ARMDecodeARM(cpu->prefetch[0], info);
25 return true;
26 } else {
27 struct ARMInstructionInfo info2;
28 ARMDecodeThumb(cpu->prefetch[0], info);
29 ARMDecodeThumb(cpu->prefetch[1], &info2);
30 return ARMDecodeThumbCombine(info, &info2, info);
31 }
32}
33
34static bool ARMDebuggerUpdateStackTraceInternal(struct mDebuggerPlatform* d, uint32_t pc) {
35 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
36 struct ARMCore* cpu = debugger->cpu;
37 struct ARMInstructionInfo info;
38 struct mStackTrace* stack = &d->p->stackTrace;
39
40 struct mStackFrame* frame = mStackTraceGetFrame(stack, 0);
41 enum RegisterBank currentStack = ARMSelectBank(cpu->cpsr.priv);
42 if (frame && frame->frameBaseAddress < (uint32_t) cpu->gprs[ARM_SP] && currentStack == ARMSelectBank(FRAME_PRIV(frame))) {
43 // The stack frame has been popped off the stack. This means the function
44 // has been returned from, or that the stack pointer has been otherwise
45 // manipulated. Either way, the function is done executing.
46 bool shouldBreak = debugger->stackTraceMode & STACK_TRACE_BREAK_ON_RETURN;
47 do {
48 shouldBreak = shouldBreak || frame->breakWhenFinished;
49 mStackTracePop(stack);
50 frame = mStackTraceGetFrame(stack, 0);
51 } while (frame && frame->frameBaseAddress < (uint32_t) cpu->gprs[ARM_SP] && currentStack == ARMSelectBank(FRAME_PRIV(frame)));
52 if (shouldBreak) {
53 struct mDebuggerEntryInfo debuggerInfo = {
54 .address = pc,
55 .type.st.traceType = STACK_TRACE_BREAK_ON_RETURN,
56 .pointId = 0
57 };
58 mDebuggerEnter(d->p, DEBUGGER_ENTER_STACK, &debuggerInfo);
59 return true;
60 } else {
61 return false;
62 }
63 }
64
65 bool interrupt = false;
66 bool isWideInstruction = ARMDecodeCombined(cpu, &info);
67 if (!isWideInstruction && info.mnemonic == ARM_MN_BL) {
68 return false;
69 }
70 if (!ARMTestCondition(cpu, info.condition)) {
71 return false;
72 }
73
74 if (_ARMModeHasSPSR(cpu->cpsr.priv)) {
75 struct mStackFrame* irqFrame = mStackTraceGetFrame(stack, 0);
76 // TODO: uint32_t ivtBase = ARMControlRegIsVE(cpu->cp15.r1.c0) ? 0xFFFF0000 : 0x00000000;
77 uint32_t ivtBase = 0x00000000;
78 if (ivtBase <= pc && pc < ivtBase + 0x20 && !(irqFrame && _ARMModeHasSPSR(((struct ARMRegisterFile*) irqFrame->regs)->cpsr.priv))) {
79 // TODO: Potential enhancement opportunity: add break-on-exception mode
80 irqFrame = mStackTracePush(stack, pc, pc, cpu->gprs[ARM_SP], &cpu->regs);
81 irqFrame->interrupt = true;
82 interrupt = true;
83 }
84 }
85
86 if (info.branchType == ARM_BRANCH_NONE && !interrupt) {
87 return false;
88 }
89
90 bool isCall = info.branchType & ARM_BRANCH_LINKED;
91 uint32_t destAddress;
92
93 if (interrupt && !isCall) {
94 // The stack frame was already pushed up above, so there's no
95 // action necessary here, but we still want to check for a
96 // breakpoint down below.
97 //
98 // The first instruction could possibly be a call, which would
99 // need ANOTHER stack frame, so only skip if it's not.
100 destAddress = pc;
101 } else if (info.operandFormat & ARM_OPERAND_MEMORY_1) {
102 // This is most likely ldmia ..., {..., pc}, which is a function return.
103 // To find which stack slot holds the return address, count the number of set bits.
104 int regCount = popcount32(info.op1.immediate);
105 uint32_t baseAddress = cpu->gprs[info.memory.baseReg] + ((regCount - 1) << 2);
106 destAddress = cpu->memory.load32(cpu, baseAddress, NULL);
107 } else if (info.operandFormat & ARM_OPERAND_IMMEDIATE_1) {
108 if (!isCall) {
109 return false;
110 }
111 destAddress = info.op1.immediate + cpu->gprs[ARM_PC];
112 } else if (info.operandFormat & ARM_OPERAND_REGISTER_1) {
113 if (isCall) {
114 destAddress = cpu->gprs[info.op1.reg];
115 } else {
116 bool isExceptionReturn = _ARMModeHasSPSR(cpu->cpsr.priv) && info.affectsCPSR && info.op1.reg == ARM_PC;
117 bool isMovPcLr = (info.operandFormat & ARM_OPERAND_REGISTER_2) && info.op1.reg == ARM_PC && info.op2.reg == ARM_LR;
118 bool isBranch = ARMInstructionIsBranch(info.mnemonic);
119 int reg = (isBranch ? info.op1.reg : info.op2.reg);
120 destAddress = cpu->gprs[reg];
121 if (!isBranch && (info.branchType & ARM_BRANCH_INDIRECT) && info.op1.reg == ARM_PC && info.operandFormat & ARM_OPERAND_MEMORY_2) {
122 uint32_t ptrAddress = ARMResolveMemoryAccess(&info, &cpu->regs, pc);
123 destAddress = cpu->memory.load32(cpu, ptrAddress, NULL);
124 }
125 if (isBranch || (info.op1.reg == ARM_PC && !isMovPcLr)) {
126 // ARMv4 doesn't have the BLX opcode, so it uses an assignment to LR before a BX for that purpose.
127 struct ARMInstructionInfo prevInfo;
128 if (cpu->executionMode == MODE_ARM) {
129 ARMDecodeARM(cpu->memory.load32(cpu, pc - 4, NULL), &prevInfo);
130 } else {
131 ARMDecodeThumb(cpu->memory.load16(cpu, pc - 2, NULL), &prevInfo);
132 }
133 if ((prevInfo.operandFormat & (ARM_OPERAND_REGISTER_1 | ARM_OPERAND_AFFECTED_1)) == (ARM_OPERAND_REGISTER_1 | ARM_OPERAND_AFFECTED_1) && prevInfo.op1.reg == ARM_LR) {
134 isCall = true;
135 } else if ((isBranch ? info.op1.reg : info.op2.reg) == ARM_LR) {
136 isBranch = true;
137 } else if (frame && frame->frameBaseAddress == (uint32_t) cpu->gprs[ARM_SP]) {
138 // A branch to something that isn't LR isn't a standard function return, but it might potentially
139 // be a nonstandard one. As a heuristic, if the stack pointer and the destination address match
140 // where we came from, consider it to be a function return.
141 isBranch = (destAddress > frame->callAddress + 1 && destAddress <= frame->callAddress + 5);
142 } else {
143 isBranch = false;
144 }
145 }
146 if (!isCall && !isBranch && !isExceptionReturn && !isMovPcLr) {
147 return false;
148 }
149 }
150 } else {
151 mLOG(DEBUGGER, ERROR, "Unknown branch operand in stack trace");
152 return false;
153 }
154
155 if (isCall) {
156 int instructionLength = isWideInstruction ? WORD_SIZE_ARM : WORD_SIZE_THUMB;
157 frame = mStackTracePush(stack, pc, destAddress + instructionLength, cpu->gprs[ARM_SP], &cpu->regs);
158 if (!(debugger->stackTraceMode & STACK_TRACE_BREAK_ON_CALL)) {
159 return false;
160 }
161 } else if (!interrupt) {
162 if (frame && currentStack == ARMSelectBank(FRAME_PRIV(frame))) {
163 mStackTracePop(stack);
164 }
165 if (!(debugger->stackTraceMode & STACK_TRACE_BREAK_ON_RETURN)) {
166 return false;
167 }
168 }
169 struct mDebuggerEntryInfo debuggerInfo = {
170 .address = pc,
171 .type.st.traceType = (interrupt || isCall) ? STACK_TRACE_BREAK_ON_CALL : STACK_TRACE_BREAK_ON_RETURN,
172 .pointId = 0
173 };
174 mDebuggerEnter(d->p, DEBUGGER_ENTER_STACK, &debuggerInfo);
175 return true;
176}
177
178static struct ARMDebugBreakpoint* _lookupBreakpoint(struct ARMDebugBreakpointList* breakpoints, uint32_t address) {
179 size_t i;
180 for (i = 0; i < ARMDebugBreakpointListSize(breakpoints); ++i) {
181 if (ARMDebugBreakpointListGetPointer(breakpoints, i)->d.address == address) {
182 return ARMDebugBreakpointListGetPointer(breakpoints, i);
183 }
184 }
185 return 0;
186}
187
188static void _destroyBreakpoint(struct ARMDebugBreakpoint* breakpoint) {
189 if (breakpoint->d.condition) {
190 parseFree(breakpoint->d.condition);
191 free(breakpoint->d.condition);
192 }
193}
194
195static void _destroyWatchpoint(struct mWatchpoint* watchpoint) {
196 if (watchpoint->condition) {
197 parseFree(watchpoint->condition);
198 free(watchpoint->condition);
199 }
200}
201
202static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform* d) {
203 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
204 int instructionLength = _ARMInstructionLength(debugger->cpu);
205 uint32_t pc = debugger->cpu->gprs[ARM_PC] - instructionLength;
206 if (debugger->stackTraceMode != STACK_TRACE_DISABLED && ARMDebuggerUpdateStackTraceInternal(d, pc)) {
207 return;
208 }
209 struct ARMDebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->breakpoints, pc);
210 if (!breakpoint) {
211 return;
212 }
213 if (breakpoint->d.condition) {
214 int32_t value;
215 int segment;
216 if (!mDebuggerEvaluateParseTree(d->p, breakpoint->d.condition, &value, &segment) || !(value || segment >= 0)) {
217 return;
218 }
219 }
220 struct mDebuggerEntryInfo info = {
221 .address = breakpoint->d.address,
222 .type.bp.breakType = BREAKPOINT_HARDWARE,
223 .pointId = breakpoint->d.id
224 };
225 mDebuggerEnter(d->p, DEBUGGER_ENTER_BREAKPOINT, &info);
226}
227
228static void ARMDebuggerInit(void* cpu, struct mDebuggerPlatform* platform);
229static void ARMDebuggerDeinit(struct mDebuggerPlatform* platform);
230
231static void ARMDebuggerEnter(struct mDebuggerPlatform* d, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info);
232
233static ssize_t ARMDebuggerSetBreakpoint(struct mDebuggerPlatform*, const struct mBreakpoint*);
234static bool ARMDebuggerClearBreakpoint(struct mDebuggerPlatform*, ssize_t id);
235static void ARMDebuggerListBreakpoints(struct mDebuggerPlatform*, struct mBreakpointList*);
236static ssize_t ARMDebuggerSetWatchpoint(struct mDebuggerPlatform*, const struct mWatchpoint*);
237static void ARMDebuggerListWatchpoints(struct mDebuggerPlatform*, struct mWatchpointList*);
238static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform*);
239static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform*);
240static void ARMDebuggerTrace(struct mDebuggerPlatform*, char* out, size_t* length);
241static void ARMDebuggerFormatRegisters(struct ARMRegisterFile* regs, char* out, size_t* length);
242static void ARMDebuggerFrameFormatRegisters(struct mStackFrame* frame, char* out, size_t* length);
243static bool ARMDebuggerGetRegister(struct mDebuggerPlatform*, const char* name, int32_t* value);
244static bool ARMDebuggerSetRegister(struct mDebuggerPlatform*, const char* name, int32_t value);
245static uint32_t ARMDebuggerGetStackTraceMode(struct mDebuggerPlatform*);
246static void ARMDebuggerSetStackTraceMode(struct mDebuggerPlatform*, uint32_t);
247static bool ARMDebuggerUpdateStackTrace(struct mDebuggerPlatform* d);
248
249struct mDebuggerPlatform* ARMDebuggerPlatformCreate(void) {
250 struct mDebuggerPlatform* platform = (struct mDebuggerPlatform*) malloc(sizeof(struct ARMDebugger));
251 platform->entered = ARMDebuggerEnter;
252 platform->init = ARMDebuggerInit;
253 platform->deinit = ARMDebuggerDeinit;
254 platform->setBreakpoint = ARMDebuggerSetBreakpoint;
255 platform->listBreakpoints = ARMDebuggerListBreakpoints;
256 platform->clearBreakpoint = ARMDebuggerClearBreakpoint;
257 platform->setWatchpoint = ARMDebuggerSetWatchpoint;
258 platform->listWatchpoints = ARMDebuggerListWatchpoints;
259 platform->checkBreakpoints = ARMDebuggerCheckBreakpoints;
260 platform->hasBreakpoints = ARMDebuggerHasBreakpoints;
261 platform->trace = ARMDebuggerTrace;
262 platform->getRegister = ARMDebuggerGetRegister;
263 platform->setRegister = ARMDebuggerSetRegister;
264 platform->getStackTraceMode = ARMDebuggerGetStackTraceMode;
265 platform->setStackTraceMode = ARMDebuggerSetStackTraceMode;
266 platform->updateStackTrace = ARMDebuggerUpdateStackTrace;
267 return platform;
268}
269
270void ARMDebuggerInit(void* cpu, struct mDebuggerPlatform* platform) {
271 struct ARMDebugger* debugger = (struct ARMDebugger*) platform;
272 debugger->cpu = cpu;
273 debugger->originalMemory = debugger->cpu->memory;
274 debugger->nextId = 1;
275 debugger->stackTraceMode = STACK_TRACE_DISABLED;
276 ARMDebugBreakpointListInit(&debugger->breakpoints, 0);
277 ARMDebugBreakpointListInit(&debugger->swBreakpoints, 0);
278 mWatchpointListInit(&debugger->watchpoints, 0);
279 struct mStackTrace* stack = &platform->p->stackTrace;
280 mStackTraceInit(stack, sizeof(struct ARMRegisterFile));
281 stack->formatRegisters = ARMDebuggerFrameFormatRegisters;
282}
283
284void ARMDebuggerDeinit(struct mDebuggerPlatform* platform) {
285 struct ARMDebugger* debugger = (struct ARMDebugger*) platform;
286 if (debugger->clearSoftwareBreakpoint) {
287 // Clear the stack backwards in case any overlap
288 size_t b;
289 for (b = ARMDebugBreakpointListSize(&debugger->swBreakpoints); b; --b) {
290 struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListGetPointer(&debugger->swBreakpoints, b - 1);
291 debugger->clearSoftwareBreakpoint(debugger, breakpoint);
292 }
293 }
294 ARMDebuggerRemoveMemoryShim(debugger);
295
296 size_t i;
297 for (i = 0; i < ARMDebugBreakpointListSize(&debugger->breakpoints); ++i) {
298 _destroyBreakpoint(ARMDebugBreakpointListGetPointer(&debugger->breakpoints, i));
299 }
300 ARMDebugBreakpointListDeinit(&debugger->breakpoints);
301
302 for (i = 0; i < mWatchpointListSize(&debugger->watchpoints); ++i) {
303 _destroyWatchpoint(mWatchpointListGetPointer(&debugger->watchpoints, i));
304 }
305 ARMDebugBreakpointListDeinit(&debugger->swBreakpoints);
306 mWatchpointListDeinit(&debugger->watchpoints);
307 mStackTraceDeinit(&platform->p->stackTrace);
308}
309
310static void ARMDebuggerEnter(struct mDebuggerPlatform* platform, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) {
311 struct ARMDebugger* debugger = (struct ARMDebugger*) platform;
312 struct ARMCore* cpu = debugger->cpu;
313 cpu->nextEvent = cpu->cycles;
314 if (reason == DEBUGGER_ENTER_BREAKPOINT) {
315 struct ARMDebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->swBreakpoints, _ARMPCAddress(cpu));
316 if (breakpoint && breakpoint->d.type == BREAKPOINT_SOFTWARE) {
317 info->address = breakpoint->d.address;
318 info->pointId = breakpoint->d.id;
319 if (debugger->clearSoftwareBreakpoint) {
320 debugger->clearSoftwareBreakpoint(debugger, breakpoint);
321 }
322
323 ARMRunFake(cpu, breakpoint->sw.opcode);
324
325 if (debugger->setSoftwareBreakpoint) {
326 debugger->setSoftwareBreakpoint(debugger, breakpoint->d.address, breakpoint->sw.mode, &breakpoint->sw.opcode);
327 }
328 }
329 }
330 if (debugger->d.p->entered) {
331 debugger->d.p->entered(debugger->d.p, reason, info);
332 }
333}
334
335ssize_t ARMDebuggerSetSoftwareBreakpoint(struct mDebuggerPlatform* d, uint32_t address, enum ExecutionMode mode) {
336 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
337 uint32_t opcode;
338 if (!debugger->setSoftwareBreakpoint || !debugger->setSoftwareBreakpoint(debugger, address, mode, &opcode)) {
339 return -1;
340 }
341
342 struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListAppend(&debugger->swBreakpoints);
343 ssize_t id = debugger->nextId;
344 ++debugger->nextId;
345 breakpoint->d.id = id;
346 breakpoint->d.address = address & ~1; // Clear Thumb bit since it's not part of a valid address
347 breakpoint->d.segment = -1;
348 breakpoint->d.condition = NULL;
349 breakpoint->d.type = BREAKPOINT_SOFTWARE;
350 breakpoint->sw.opcode = opcode;
351 breakpoint->sw.mode = mode;
352
353 return id;
354}
355
356static ssize_t ARMDebuggerSetBreakpoint(struct mDebuggerPlatform* d, const struct mBreakpoint* info) {
357 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
358 struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListAppend(&debugger->breakpoints);
359 ssize_t id = debugger->nextId;
360 ++debugger->nextId;
361 breakpoint->d = *info;
362 breakpoint->d.address &= ~1; // Clear Thumb bit since it's not part of a valid address
363 breakpoint->d.id = id;
364 if (info->type == BREAKPOINT_SOFTWARE) {
365 // TODO
366 abort();
367 }
368 return id;
369}
370
371static bool ARMDebuggerClearBreakpoint(struct mDebuggerPlatform* d, ssize_t id) {
372 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
373 size_t i;
374
375 struct ARMDebugBreakpointList* breakpoints = &debugger->breakpoints;
376 for (i = 0; i < ARMDebugBreakpointListSize(breakpoints); ++i) {
377 if (ARMDebugBreakpointListGetPointer(breakpoints, i)->d.id == id) {
378 _destroyBreakpoint(ARMDebugBreakpointListGetPointer(breakpoints, i));
379 ARMDebugBreakpointListShift(breakpoints, i, 1);
380 return true;
381 }
382 }
383
384 struct ARMDebugBreakpointList* swBreakpoints = &debugger->swBreakpoints;
385 if (debugger->clearSoftwareBreakpoint) {
386 for (i = 0; i < ARMDebugBreakpointListSize(swBreakpoints); ++i) {
387 if (ARMDebugBreakpointListGetPointer(swBreakpoints, i)->d.id == id) {
388 debugger->clearSoftwareBreakpoint(debugger, ARMDebugBreakpointListGetPointer(swBreakpoints, i));
389 ARMDebugBreakpointListShift(swBreakpoints, i, 1);
390 return true;
391 }
392 }
393 }
394
395 struct mWatchpointList* watchpoints = &debugger->watchpoints;
396 for (i = 0; i < mWatchpointListSize(watchpoints); ++i) {
397 if (mWatchpointListGetPointer(watchpoints, i)->id == id) {
398 _destroyWatchpoint(mWatchpointListGetPointer(watchpoints, i));
399 mWatchpointListShift(watchpoints, i, 1);
400 if (!mWatchpointListSize(&debugger->watchpoints)) {
401 ARMDebuggerRemoveMemoryShim(debugger);
402 }
403 return true;
404 }
405 }
406 return false;
407}
408
409static void ARMDebuggerListBreakpoints(struct mDebuggerPlatform* d, struct mBreakpointList* list) {
410 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
411 mBreakpointListClear(list);
412 size_t i, s;
413 for (i = 0, s = 0; i < ARMDebugBreakpointListSize(&debugger->breakpoints) || s < ARMDebugBreakpointListSize(&debugger->swBreakpoints);) {
414 struct ARMDebugBreakpoint* hw = NULL;
415 struct ARMDebugBreakpoint* sw = NULL;
416 if (i < ARMDebugBreakpointListSize(&debugger->breakpoints)) {
417 hw = ARMDebugBreakpointListGetPointer(&debugger->breakpoints, i);
418 }
419 if (s < ARMDebugBreakpointListSize(&debugger->swBreakpoints)) {
420 sw = ARMDebugBreakpointListGetPointer(&debugger->swBreakpoints, s);
421 }
422 struct mBreakpoint* b = mBreakpointListAppend(list);
423 if (hw && sw) {
424 if (hw->d.id < sw->d.id) {
425 *b = hw->d;
426 ++i;
427 } else {
428 *b = sw->d;
429 ++s;
430 }
431 } else if (hw) {
432 *b = hw->d;
433 ++i;
434 } else if (sw) {
435 *b = sw->d;
436 ++s;
437 } else {
438 abort(); // Should be unreachable
439 }
440 }
441}
442
443static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform* d) {
444 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
445 return ARMDebugBreakpointListSize(&debugger->breakpoints) || mWatchpointListSize(&debugger->watchpoints) || debugger->stackTraceMode != STACK_TRACE_DISABLED;
446}
447
448static ssize_t ARMDebuggerSetWatchpoint(struct mDebuggerPlatform* d, const struct mWatchpoint* info) {
449 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
450 if (!mWatchpointListSize(&debugger->watchpoints)) {
451 ARMDebuggerInstallMemoryShim(debugger);
452 }
453 struct mWatchpoint* watchpoint = mWatchpointListAppend(&debugger->watchpoints);
454 ssize_t id = debugger->nextId;
455 ++debugger->nextId;
456 *watchpoint = *info;
457 watchpoint->id = id;
458 return id;
459}
460
461static void ARMDebuggerListWatchpoints(struct mDebuggerPlatform* d, struct mWatchpointList* list) {
462 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
463 mWatchpointListClear(list);
464 mWatchpointListCopy(list, &debugger->watchpoints);
465}
466
467static void ARMDebuggerTrace(struct mDebuggerPlatform* d, char* out, size_t* length) {
468 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
469 struct ARMCore* cpu = debugger->cpu;
470
471 char disassembly[64];
472
473 struct ARMInstructionInfo info;
474 bool isWideInstruction = ARMDecodeCombined(cpu, &info);
475 if (cpu->executionMode == MODE_ARM) {
476 uint32_t instruction = cpu->prefetch[0];
477 sprintf(disassembly, "%08X: ", instruction);
478 ARMDisassemble(&info, cpu->gprs[ARM_PC], disassembly + strlen("00000000: "), sizeof(disassembly) - strlen("00000000: "));
479 } else {
480 uint16_t instruction = cpu->prefetch[0];
481 ARMDecodeThumb(instruction, &info);
482 if (isWideInstruction) {
483 uint16_t instruction2 = cpu->prefetch[1];
484 sprintf(disassembly, "%04X%04X: ", instruction, instruction2);
485 ARMDisassemble(&info, cpu->gprs[ARM_PC], disassembly + strlen("00000000: "), sizeof(disassembly) - strlen("00000000: "));
486 } else {
487 sprintf(disassembly, " %04X: ", instruction);
488 ARMDisassemble(&info, cpu->gprs[ARM_PC], disassembly + strlen("00000000: "), sizeof(disassembly) - strlen("00000000: "));
489 }
490 }
491
492 size_t regStringLen = *length;
493 ARMDebuggerFormatRegisters(&cpu->regs, out, ®StringLen);
494 regStringLen += snprintf(out + regStringLen, *length - regStringLen, " | %s", disassembly);
495 *length = regStringLen;
496}
497
498static void ARMDebuggerFormatRegisters(struct ARMRegisterFile* regs, char* out, size_t* length) {
499 *length = snprintf(out, *length, "%08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X cpsr: %08X",
500 regs->gprs[0], regs->gprs[1], regs->gprs[2], regs->gprs[3],
501 regs->gprs[4], regs->gprs[5], regs->gprs[6], regs->gprs[7],
502 regs->gprs[8], regs->gprs[9], regs->gprs[10], regs->gprs[11],
503 regs->gprs[12], regs->gprs[13], regs->gprs[14], regs->gprs[15],
504 regs->cpsr.packed);
505}
506
507static void ARMDebuggerFrameFormatRegisters(struct mStackFrame* frame, char* out, size_t* length) {
508 ARMDebuggerFormatRegisters(frame->regs, out, length);
509}
510
511bool ARMDebuggerGetRegister(struct mDebuggerPlatform* d, const char* name, int32_t* value) {
512 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
513 struct ARMCore* cpu = debugger->cpu;
514
515 if (strcmp(name, "sp") == 0) {
516 *value = cpu->gprs[ARM_SP];
517 return true;
518 }
519 if (strcmp(name, "lr") == 0) {
520 *value = cpu->gprs[ARM_LR];
521 return true;
522 }
523 if (strcmp(name, "pc") == 0) {
524 *value = cpu->gprs[ARM_PC];
525 return true;
526 }
527 if (strcmp(name, "cpsr") == 0) {
528 *value = cpu->cpsr.packed;
529 return true;
530 }
531 // TODO: test if mode has SPSR
532 if (strcmp(name, "spsr") == 0) {
533 *value = cpu->spsr.packed;
534 return true;
535 }
536 if (name[0] == 'r') {
537 char* end;
538 uint32_t reg = strtoul(&name[1], &end, 10);
539 if (reg <= ARM_PC) {
540 *value = cpu->gprs[reg];
541 return true;
542 }
543 }
544 return false;
545}
546
547bool ARMDebuggerSetRegister(struct mDebuggerPlatform* d, const char* name, int32_t value) {
548 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
549 struct ARMCore* cpu = debugger->cpu;
550
551 if (strcmp(name, "sp") == 0) {
552 cpu->gprs[ARM_SP] = value;
553 return true;
554 }
555 if (strcmp(name, "lr") == 0) {
556 cpu->gprs[ARM_LR] = value;
557 return true;
558 }
559 if (strcmp(name, "pc") == 0) {
560 cpu->gprs[ARM_PC] = value;
561 if (cpu->executionMode == MODE_ARM) {
562 ARMWritePC(cpu);
563 } else {
564 ThumbWritePC(cpu);
565 }
566 return true;
567 }
568 if (name[0] == 'r') {
569 char* end;
570 uint32_t reg = strtoul(&name[1], &end, 10);
571 if (reg > ARM_PC) {
572 return false;
573 }
574 cpu->gprs[reg] = value;
575 if (reg == ARM_PC) {
576 if (cpu->executionMode == MODE_ARM) {
577 ARMWritePC(cpu);
578 } else {
579 ThumbWritePC(cpu);
580 }
581 }
582 return true;
583 }
584 return false;
585}
586
587static uint32_t ARMDebuggerGetStackTraceMode(struct mDebuggerPlatform* d) {
588 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
589 return debugger->stackTraceMode;
590}
591
592static void ARMDebuggerSetStackTraceMode(struct mDebuggerPlatform* d, uint32_t mode) {
593 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
594 struct mStackTrace* stack = &d->p->stackTrace;
595 if (mode == STACK_TRACE_DISABLED && debugger->stackTraceMode != STACK_TRACE_DISABLED) {
596 mStackTraceClear(stack);
597 }
598 debugger->stackTraceMode = mode;
599}
600
601static bool ARMDebuggerUpdateStackTrace(struct mDebuggerPlatform* d) {
602 struct ARMDebugger* debugger = (struct ARMDebugger*) d;
603 int instructionLength = _ARMInstructionLength(debugger->cpu);
604 uint32_t pc = debugger->cpu->gprs[ARM_PC] - instructionLength;
605 if (debugger->stackTraceMode != STACK_TRACE_DISABLED) {
606 return ARMDebuggerUpdateStackTraceInternal(d, pc);
607 } else {
608 return false;
609 }
610}