all repos — mgba @ 0383c82b469e7ca38832f66d07418cd890ea68a5

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