all repos — mgba @ 849f80e7a1959923c9d3d47191d0a162f0887bb9

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/isa-inlines.h>
 11#include <mgba/internal/arm/debugger/memory-debugger.h>
 12
 13DEFINE_VECTOR(ARMDebugBreakpointList, struct ARMDebugBreakpoint);
 14DEFINE_VECTOR(ARMDebugWatchpointList, struct ARMDebugWatchpoint);
 15
 16static struct ARMDebugBreakpoint* _lookupBreakpoint(struct ARMDebugBreakpointList* breakpoints, uint32_t address) {
 17	size_t i;
 18	for (i = 0; i < ARMDebugBreakpointListSize(breakpoints); ++i) {
 19		if (ARMDebugBreakpointListGetPointer(breakpoints, i)->address == address) {
 20			return ARMDebugBreakpointListGetPointer(breakpoints, i);
 21		}
 22	}
 23	return 0;
 24}
 25
 26static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform* d) {
 27	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
 28	int instructionLength;
 29	enum ExecutionMode mode = debugger->cpu->cpsr.t;
 30	if (mode == MODE_ARM) {
 31		instructionLength = WORD_SIZE_ARM;
 32	} else {
 33		instructionLength = WORD_SIZE_THUMB;
 34	}
 35	struct ARMDebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->breakpoints, debugger->cpu->gprs[ARM_PC] - instructionLength);
 36	if (!breakpoint) {
 37		return;
 38	}
 39	struct mDebuggerEntryInfo info = {
 40		.address = breakpoint->address,
 41		.breakType = BREAKPOINT_HARDWARE
 42	};
 43	mDebuggerEnter(d->p, DEBUGGER_ENTER_BREAKPOINT, &info);
 44}
 45
 46static void ARMDebuggerInit(void* cpu, struct mDebuggerPlatform* platform);
 47static void ARMDebuggerDeinit(struct mDebuggerPlatform* platform);
 48
 49static void ARMDebuggerEnter(struct mDebuggerPlatform* d, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info);
 50
 51static void ARMDebuggerSetBreakpoint(struct mDebuggerPlatform*, uint32_t address);
 52static void ARMDebuggerClearBreakpoint(struct mDebuggerPlatform*, uint32_t address);
 53static void ARMDebuggerSetWatchpoint(struct mDebuggerPlatform*, uint32_t address, enum mWatchpointType type);
 54static void ARMDebuggerClearWatchpoint(struct mDebuggerPlatform*, uint32_t address);
 55static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform*);
 56static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform*);
 57
 58struct mDebuggerPlatform* ARMDebuggerPlatformCreate(void) {
 59	struct mDebuggerPlatform* platform = (struct mDebuggerPlatform*) malloc(sizeof(struct ARMDebugger));
 60	platform->entered = ARMDebuggerEnter;
 61	platform->init = ARMDebuggerInit;
 62	platform->deinit = ARMDebuggerDeinit;
 63	platform->setBreakpoint = ARMDebuggerSetBreakpoint;
 64	platform->clearBreakpoint = ARMDebuggerClearBreakpoint;
 65	platform->setWatchpoint = ARMDebuggerSetWatchpoint;
 66	platform->clearWatchpoint = ARMDebuggerClearWatchpoint;
 67	platform->checkBreakpoints = ARMDebuggerCheckBreakpoints;
 68	platform->hasBreakpoints = ARMDebuggerHasBreakpoints;
 69	return platform;
 70}
 71
 72void ARMDebuggerInit(void* cpu, struct mDebuggerPlatform* platform) {
 73	struct ARMDebugger* debugger = (struct ARMDebugger*) platform;
 74	debugger->cpu = cpu;
 75	debugger->originalMemory = debugger->cpu->memory;
 76	ARMDebugBreakpointListInit(&debugger->breakpoints, 0);
 77	ARMDebugBreakpointListInit(&debugger->swBreakpoints, 0);
 78	ARMDebugWatchpointListInit(&debugger->watchpoints, 0);
 79}
 80
 81void ARMDebuggerDeinit(struct mDebuggerPlatform* platform) {
 82	struct ARMDebugger* debugger = (struct ARMDebugger*) platform;
 83	if (debugger->clearSoftwareBreakpoint) {
 84		// Clear the stack backwards in case any overlap
 85		size_t b;
 86		for (b = ARMDebugBreakpointListSize(&debugger->swBreakpoints); b; --b) {
 87			struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListGetPointer(&debugger->swBreakpoints, b - 1);
 88			debugger->clearSoftwareBreakpoint(debugger, breakpoint->address, breakpoint->sw.mode, breakpoint->sw.opcode);
 89		}
 90	}
 91	ARMDebuggerRemoveMemoryShim(debugger);
 92
 93	ARMDebugBreakpointListDeinit(&debugger->breakpoints);
 94	ARMDebugBreakpointListDeinit(&debugger->swBreakpoints);
 95	ARMDebugWatchpointListDeinit(&debugger->watchpoints);
 96}
 97
 98static void ARMDebuggerEnter(struct mDebuggerPlatform* platform, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) {
 99	struct ARMDebugger* debugger = (struct ARMDebugger*) platform;
100	struct ARMCore* cpu = debugger->cpu;
101	cpu->nextEvent = cpu->cycles;
102	if (reason == DEBUGGER_ENTER_BREAKPOINT) {
103		struct ARMDebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->swBreakpoints, _ARMPCAddress(cpu));
104		if (breakpoint && breakpoint->isSw) {
105			info->address = breakpoint->address;
106			if (debugger->clearSoftwareBreakpoint) {
107				debugger->clearSoftwareBreakpoint(debugger, breakpoint->address, breakpoint->sw.mode, breakpoint->sw.opcode);
108			}
109
110			ARMRunFake(cpu, breakpoint->sw.opcode);
111
112			if (debugger->setSoftwareBreakpoint) {
113				debugger->setSoftwareBreakpoint(debugger, breakpoint->address, breakpoint->sw.mode, &breakpoint->sw.opcode);
114			}
115		}
116	}
117	if (debugger->d.p->entered) {
118		debugger->d.p->entered(debugger->d.p, reason, info);
119	}
120}
121
122bool ARMDebuggerSetSoftwareBreakpoint(struct mDebuggerPlatform* d, uint32_t address, enum ExecutionMode mode) {
123	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
124	uint32_t opcode;
125	if (!debugger->setSoftwareBreakpoint || !debugger->setSoftwareBreakpoint(debugger, address, mode, &opcode)) {
126		return false;
127	}
128
129	struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListAppend(&debugger->swBreakpoints);
130	breakpoint->address = address;
131	breakpoint->isSw = true;
132	breakpoint->sw.opcode = opcode;
133	breakpoint->sw.mode = mode;
134
135	return true;
136}
137
138void ARMDebuggerClearSoftwareBreakpoint(struct mDebuggerPlatform* d, uint32_t address) {
139	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
140	if (!debugger->clearSoftwareBreakpoint) {
141		return;
142	}
143
144	struct ARMDebugBreakpoint* breakpoint = NULL;
145	// Clear the stack backwards in case any overlap
146	size_t b;
147	for (b = ARMDebugBreakpointListSize(&debugger->swBreakpoints); b; --b) {
148		breakpoint = ARMDebugBreakpointListGetPointer(&debugger->swBreakpoints, b - 1);
149		if (breakpoint->address == address) {
150			break;
151		}
152		breakpoint = NULL;
153	}
154
155	if (breakpoint) {
156		debugger->clearSoftwareBreakpoint(debugger, address, breakpoint->sw.mode, breakpoint->sw.opcode);
157	}
158}
159
160static void ARMDebuggerSetBreakpoint(struct mDebuggerPlatform* d, uint32_t address) {
161	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
162	struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListAppend(&debugger->breakpoints);
163	breakpoint->address = address;
164	breakpoint->isSw = false;
165}
166
167static void ARMDebuggerClearBreakpoint(struct mDebuggerPlatform* d, uint32_t address) {
168	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
169	struct ARMDebugBreakpointList* breakpoints = &debugger->breakpoints;
170	size_t i;
171	for (i = 0; i < ARMDebugBreakpointListSize(breakpoints); ++i) {
172		if (ARMDebugBreakpointListGetPointer(breakpoints, i)->address == address) {
173			ARMDebugBreakpointListShift(breakpoints, i, 1);
174		}
175	}
176}
177
178static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform* d) {
179	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
180	return ARMDebugBreakpointListSize(&debugger->breakpoints) || ARMDebugWatchpointListSize(&debugger->watchpoints);
181}
182
183static void ARMDebuggerSetWatchpoint(struct mDebuggerPlatform* d, uint32_t address, enum mWatchpointType type) {
184	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
185	if (!ARMDebugWatchpointListSize(&debugger->watchpoints)) {
186		ARMDebuggerInstallMemoryShim(debugger);
187	}
188	struct ARMDebugWatchpoint* watchpoint = ARMDebugWatchpointListAppend(&debugger->watchpoints);
189	watchpoint->address = address;
190	watchpoint->type = type;
191}
192
193static void ARMDebuggerClearWatchpoint(struct mDebuggerPlatform* d, uint32_t address) {
194	struct ARMDebugger* debugger = (struct ARMDebugger*) d;
195	struct ARMDebugWatchpointList* watchpoints = &debugger->watchpoints;
196	size_t i;
197	for (i = 0; i < ARMDebugWatchpointListSize(watchpoints); ++i) {
198		if (ARMDebugWatchpointListGetPointer(watchpoints, i)->address == address) {
199			ARMDebugWatchpointListShift(watchpoints, i, 1);
200		}
201	}
202	if (!ARMDebugWatchpointListSize(&debugger->watchpoints)) {
203		ARMDebuggerRemoveMemoryShim(debugger);
204	}
205}