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}