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}