all repos — mgba @ 7736e72b3841d060d320836781c9f078ee8434e2

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