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