all repos — mgba @ f2134880898dd3ceaba7945c992c8ff34e245682

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