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