all repos — mgba @ 0c51bdf61828104fba2c3a707b1d507d98f8f72d

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