all repos — mgba @ 70a6622a5c65c7a5636126615704436f12ecb797

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
 14DEFINE_VECTOR(ARMDebugBreakpointList, struct ARMDebugBreakpoint);
 15DEFINE_VECTOR(ARMDebugWatchpointList, struct ARMDebugWatchpoint);
 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)->address == address) {
 21			return ARMDebugBreakpointListGetPointer(breakpoints, i);
 22		}
 23	}
 24	return 0;
 25}
 26
 27static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform* d) {
 28	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
 29	int instructionLength;
 30	enum ExecutionMode mode = debugger->cpu->cpsr.t;
 31	if (mode == MODE_ARM) {
 32		instructionLength = WORD_SIZE_ARM;
 33	} else {
 34		instructionLength = WORD_SIZE_THUMB;
 35	}
 36	struct ARMDebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->breakpoints, debugger->cpu->gprs[ARM_PC] - instructionLength);
 37	if (!breakpoint) {
 38		return;
 39	}
 40	struct mDebuggerEntryInfo info = {
 41		.address = breakpoint->address,
 42		.breakType = BREAKPOINT_HARDWARE
 43	};
 44	mDebuggerEnter(d->p, DEBUGGER_ENTER_BREAKPOINT, &info);
 45}
 46
 47static void ARMDebuggerInit(void* cpu, struct mDebuggerPlatform* platform);
 48static void ARMDebuggerDeinit(struct mDebuggerPlatform* platform);
 49
 50static void ARMDebuggerEnter(struct mDebuggerPlatform* d, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info);
 51
 52static void ARMDebuggerSetBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
 53static void ARMDebuggerClearBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
 54static void ARMDebuggerSetWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type);
 55static void ARMDebuggerClearWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
 56static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform*);
 57static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform*);
 58static void ARMDebuggerTrace(struct mDebuggerPlatform*, char* out, size_t* length);
 59
 60struct mDebuggerPlatform* ARMDebuggerPlatformCreate(void) {
 61	struct mDebuggerPlatform* platform = (struct mDebuggerPlatform*) malloc(sizeof(struct ARMDebugger));
 62	platform->entered = ARMDebuggerEnter;
 63	platform->init = ARMDebuggerInit;
 64	platform->deinit = ARMDebuggerDeinit;
 65	platform->setBreakpoint = ARMDebuggerSetBreakpoint;
 66	platform->clearBreakpoint = ARMDebuggerClearBreakpoint;
 67	platform->setWatchpoint = ARMDebuggerSetWatchpoint;
 68	platform->clearWatchpoint = ARMDebuggerClearWatchpoint;
 69	platform->checkBreakpoints = ARMDebuggerCheckBreakpoints;
 70	platform->hasBreakpoints = ARMDebuggerHasBreakpoints;
 71	platform->trace = ARMDebuggerTrace;
 72	return platform;
 73}
 74
 75void ARMDebuggerInit(void* cpu, struct mDebuggerPlatform* platform) {
 76	struct ARMDebugger* debugger = (struct ARMDebugger*) platform;
 77	debugger->cpu = cpu;
 78	debugger->originalMemory = debugger->cpu->memory;
 79	ARMDebugBreakpointListInit(&debugger->breakpoints, 0);
 80	ARMDebugBreakpointListInit(&debugger->swBreakpoints, 0);
 81	ARMDebugWatchpointListInit(&debugger->watchpoints, 0);
 82}
 83
 84void ARMDebuggerDeinit(struct mDebuggerPlatform* platform) {
 85	struct ARMDebugger* debugger = (struct ARMDebugger*) platform;
 86	if (debugger->clearSoftwareBreakpoint) {
 87		// Clear the stack backwards in case any overlap
 88		size_t b;
 89		for (b = ARMDebugBreakpointListSize(&debugger->swBreakpoints); b; --b) {
 90			struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListGetPointer(&debugger->swBreakpoints, b - 1);
 91			debugger->clearSoftwareBreakpoint(debugger, breakpoint->address, breakpoint->sw.mode, breakpoint->sw.opcode);
 92		}
 93	}
 94	ARMDebuggerRemoveMemoryShim(debugger);
 95
 96	ARMDebugBreakpointListDeinit(&debugger->breakpoints);
 97	ARMDebugBreakpointListDeinit(&debugger->swBreakpoints);
 98	ARMDebugWatchpointListDeinit(&debugger->watchpoints);
 99}
100
101static void ARMDebuggerEnter(struct mDebuggerPlatform* platform, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) {
102	struct ARMDebugger* debugger = (struct ARMDebugger*) platform;
103	struct ARMCore* cpu = debugger->cpu;
104	cpu->nextEvent = cpu->cycles;
105	if (reason == DEBUGGER_ENTER_BREAKPOINT) {
106		struct ARMDebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->swBreakpoints, _ARMPCAddress(cpu));
107		if (breakpoint && breakpoint->isSw) {
108			info->address = breakpoint->address;
109			if (debugger->clearSoftwareBreakpoint) {
110				debugger->clearSoftwareBreakpoint(debugger, breakpoint->address, breakpoint->sw.mode, breakpoint->sw.opcode);
111			}
112
113			ARMRunFake(cpu, breakpoint->sw.opcode);
114
115			if (debugger->setSoftwareBreakpoint) {
116				debugger->setSoftwareBreakpoint(debugger, breakpoint->address, breakpoint->sw.mode, &breakpoint->sw.opcode);
117			}
118		}
119	}
120	if (debugger->d.p->entered) {
121		debugger->d.p->entered(debugger->d.p, reason, info);
122	}
123}
124
125bool ARMDebuggerSetSoftwareBreakpoint(struct mDebuggerPlatform* d, uint32_t address, enum ExecutionMode mode) {
126	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
127	uint32_t opcode;
128	if (!debugger->setSoftwareBreakpoint || !debugger->setSoftwareBreakpoint(debugger, address, mode, &opcode)) {
129		return false;
130	}
131
132	struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListAppend(&debugger->swBreakpoints);
133	breakpoint->address = address;
134	breakpoint->isSw = true;
135	breakpoint->sw.opcode = opcode;
136	breakpoint->sw.mode = mode;
137
138	return true;
139}
140
141void ARMDebuggerClearSoftwareBreakpoint(struct mDebuggerPlatform* d, uint32_t address) {
142	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
143	if (!debugger->clearSoftwareBreakpoint) {
144		return;
145	}
146
147	struct ARMDebugBreakpoint* breakpoint = NULL;
148	// Clear the stack backwards in case any overlap
149	size_t b;
150	for (b = ARMDebugBreakpointListSize(&debugger->swBreakpoints); b; --b) {
151		breakpoint = ARMDebugBreakpointListGetPointer(&debugger->swBreakpoints, b - 1);
152		if (breakpoint->address == address) {
153			break;
154		}
155		breakpoint = NULL;
156	}
157
158	if (breakpoint) {
159		debugger->clearSoftwareBreakpoint(debugger, address, breakpoint->sw.mode, breakpoint->sw.opcode);
160	}
161}
162
163static void ARMDebuggerSetBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) {
164	UNUSED(segment);
165	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
166	struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListAppend(&debugger->breakpoints);
167	breakpoint->address = address;
168	breakpoint->isSw = false;
169}
170
171static void ARMDebuggerClearBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) {
172	UNUSED(segment);
173	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
174	struct ARMDebugBreakpointList* breakpoints = &debugger->breakpoints;
175	size_t i;
176	for (i = 0; i < ARMDebugBreakpointListSize(breakpoints); ++i) {
177		if (ARMDebugBreakpointListGetPointer(breakpoints, i)->address == address) {
178			ARMDebugBreakpointListShift(breakpoints, i, 1);
179		}
180	}
181}
182
183static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform* d) {
184	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
185	return ARMDebugBreakpointListSize(&debugger->breakpoints) || ARMDebugWatchpointListSize(&debugger->watchpoints);
186}
187
188static void ARMDebuggerSetWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, enum mWatchpointType type) {
189	UNUSED(segment);
190	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
191	if (!ARMDebugWatchpointListSize(&debugger->watchpoints)) {
192		ARMDebuggerInstallMemoryShim(debugger);
193	}
194	struct ARMDebugWatchpoint* watchpoint = ARMDebugWatchpointListAppend(&debugger->watchpoints);
195	watchpoint->address = address;
196	watchpoint->type = type;
197}
198
199static void ARMDebuggerClearWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) {
200	UNUSED(segment);
201	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
202	struct ARMDebugWatchpointList* watchpoints = &debugger->watchpoints;
203	size_t i;
204	for (i = 0; i < ARMDebugWatchpointListSize(watchpoints); ++i) {
205		if (ARMDebugWatchpointListGetPointer(watchpoints, i)->address == address) {
206			ARMDebugWatchpointListShift(watchpoints, i, 1);
207		}
208	}
209	if (!ARMDebugWatchpointListSize(&debugger->watchpoints)) {
210		ARMDebuggerRemoveMemoryShim(debugger);
211	}
212}
213
214static void ARMDebuggerTrace(struct mDebuggerPlatform* d, char* out, size_t* length) {
215	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
216	struct ARMCore* cpu = debugger->cpu;
217
218	char disassembly[64];
219
220	struct ARMInstructionInfo info;
221	if (cpu->executionMode == MODE_ARM) {
222		uint32_t instruction = cpu->prefetch[0];
223		sprintf(disassembly, "%08X: ", instruction);
224		ARMDecodeARM(instruction, &info);
225		ARMDisassemble(&info, cpu->gprs[ARM_PC], disassembly + strlen("00000000: "), sizeof(disassembly) - strlen("00000000: "));
226	} else {
227		struct ARMInstructionInfo info2;
228		struct ARMInstructionInfo combined;
229		uint16_t instruction = cpu->prefetch[0];
230		uint16_t instruction2 = cpu->prefetch[1];
231		ARMDecodeThumb(instruction, &info);
232		ARMDecodeThumb(instruction2, &info2);
233		if (ARMDecodeThumbCombine(&info, &info2, &combined)) {
234			sprintf(disassembly, "%04X%04X: ", instruction, instruction2);
235			ARMDisassemble(&combined, cpu->gprs[ARM_PC], disassembly + strlen("00000000: "), sizeof(disassembly) - strlen("00000000: "));
236		} else {
237			sprintf(disassembly, "    %04X: ", instruction);
238			ARMDisassemble(&info, cpu->gprs[ARM_PC], disassembly + strlen("00000000: "), sizeof(disassembly) - strlen("00000000: "));
239		}
240	}
241
242	*length = snprintf(out, *length, "%08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X cpsr: %08X | %s",
243		               cpu->gprs[0],  cpu->gprs[1],  cpu->gprs[2],  cpu->gprs[3],
244		               cpu->gprs[4],  cpu->gprs[5],  cpu->gprs[6],  cpu->gprs[7],
245		               cpu->gprs[8],  cpu->gprs[9],  cpu->gprs[10], cpu->gprs[11],
246		               cpu->gprs[12], cpu->gprs[13], cpu->gprs[14], cpu->gprs[15],
247		               cpu->cpsr.packed, disassembly);
248}