all repos — mgba @ f3ea4caf84ab2292e65b10245d9343b5ba31cdaf

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