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