all repos — mgba @ bb1ce789d268b484311b121943acf3ce3809c6d7

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);
 16
 17static struct ARMDebugBreakpoint* _lookupBreakpoint(struct ARMDebugBreakpointList* breakpoints, uint32_t address) {
 18	size_t i;
 19	for (i = 0; i < ARMDebugBreakpointListSize(breakpoints); ++i) {
 20		if (ARMDebugBreakpointListGetPointer(breakpoints, i)->d.address == address) {
 21			return ARMDebugBreakpointListGetPointer(breakpoints, i);
 22		}
 23	}
 24	return 0;
 25}
 26
 27static void _destroyBreakpoint(struct ARMDebugBreakpoint* breakpoint) {
 28	if (breakpoint->d.condition) {
 29		parseFree(breakpoint->d.condition);
 30		free(breakpoint->d.condition);
 31	}
 32}
 33
 34static void _destroyWatchpoint(struct mWatchpoint* watchpoint) {
 35	if (watchpoint->condition) {
 36		parseFree(watchpoint->condition);
 37		free(watchpoint->condition);
 38	}
 39}
 40
 41static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform* d) {
 42	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
 43	int instructionLength;
 44	enum ExecutionMode mode = debugger->cpu->cpsr.t;
 45	if (mode == MODE_ARM) {
 46		instructionLength = WORD_SIZE_ARM;
 47	} else {
 48		instructionLength = WORD_SIZE_THUMB;
 49	}
 50	struct ARMDebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->breakpoints, debugger->cpu->gprs[ARM_PC] - instructionLength);
 51	if (!breakpoint) {
 52		return;
 53	}
 54	if (breakpoint->d.condition) {
 55		int32_t value;
 56		int segment;
 57		if (!mDebuggerEvaluateParseTree(d->p, breakpoint->d.condition, &value, &segment) || !(value || segment >= 0)) {
 58			return;
 59		}
 60	}
 61	struct mDebuggerEntryInfo info = {
 62		.address = breakpoint->d.address,
 63		.type.bp.breakType = BREAKPOINT_HARDWARE,
 64		.pointId = breakpoint->d.id
 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 ssize_t ARMDebuggerSetBreakpoint(struct mDebuggerPlatform*, const struct mBreakpoint*);
 75static bool ARMDebuggerClearBreakpoint(struct mDebuggerPlatform*, ssize_t id);
 76static void ARMDebuggerListBreakpoints(struct mDebuggerPlatform*, struct mBreakpointList*);
 77static ssize_t ARMDebuggerSetWatchpoint(struct mDebuggerPlatform*, const struct mWatchpoint*);
 78static void ARMDebuggerListWatchpoints(struct mDebuggerPlatform*, struct mWatchpointList*);
 79static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform*);
 80static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform*);
 81static void ARMDebuggerTrace(struct mDebuggerPlatform*, char* out, size_t* length);
 82static bool ARMDebuggerGetRegister(struct mDebuggerPlatform*, const char* name, int32_t* value);
 83static bool ARMDebuggerSetRegister(struct mDebuggerPlatform*, const char* name, int32_t value);
 84
 85struct mDebuggerPlatform* ARMDebuggerPlatformCreate(void) {
 86	struct mDebuggerPlatform* platform = (struct mDebuggerPlatform*) malloc(sizeof(struct ARMDebugger));
 87	platform->entered = ARMDebuggerEnter;
 88	platform->init = ARMDebuggerInit;
 89	platform->deinit = ARMDebuggerDeinit;
 90	platform->setBreakpoint = ARMDebuggerSetBreakpoint;
 91	platform->listBreakpoints = ARMDebuggerListBreakpoints;
 92	platform->clearBreakpoint = ARMDebuggerClearBreakpoint;
 93	platform->setWatchpoint = ARMDebuggerSetWatchpoint;
 94	platform->listWatchpoints = ARMDebuggerListWatchpoints;
 95	platform->checkBreakpoints = ARMDebuggerCheckBreakpoints;
 96	platform->hasBreakpoints = ARMDebuggerHasBreakpoints;
 97	platform->trace = ARMDebuggerTrace;
 98	platform->getRegister = ARMDebuggerGetRegister;
 99	platform->setRegister = ARMDebuggerSetRegister;
100	return platform;
101}
102
103void ARMDebuggerInit(void* cpu, struct mDebuggerPlatform* platform) {
104	struct ARMDebugger* debugger = (struct ARMDebugger*) platform;
105	debugger->cpu = cpu;
106	debugger->originalMemory = debugger->cpu->memory;
107	debugger->nextId = 1;
108	ARMDebugBreakpointListInit(&debugger->breakpoints, 0);
109	ARMDebugBreakpointListInit(&debugger->swBreakpoints, 0);
110	mWatchpointListInit(&debugger->watchpoints, 0);
111}
112
113void ARMDebuggerDeinit(struct mDebuggerPlatform* platform) {
114	struct ARMDebugger* debugger = (struct ARMDebugger*) platform;
115	if (debugger->clearSoftwareBreakpoint) {
116		// Clear the stack backwards in case any overlap
117		size_t b;
118		for (b = ARMDebugBreakpointListSize(&debugger->swBreakpoints); b; --b) {
119			struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListGetPointer(&debugger->swBreakpoints, b - 1);
120			debugger->clearSoftwareBreakpoint(debugger, breakpoint);
121		}
122	}
123	ARMDebuggerRemoveMemoryShim(debugger);
124
125	size_t i;
126	for (i = 0; i < ARMDebugBreakpointListSize(&debugger->breakpoints); ++i) {
127		_destroyBreakpoint(ARMDebugBreakpointListGetPointer(&debugger->breakpoints, i));
128	}
129	ARMDebugBreakpointListDeinit(&debugger->breakpoints);
130
131	for (i = 0; i < mWatchpointListSize(&debugger->watchpoints); ++i) {
132		_destroyWatchpoint(mWatchpointListGetPointer(&debugger->watchpoints, i));
133	}
134	ARMDebugBreakpointListDeinit(&debugger->swBreakpoints);
135	mWatchpointListDeinit(&debugger->watchpoints);
136}
137
138static void ARMDebuggerEnter(struct mDebuggerPlatform* platform, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) {
139	struct ARMDebugger* debugger = (struct ARMDebugger*) platform;
140	struct ARMCore* cpu = debugger->cpu;
141	cpu->nextEvent = cpu->cycles;
142	if (reason == DEBUGGER_ENTER_BREAKPOINT) {
143		struct ARMDebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->swBreakpoints, _ARMPCAddress(cpu));
144		if (breakpoint && breakpoint->d.type == BREAKPOINT_SOFTWARE) {
145			info->address = breakpoint->d.address;
146			info->pointId = breakpoint->d.id;
147			if (debugger->clearSoftwareBreakpoint) {
148				debugger->clearSoftwareBreakpoint(debugger, breakpoint);
149			}
150
151			ARMRunFake(cpu, breakpoint->sw.opcode);
152
153			if (debugger->setSoftwareBreakpoint) {
154				debugger->setSoftwareBreakpoint(debugger, breakpoint->d.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
163ssize_t 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 -1;
168	}
169
170	struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListAppend(&debugger->swBreakpoints);
171	ssize_t id = debugger->nextId;
172	++debugger->nextId;
173	breakpoint->d.id = id;
174	breakpoint->d.address = address;
175	breakpoint->d.segment = -1;
176	breakpoint->d.condition = NULL;
177	breakpoint->d.type = BREAKPOINT_SOFTWARE;
178	breakpoint->sw.opcode = opcode;
179	breakpoint->sw.mode = mode;
180
181	return id;
182}
183
184static ssize_t ARMDebuggerSetBreakpoint(struct mDebuggerPlatform* d, const struct mBreakpoint* info) {
185	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
186	struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListAppend(&debugger->breakpoints);
187	ssize_t id = debugger->nextId;
188	++debugger->nextId;
189	breakpoint->d = *info;
190	breakpoint->d.id = id;
191	if (info->type == BREAKPOINT_SOFTWARE) {
192		// TODO
193		abort();
194	}
195	return id;
196}
197
198static bool ARMDebuggerClearBreakpoint(struct mDebuggerPlatform* d, ssize_t id) {
199	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
200	size_t i;
201
202	struct ARMDebugBreakpointList* breakpoints = &debugger->breakpoints;
203	for (i = 0; i < ARMDebugBreakpointListSize(breakpoints); ++i) {
204		if (ARMDebugBreakpointListGetPointer(breakpoints, i)->d.id == id) {
205			_destroyBreakpoint(ARMDebugBreakpointListGetPointer(breakpoints, i));
206			ARMDebugBreakpointListShift(breakpoints, i, 1);
207			return true;
208		}
209	}
210
211	struct ARMDebugBreakpointList* swBreakpoints = &debugger->swBreakpoints;
212	if (debugger->clearSoftwareBreakpoint) {
213		for (i = 0; i < ARMDebugBreakpointListSize(swBreakpoints); ++i) {
214			if (ARMDebugBreakpointListGetPointer(swBreakpoints, i)->d.id == id) {
215				debugger->clearSoftwareBreakpoint(debugger, ARMDebugBreakpointListGetPointer(swBreakpoints, i));
216				ARMDebugBreakpointListShift(swBreakpoints, i, 1);
217				return true;
218			}
219		}
220	}
221
222	struct mWatchpointList* watchpoints = &debugger->watchpoints;
223	for (i = 0; i < mWatchpointListSize(watchpoints); ++i) {
224		if (mWatchpointListGetPointer(watchpoints, i)->id == id) {
225			_destroyWatchpoint(mWatchpointListGetPointer(watchpoints, i));
226			mWatchpointListShift(watchpoints, i, 1);
227			if (!mWatchpointListSize(&debugger->watchpoints)) {
228				ARMDebuggerRemoveMemoryShim(debugger);
229			}
230			return true;
231		}
232	}
233	return false;
234}
235
236static void ARMDebuggerListBreakpoints(struct mDebuggerPlatform* d, struct mBreakpointList* list) {
237	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
238	mBreakpointListClear(list);
239	size_t i, s;
240	for (i = 0, s = 0; i < ARMDebugBreakpointListSize(&debugger->breakpoints) || s < ARMDebugBreakpointListSize(&debugger->swBreakpoints);) {
241		struct ARMDebugBreakpoint* hw = NULL;
242		struct ARMDebugBreakpoint* sw = NULL;
243		if (i < ARMDebugBreakpointListSize(&debugger->breakpoints)) {
244			hw = ARMDebugBreakpointListGetPointer(&debugger->breakpoints, i);
245		}
246		if (s < ARMDebugBreakpointListSize(&debugger->swBreakpoints)) {
247			sw = ARMDebugBreakpointListGetPointer(&debugger->swBreakpoints, s);
248		}
249		struct mBreakpoint* b = mBreakpointListAppend(list);
250		if (hw && sw) {
251			if (hw->d.id < sw->d.id) {
252				*b = hw->d;
253				++i;
254			} else {
255				*b = sw->d;
256				++s;
257			}
258		} else if (hw) {
259			*b = hw->d;
260			++i;
261		} else if (sw) {
262			*b = sw->d;
263			++s;		
264		} else {
265			abort(); // Should be unreachable
266		}
267	}
268}
269
270static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform* d) {
271	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
272	return ARMDebugBreakpointListSize(&debugger->breakpoints) || mWatchpointListSize(&debugger->watchpoints);
273}
274
275static ssize_t ARMDebuggerSetWatchpoint(struct mDebuggerPlatform* d, const struct mWatchpoint* info) {
276	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
277	if (!mWatchpointListSize(&debugger->watchpoints)) {
278		ARMDebuggerInstallMemoryShim(debugger);
279	}
280	struct mWatchpoint* watchpoint = mWatchpointListAppend(&debugger->watchpoints);
281	ssize_t id = debugger->nextId;
282	++debugger->nextId;
283	*watchpoint = *info;
284	watchpoint->id = id;
285	return id;
286}
287
288static void ARMDebuggerListWatchpoints(struct mDebuggerPlatform* d, struct mWatchpointList* list) {
289	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
290	mWatchpointListClear(list);
291	mWatchpointListCopy(list, &debugger->watchpoints);
292}
293
294static void ARMDebuggerTrace(struct mDebuggerPlatform* d, char* out, size_t* length) {
295	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
296	struct ARMCore* cpu = debugger->cpu;
297
298	char disassembly[64];
299
300	struct ARMInstructionInfo info;
301	if (cpu->executionMode == MODE_ARM) {
302		uint32_t instruction = cpu->prefetch[0];
303		sprintf(disassembly, "%08X: ", instruction);
304		ARMDecodeARM(instruction, &info);
305		ARMDisassemble(&info, cpu->gprs[ARM_PC], disassembly + strlen("00000000: "), sizeof(disassembly) - strlen("00000000: "));
306	} else {
307		struct ARMInstructionInfo info2;
308		struct ARMInstructionInfo combined;
309		uint16_t instruction = cpu->prefetch[0];
310		uint16_t instruction2 = cpu->prefetch[1];
311		ARMDecodeThumb(instruction, &info);
312		ARMDecodeThumb(instruction2, &info2);
313		if (ARMDecodeThumbCombine(&info, &info2, &combined)) {
314			sprintf(disassembly, "%04X%04X: ", instruction, instruction2);
315			ARMDisassemble(&combined, cpu->gprs[ARM_PC], disassembly + strlen("00000000: "), sizeof(disassembly) - strlen("00000000: "));
316		} else {
317			sprintf(disassembly, "    %04X: ", instruction);
318			ARMDisassemble(&info, cpu->gprs[ARM_PC], disassembly + strlen("00000000: "), sizeof(disassembly) - strlen("00000000: "));
319		}
320	}
321
322	*length = snprintf(out, *length, "%08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X cpsr: %08X | %s",
323		               cpu->gprs[0],  cpu->gprs[1],  cpu->gprs[2],  cpu->gprs[3],
324		               cpu->gprs[4],  cpu->gprs[5],  cpu->gprs[6],  cpu->gprs[7],
325		               cpu->gprs[8],  cpu->gprs[9],  cpu->gprs[10], cpu->gprs[11],
326		               cpu->gprs[12], cpu->gprs[13], cpu->gprs[14], cpu->gprs[15],
327		               cpu->cpsr.packed, disassembly);
328}
329
330bool ARMDebuggerGetRegister(struct mDebuggerPlatform* d, const char* name, int32_t* value) {
331	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
332	struct ARMCore* cpu = debugger->cpu;
333
334	if (strcmp(name, "sp") == 0) {
335		*value = cpu->gprs[ARM_SP];
336		return true;
337	}
338	if (strcmp(name, "lr") == 0) {
339		*value = cpu->gprs[ARM_LR];
340		return true;
341	}
342	if (strcmp(name, "pc") == 0) {
343		*value = cpu->gprs[ARM_PC];
344		return true;
345	}
346	if (strcmp(name, "cpsr") == 0) {
347		*value = cpu->cpsr.packed;
348		return true;
349	}
350	// TODO: test if mode has SPSR
351	if (strcmp(name, "spsr") == 0) {
352		*value = cpu->spsr.packed;
353		return true;
354	}
355	if (name[0] == 'r') {
356		char* end;
357		uint32_t reg = strtoul(&name[1], &end, 10);
358		if (reg <= ARM_PC) {
359			*value = cpu->gprs[reg];
360			return true;
361		}
362	}
363	return false;
364}
365
366bool ARMDebuggerSetRegister(struct mDebuggerPlatform* d, const char* name, int32_t value) {
367	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
368	struct ARMCore* cpu = debugger->cpu;
369
370	if (strcmp(name, "sp") == 0) {
371		cpu->gprs[ARM_SP] = value;
372		return true;
373	}
374	if (strcmp(name, "lr") == 0) {
375		cpu->gprs[ARM_LR] = value;
376		return true;
377	}
378	if (strcmp(name, "pc") == 0) {
379		cpu->gprs[ARM_PC] = value;
380		if (cpu->executionMode == MODE_ARM) {
381			ARMWritePC(cpu);
382		} else {
383			ThumbWritePC(cpu);
384		}
385		return true;
386	}
387	if (name[0] == 'r') {
388		char* end;
389		uint32_t reg = strtoul(&name[1], &end, 10);
390		if (reg > ARM_PC) {
391			return false;
392		}
393		cpu->gprs[reg] = value;
394		if (reg == ARM_PC) {
395			if (cpu->executionMode == MODE_ARM) {
396				ARMWritePC(cpu);
397			} else {
398				ThumbWritePC(cpu);
399			}
400		}
401		return true;
402	}
403	return false;
404}