all repos — mgba @ 830cad3e7bc24281d8d9da31672c020693a17c5b

mGBA Game Boy Advance Emulator

src/arm/debugger/debugger.c (view raw)

  1/* Copyright (c) 2013-2016 Jeffrey Pfau
  2 *
  3 * This Source Code Form is subject to the terms of the Mozilla Public
  4 * License, v. 2.0. If a copy of the MPL was not distributed with this
  5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6#include <mgba/internal/arm/debugger/debugger.h>
  7
  8#include <mgba/core/core.h>
  9#include <mgba/internal/arm/arm.h>
 10#include <mgba/internal/arm/decoder.h>
 11#include <mgba/internal/arm/isa-inlines.h>
 12#include <mgba/internal/arm/debugger/memory-debugger.h>
 13#include <mgba/internal/debugger/parser.h>
 14
 15DEFINE_VECTOR(ARMDebugBreakpointList, struct ARMDebugBreakpoint);
 16DEFINE_VECTOR(ARMDebugWatchpointList, struct ARMDebugWatchpoint);
 17
 18static struct ARMDebugBreakpoint* _lookupBreakpoint(struct ARMDebugBreakpointList* breakpoints, uint32_t address) {
 19	size_t i;
 20	for (i = 0; i < ARMDebugBreakpointListSize(breakpoints); ++i) {
 21		if (ARMDebugBreakpointListGetPointer(breakpoints, i)->address == address) {
 22			return ARMDebugBreakpointListGetPointer(breakpoints, i);
 23		}
 24	}
 25	return 0;
 26}
 27
 28static void _destroyBreakpoint(struct ARMDebugBreakpoint* breakpoint) {
 29	if (breakpoint->condition) {
 30		parseFree(breakpoint->condition);
 31		free(breakpoint->condition);
 32	}
 33}
 34
 35static void _destroyWatchpoint(struct ARMDebugWatchpoint* watchpoint) {
 36	if (watchpoint->condition) {
 37		parseFree(watchpoint->condition);
 38		free(watchpoint->condition);
 39	}
 40}
 41
 42static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform* d) {
 43	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
 44	int instructionLength;
 45	enum ExecutionMode mode = debugger->cpu->cpsr.t;
 46	if (mode == MODE_ARM) {
 47		instructionLength = WORD_SIZE_ARM;
 48	} else {
 49		instructionLength = WORD_SIZE_THUMB;
 50	}
 51	struct ARMDebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->breakpoints, debugger->cpu->gprs[ARM_PC] - instructionLength);
 52	if (!breakpoint) {
 53		return;
 54	}
 55	if (breakpoint->condition) {
 56		int32_t value;
 57		int segment;
 58		if (!mDebuggerEvaluateParseTree(d->p, breakpoint->condition, &value, &segment) || !(value || segment >= 0)) {
 59			return;
 60		}
 61	}
 62	struct mDebuggerEntryInfo info = {
 63		.address = breakpoint->address,
 64		.type.bp.breakType = BREAKPOINT_HARDWARE
 65	};
 66	mDebuggerEnter(d->p, DEBUGGER_ENTER_BREAKPOINT, &info);
 67}
 68
 69static void ARMDebuggerInit(void* cpu, struct mDebuggerPlatform* platform);
 70static void ARMDebuggerDeinit(struct mDebuggerPlatform* platform);
 71
 72static void ARMDebuggerEnter(struct mDebuggerPlatform* d, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info);
 73
 74static void ARMDebuggerSetBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
 75static void ARMDebuggerSetConditionalBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment, struct ParseTree* condition);
 76static void ARMDebuggerClearBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
 77static void ARMDebuggerSetWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type);
 78static void ARMDebuggerSetConditionalWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type, struct ParseTree* condition);
 79static void ARMDebuggerClearWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
 80static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform*);
 81static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform*);
 82static void ARMDebuggerTrace(struct mDebuggerPlatform*, char* out, size_t* length);
 83static bool ARMDebuggerGetRegister(struct mDebuggerPlatform*, const char* name, int32_t* value);
 84static bool ARMDebuggerSetRegister(struct mDebuggerPlatform*, const char* name, int32_t value);
 85
 86struct mDebuggerPlatform* ARMDebuggerPlatformCreate(void) {
 87	struct mDebuggerPlatform* platform = (struct mDebuggerPlatform*) malloc(sizeof(struct ARMDebugger));
 88	platform->entered = ARMDebuggerEnter;
 89	platform->init = ARMDebuggerInit;
 90	platform->deinit = ARMDebuggerDeinit;
 91	platform->setBreakpoint = ARMDebuggerSetBreakpoint;
 92	platform->setConditionalBreakpoint = ARMDebuggerSetConditionalBreakpoint;
 93	platform->clearBreakpoint = ARMDebuggerClearBreakpoint;
 94	platform->setWatchpoint = ARMDebuggerSetWatchpoint;
 95	platform->setConditionalWatchpoint = ARMDebuggerSetConditionalWatchpoint;
 96	platform->clearWatchpoint = ARMDebuggerClearWatchpoint;
 97	platform->checkBreakpoints = ARMDebuggerCheckBreakpoints;
 98	platform->hasBreakpoints = ARMDebuggerHasBreakpoints;
 99	platform->trace = ARMDebuggerTrace;
100	platform->getRegister = ARMDebuggerGetRegister;
101	platform->setRegister = ARMDebuggerSetRegister;
102	return platform;
103}
104
105void ARMDebuggerInit(void* cpu, struct mDebuggerPlatform* platform) {
106	struct ARMDebugger* debugger = (struct ARMDebugger*) platform;
107	debugger->cpu = cpu;
108	debugger->originalMemory = debugger->cpu->memory;
109	ARMDebugBreakpointListInit(&debugger->breakpoints, 0);
110	ARMDebugBreakpointListInit(&debugger->swBreakpoints, 0);
111	ARMDebugWatchpointListInit(&debugger->watchpoints, 0);
112}
113
114void ARMDebuggerDeinit(struct mDebuggerPlatform* platform) {
115	struct ARMDebugger* debugger = (struct ARMDebugger*) platform;
116	if (debugger->clearSoftwareBreakpoint) {
117		// Clear the stack backwards in case any overlap
118		size_t b;
119		for (b = ARMDebugBreakpointListSize(&debugger->swBreakpoints); b; --b) {
120			struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListGetPointer(&debugger->swBreakpoints, b - 1);
121			debugger->clearSoftwareBreakpoint(debugger, breakpoint->address, breakpoint->sw.mode, breakpoint->sw.opcode);
122		}
123	}
124	ARMDebuggerRemoveMemoryShim(debugger);
125
126	size_t i;
127	for (i = 0; i < ARMDebugBreakpointListSize(&debugger->breakpoints); ++i) {
128		_destroyBreakpoint(ARMDebugBreakpointListGetPointer(&debugger->breakpoints, i));
129	}
130	ARMDebugBreakpointListDeinit(&debugger->breakpoints);
131
132	for (i = 0; i < ARMDebugWatchpointListSize(&debugger->watchpoints); ++i) {
133		_destroyWatchpoint(ARMDebugWatchpointListGetPointer(&debugger->watchpoints, i));
134	}
135	ARMDebugBreakpointListDeinit(&debugger->swBreakpoints);
136	ARMDebugWatchpointListDeinit(&debugger->watchpoints);
137}
138
139static void ARMDebuggerEnter(struct mDebuggerPlatform* platform, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) {
140	struct ARMDebugger* debugger = (struct ARMDebugger*) platform;
141	struct ARMCore* cpu = debugger->cpu;
142	cpu->nextEvent = cpu->cycles;
143	if (reason == DEBUGGER_ENTER_BREAKPOINT) {
144		struct ARMDebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->swBreakpoints, _ARMPCAddress(cpu));
145		if (breakpoint && breakpoint->isSw) {
146			info->address = breakpoint->address;
147			if (debugger->clearSoftwareBreakpoint) {
148				debugger->clearSoftwareBreakpoint(debugger, breakpoint->address, breakpoint->sw.mode, breakpoint->sw.opcode);
149			}
150
151			ARMRunFake(cpu, breakpoint->sw.opcode);
152
153			if (debugger->setSoftwareBreakpoint) {
154				debugger->setSoftwareBreakpoint(debugger, breakpoint->address, breakpoint->sw.mode, &breakpoint->sw.opcode);
155			}
156		}
157	}
158	if (debugger->d.p->entered) {
159		debugger->d.p->entered(debugger->d.p, reason, info);
160	}
161}
162
163bool ARMDebuggerSetSoftwareBreakpoint(struct mDebuggerPlatform* d, uint32_t address, enum ExecutionMode mode) {
164	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
165	uint32_t opcode;
166	if (!debugger->setSoftwareBreakpoint || !debugger->setSoftwareBreakpoint(debugger, address, mode, &opcode)) {
167		return false;
168	}
169
170	struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListAppend(&debugger->swBreakpoints);
171	breakpoint->address = address;
172	breakpoint->isSw = true;
173	breakpoint->sw.opcode = opcode;
174	breakpoint->sw.mode = mode;
175
176	return true;
177}
178
179void ARMDebuggerClearSoftwareBreakpoint(struct mDebuggerPlatform* d, uint32_t address) {
180	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
181	if (!debugger->clearSoftwareBreakpoint) {
182		return;
183	}
184
185	struct ARMDebugBreakpoint* breakpoint = NULL;
186	// Clear the stack backwards in case any overlap
187	size_t b;
188	for (b = ARMDebugBreakpointListSize(&debugger->swBreakpoints); b; --b) {
189		breakpoint = ARMDebugBreakpointListGetPointer(&debugger->swBreakpoints, b - 1);
190		if (breakpoint->address == address) {
191			break;
192		}
193		breakpoint = NULL;
194	}
195
196	if (breakpoint) {
197		debugger->clearSoftwareBreakpoint(debugger, address, breakpoint->sw.mode, breakpoint->sw.opcode);
198	}
199}
200
201static void ARMDebuggerSetBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) {
202	ARMDebuggerSetConditionalBreakpoint(d, address, segment, NULL);
203}
204
205static void ARMDebuggerSetConditionalBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, struct ParseTree* condition) {
206	UNUSED(segment);
207	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
208	struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListAppend(&debugger->breakpoints);
209	breakpoint->condition = condition;
210	breakpoint->address = address;
211	breakpoint->isSw = false;
212}
213
214static void ARMDebuggerClearBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) {
215	UNUSED(segment);
216	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
217	struct ARMDebugBreakpointList* breakpoints = &debugger->breakpoints;
218	size_t i;
219	for (i = 0; i < ARMDebugBreakpointListSize(breakpoints); ++i) {
220		if (ARMDebugBreakpointListGetPointer(breakpoints, i)->address == address) {
221			_destroyBreakpoint(ARMDebugBreakpointListGetPointer(breakpoints, i));
222			ARMDebugBreakpointListShift(breakpoints, i, 1);
223		}
224	}
225}
226
227static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform* d) {
228	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
229	return ARMDebugBreakpointListSize(&debugger->breakpoints) || ARMDebugWatchpointListSize(&debugger->watchpoints);
230}
231
232static void ARMDebuggerSetWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, enum mWatchpointType type) {
233	ARMDebuggerSetConditionalWatchpoint(d, address, segment, type, NULL);
234}
235
236static void ARMDebuggerSetConditionalWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, enum mWatchpointType type, struct ParseTree* condition) {
237	UNUSED(segment);
238	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
239	if (!ARMDebugWatchpointListSize(&debugger->watchpoints)) {
240		ARMDebuggerInstallMemoryShim(debugger);
241	}
242	struct ARMDebugWatchpoint* watchpoint = ARMDebugWatchpointListAppend(&debugger->watchpoints);
243	watchpoint->address = address;
244	watchpoint->type = type;
245	watchpoint->condition = condition;
246}
247
248static void ARMDebuggerClearWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) {
249	UNUSED(segment);
250	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
251	struct ARMDebugWatchpointList* watchpoints = &debugger->watchpoints;
252	size_t i;
253	for (i = 0; i < ARMDebugWatchpointListSize(watchpoints); ++i) {
254		if (ARMDebugWatchpointListGetPointer(watchpoints, i)->address == address) {
255			_destroyWatchpoint(ARMDebugWatchpointListGetPointer(watchpoints, i));
256			ARMDebugWatchpointListShift(watchpoints, i, 1);
257		}
258	}
259	if (!ARMDebugWatchpointListSize(&debugger->watchpoints)) {
260		ARMDebuggerRemoveMemoryShim(debugger);
261	}
262}
263
264static void ARMDebuggerTrace(struct mDebuggerPlatform* d, char* out, size_t* length) {
265	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
266	struct ARMCore* cpu = debugger->cpu;
267
268	char disassembly[64];
269
270	struct ARMInstructionInfo info;
271	if (cpu->executionMode == MODE_ARM) {
272		uint32_t instruction = cpu->prefetch[0];
273		sprintf(disassembly, "%08X: ", instruction);
274		ARMDecodeARM(instruction, &info);
275		ARMDisassemble(&info, cpu->gprs[ARM_PC], disassembly + strlen("00000000: "), sizeof(disassembly) - strlen("00000000: "));
276	} else {
277		struct ARMInstructionInfo info2;
278		struct ARMInstructionInfo combined;
279		uint16_t instruction = cpu->prefetch[0];
280		uint16_t instruction2 = cpu->prefetch[1];
281		ARMDecodeThumb(instruction, &info);
282		ARMDecodeThumb(instruction2, &info2);
283		if (ARMDecodeThumbCombine(&info, &info2, &combined)) {
284			sprintf(disassembly, "%04X%04X: ", instruction, instruction2);
285			ARMDisassemble(&combined, cpu->gprs[ARM_PC], disassembly + strlen("00000000: "), sizeof(disassembly) - strlen("00000000: "));
286		} else {
287			sprintf(disassembly, "    %04X: ", instruction);
288			ARMDisassemble(&info, cpu->gprs[ARM_PC], disassembly + strlen("00000000: "), sizeof(disassembly) - strlen("00000000: "));
289		}
290	}
291
292	*length = snprintf(out, *length, "%08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X cpsr: %08X | %s",
293		               cpu->gprs[0],  cpu->gprs[1],  cpu->gprs[2],  cpu->gprs[3],
294		               cpu->gprs[4],  cpu->gprs[5],  cpu->gprs[6],  cpu->gprs[7],
295		               cpu->gprs[8],  cpu->gprs[9],  cpu->gprs[10], cpu->gprs[11],
296		               cpu->gprs[12], cpu->gprs[13], cpu->gprs[14], cpu->gprs[15],
297		               cpu->cpsr.packed, disassembly);
298}
299
300bool ARMDebuggerGetRegister(struct mDebuggerPlatform* d, const char* name, int32_t* value) {
301	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
302	struct ARMCore* cpu = debugger->cpu;
303
304	if (strcmp(name, "sp") == 0) {
305		*value = cpu->gprs[ARM_SP];
306		return true;
307	}
308	if (strcmp(name, "lr") == 0) {
309		*value = cpu->gprs[ARM_LR];
310		return true;
311	}
312	if (strcmp(name, "pc") == 0) {
313		*value = cpu->gprs[ARM_PC];
314		return true;
315	}
316	if (strcmp(name, "cpsr") == 0) {
317		*value = cpu->cpsr.packed;
318		return true;
319	}
320	// TODO: test if mode has SPSR
321	if (strcmp(name, "spsr") == 0) {
322		*value = cpu->spsr.packed;
323		return true;
324	}
325	if (name[0] == 'r') {
326		char* end;
327		uint32_t reg = strtoul(&name[1], &end, 10);
328		if (reg <= ARM_PC) {
329			*value = cpu->gprs[reg];
330			return true;
331		}
332	}
333	return false;
334}
335
336bool ARMDebuggerSetRegister(struct mDebuggerPlatform* d, const char* name, int32_t value) {
337	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
338	struct ARMCore* cpu = debugger->cpu;
339
340	if (strcmp(name, "sp") == 0) {
341		cpu->gprs[ARM_SP] = value;
342		return true;
343	}
344	if (strcmp(name, "lr") == 0) {
345		cpu->gprs[ARM_LR] = value;
346		return true;
347	}
348	if (strcmp(name, "pc") == 0) {
349		cpu->gprs[ARM_PC] = value;
350		int32_t currentCycles = 0;
351		if (cpu->executionMode == MODE_ARM) {
352			ARM_WRITE_PC;
353		} else {
354			THUMB_WRITE_PC;
355		}
356		return true;
357	}
358	if (name[0] == 'r') {
359		char* end;
360		uint32_t reg = strtoul(&name[1], &end, 10);
361		if (reg > ARM_PC) {
362			return false;
363		}
364		cpu->gprs[reg] = value;
365		if (reg == ARM_PC) {
366			int32_t currentCycles = 0;
367			if (cpu->executionMode == MODE_ARM) {
368				ARM_WRITE_PC;
369			} else {
370				THUMB_WRITE_PC;
371			}
372		}
373		return true;
374	}
375	return false;
376}