all repos — mgba @ medusa

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/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, &regStringLen);
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}