all repos — mgba @ fb267a32ff4b9ff6dc8688610b936cb8450edd8e

mGBA Game Boy Advance Emulator

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