all repos — mgba @ 07667955f66e4d00b0f5421f2d08186109211080

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