src/lr35902/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/lr35902/debugger/debugger.h>
7
8#include <mgba/core/core.h>
9#include <mgba/internal/debugger/parser.h>
10#include <mgba/internal/lr35902/decoder.h>
11#include <mgba/internal/lr35902/lr35902.h>
12#include <mgba/internal/lr35902/debugger/memory-debugger.h>
13
14DEFINE_VECTOR(LR35902DebugBreakpointList, struct LR35902DebugBreakpoint);
15DEFINE_VECTOR(LR35902DebugWatchpointList, struct LR35902DebugWatchpoint);
16
17static struct LR35902DebugBreakpoint* _lookupBreakpoint(struct LR35902DebugBreakpointList* breakpoints, uint16_t address) {
18 size_t i;
19 for (i = 0; i < LR35902DebugBreakpointListSize(breakpoints); ++i) {
20 if (LR35902DebugBreakpointListGetPointer(breakpoints, i)->address == address) {
21 return LR35902DebugBreakpointListGetPointer(breakpoints, i);
22 }
23 }
24 return 0;
25}
26
27static void _destroyBreakpoint(struct LR35902DebugBreakpoint* breakpoint) {
28 if (breakpoint->condition) {
29 parseFree(breakpoint->condition);
30 free(breakpoint->condition);
31 }
32}
33
34static void _destroyWatchpoint(struct LR35902DebugWatchpoint* watchpoint) {
35 if (watchpoint->condition) {
36 parseFree(watchpoint->condition);
37 free(watchpoint->condition);
38 }
39}
40
41static void LR35902DebuggerCheckBreakpoints(struct mDebuggerPlatform* d) {
42 struct LR35902Debugger* debugger = (struct LR35902Debugger*) d;
43 struct LR35902DebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->breakpoints, debugger->cpu->pc);
44 if (!breakpoint) {
45 return;
46 }
47 if (breakpoint->segment >= 0 && debugger->cpu->memory.currentSegment(debugger->cpu, breakpoint->address) != breakpoint->segment) {
48 return;
49 }
50 if (breakpoint->condition) {
51 int32_t value;
52 int segment;
53 if (!mDebuggerEvaluateParseTree(d->p, breakpoint->condition, &value, &segment) || !(value || segment >= 0)) {
54 return;
55 }
56 }
57 struct mDebuggerEntryInfo info = {
58 .address = breakpoint->address
59 };
60 mDebuggerEnter(d->p, DEBUGGER_ENTER_BREAKPOINT, &info);
61}
62
63static void LR35902DebuggerInit(void* cpu, struct mDebuggerPlatform* platform);
64static void LR35902DebuggerDeinit(struct mDebuggerPlatform* platform);
65
66static void LR35902DebuggerEnter(struct mDebuggerPlatform* d, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info);
67
68static void LR35902DebuggerSetBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
69static void LR35902DebuggerSetConditionalBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment, struct ParseTree* condition);
70static void LR35902DebuggerClearBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
71static void LR35902DebuggerSetWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type);
72static void LR35902DebuggerSetConditionalWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type, struct ParseTree* condition);
73static void LR35902DebuggerClearWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
74static void LR35902DebuggerCheckBreakpoints(struct mDebuggerPlatform*);
75static bool LR35902DebuggerHasBreakpoints(struct mDebuggerPlatform*);
76static void LR35902DebuggerTrace(struct mDebuggerPlatform*, char* out, size_t* length);
77static bool LR35902DebuggerGetRegister(struct mDebuggerPlatform*, const char* name, int32_t* value);
78static bool LR35902DebuggerSetRegister(struct mDebuggerPlatform*, const char* name, int32_t value);
79
80struct mDebuggerPlatform* LR35902DebuggerPlatformCreate(void) {
81 struct mDebuggerPlatform* platform = (struct mDebuggerPlatform*) malloc(sizeof(struct LR35902Debugger));
82 platform->entered = LR35902DebuggerEnter;
83 platform->init = LR35902DebuggerInit;
84 platform->deinit = LR35902DebuggerDeinit;
85 platform->setBreakpoint = LR35902DebuggerSetBreakpoint;
86 platform->setConditionalBreakpoint = LR35902DebuggerSetConditionalBreakpoint;
87 platform->clearBreakpoint = LR35902DebuggerClearBreakpoint;
88 platform->setWatchpoint = LR35902DebuggerSetWatchpoint;
89 platform->setConditionalWatchpoint = LR35902DebuggerSetConditionalWatchpoint;
90 platform->clearWatchpoint = LR35902DebuggerClearWatchpoint;
91 platform->checkBreakpoints = LR35902DebuggerCheckBreakpoints;
92 platform->hasBreakpoints = LR35902DebuggerHasBreakpoints;
93 platform->trace = LR35902DebuggerTrace;
94 platform->getRegister = LR35902DebuggerGetRegister;
95 platform->setRegister = LR35902DebuggerSetRegister;
96 return platform;
97}
98
99void LR35902DebuggerInit(void* cpu, struct mDebuggerPlatform* platform) {
100 struct LR35902Debugger* debugger = (struct LR35902Debugger*) platform;
101 debugger->cpu = cpu;
102 debugger->originalMemory = debugger->cpu->memory;
103 LR35902DebugBreakpointListInit(&debugger->breakpoints, 0);
104 LR35902DebugWatchpointListInit(&debugger->watchpoints, 0);
105}
106
107void LR35902DebuggerDeinit(struct mDebuggerPlatform* platform) {
108 struct LR35902Debugger* debugger = (struct LR35902Debugger*) platform;
109 size_t i;
110 for (i = 0; i < LR35902DebugBreakpointListSize(&debugger->breakpoints); ++i) {
111 _destroyBreakpoint(LR35902DebugBreakpointListGetPointer(&debugger->breakpoints, i));
112 }
113 LR35902DebugBreakpointListDeinit(&debugger->breakpoints);
114
115 for (i = 0; i < LR35902DebugWatchpointListSize(&debugger->watchpoints); ++i) {
116 _destroyWatchpoint(LR35902DebugWatchpointListGetPointer(&debugger->watchpoints, i));
117 }
118 LR35902DebugWatchpointListDeinit(&debugger->watchpoints);
119}
120
121static void LR35902DebuggerEnter(struct mDebuggerPlatform* platform, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) {
122 UNUSED(reason);
123 UNUSED(info);
124 struct LR35902Debugger* debugger = (struct LR35902Debugger*) platform;
125 struct LR35902Core* cpu = debugger->cpu;
126 cpu->nextEvent = cpu->cycles;
127
128 if (debugger->d.p->entered) {
129 debugger->d.p->entered(debugger->d.p, reason, info);
130 }
131}
132
133static void LR35902DebuggerSetBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) {
134 LR35902DebuggerSetConditionalBreakpoint(d, address, segment, NULL);
135}
136
137static void LR35902DebuggerSetConditionalBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, struct ParseTree* condition) {
138 struct LR35902Debugger* debugger = (struct LR35902Debugger*) d;
139 struct LR35902DebugBreakpoint* breakpoint = LR35902DebugBreakpointListAppend(&debugger->breakpoints);
140 breakpoint->address = address;
141 breakpoint->segment = segment;
142 breakpoint->condition = condition;
143}
144
145static void LR35902DebuggerClearBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) {
146 struct LR35902Debugger* debugger = (struct LR35902Debugger*) d;
147 struct LR35902DebugBreakpointList* breakpoints = &debugger->breakpoints;
148 size_t i;
149 for (i = 0; i < LR35902DebugBreakpointListSize(breakpoints); ++i) {
150 struct LR35902DebugBreakpoint* breakpoint = LR35902DebugBreakpointListGetPointer(breakpoints, i);
151 if (breakpoint->address == address && breakpoint->segment == segment) {
152 _destroyBreakpoint(LR35902DebugBreakpointListGetPointer(breakpoints, i));
153 LR35902DebugBreakpointListShift(breakpoints, i, 1);
154 }
155 }
156}
157
158static bool LR35902DebuggerHasBreakpoints(struct mDebuggerPlatform* d) {
159 struct LR35902Debugger* debugger = (struct LR35902Debugger*) d;
160 return LR35902DebugBreakpointListSize(&debugger->breakpoints) || LR35902DebugWatchpointListSize(&debugger->watchpoints);
161}
162
163static void LR35902DebuggerSetWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, enum mWatchpointType type) {
164 LR35902DebuggerSetConditionalWatchpoint(d, address, segment, type, NULL);
165}
166
167static void LR35902DebuggerSetConditionalWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, enum mWatchpointType type, struct ParseTree* condition) {
168 struct LR35902Debugger* debugger = (struct LR35902Debugger*) d;
169 if (!LR35902DebugWatchpointListSize(&debugger->watchpoints)) {
170 LR35902DebuggerInstallMemoryShim(debugger);
171 }
172 struct LR35902DebugWatchpoint* watchpoint = LR35902DebugWatchpointListAppend(&debugger->watchpoints);
173 watchpoint->address = address;
174 watchpoint->type = type;
175 watchpoint->segment = segment;
176 watchpoint->condition = condition;
177}
178
179static void LR35902DebuggerClearWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) {
180 struct LR35902Debugger* debugger = (struct LR35902Debugger*) d;
181 struct LR35902DebugWatchpointList* watchpoints = &debugger->watchpoints;
182 size_t i;
183 for (i = 0; i < LR35902DebugWatchpointListSize(watchpoints); ++i) {
184 struct LR35902DebugWatchpoint* watchpoint = LR35902DebugWatchpointListGetPointer(watchpoints, i);
185 if (watchpoint->address == address && watchpoint->segment == segment) {
186 LR35902DebugWatchpointListShift(watchpoints, i, 1);
187 }
188 }
189 if (!LR35902DebugWatchpointListSize(&debugger->watchpoints)) {
190 LR35902DebuggerRemoveMemoryShim(debugger);
191 }
192}
193
194static void LR35902DebuggerTrace(struct mDebuggerPlatform* d, char* out, size_t* length) {
195 struct LR35902Debugger* debugger = (struct LR35902Debugger*) d;
196 struct LR35902Core* cpu = debugger->cpu;
197
198 char disassembly[64];
199
200 struct LR35902InstructionInfo info = {{0}};
201 char* disPtr = disassembly;
202 uint8_t instruction;
203 uint16_t address = cpu->pc;
204 size_t bytesRemaining = 1;
205 for (bytesRemaining = 1; bytesRemaining; --bytesRemaining) {
206 instruction = debugger->d.p->core->rawRead8(debugger->d.p->core, address, -1);
207 disPtr += snprintf(disPtr, sizeof(disassembly) - (disPtr - disassembly), "%02X", instruction);
208 ++address;
209 bytesRemaining += LR35902Decode(instruction, &info);
210 };
211 disPtr[0] = ':';
212 disPtr[1] = ' ';
213 disPtr += 2;
214 LR35902Disassemble(&info, disPtr, sizeof(disassembly) - (disPtr - disassembly));
215
216 *length = snprintf(out, *length, "A: %02X F: %02X B: %02X C: %02X D: %02X E: %02X H: %02X L: %02X SP: %04X PC: %02X:%04X | %s",
217 cpu->a, cpu->f.packed, cpu->b, cpu->c,
218 cpu->d, cpu->e, cpu->h, cpu->l,
219 cpu->sp, cpu->memory.currentSegment(cpu, cpu->pc), cpu->pc, disassembly);
220}
221
222bool LR35902DebuggerGetRegister(struct mDebuggerPlatform* d, const char* name, int32_t* value) {
223 struct LR35902Debugger* debugger = (struct LR35902Debugger*) d;
224 struct LR35902Core* cpu = debugger->cpu;
225
226 if (strcmp(name, "a") == 0) {
227 *value = cpu->a;
228 return true;
229 }
230 if (strcmp(name, "b") == 0) {
231 *value = cpu->b;
232 return true;
233 }
234 if (strcmp(name, "c") == 0) {
235 *value = cpu->c;
236 return true;
237 }
238 if (strcmp(name, "d") == 0) {
239 *value = cpu->d;
240 return true;
241 }
242 if (strcmp(name, "e") == 0) {
243 *value = cpu->e;
244 return true;
245 }
246 if (strcmp(name, "h") == 0) {
247 *value = cpu->h;
248 return true;
249 }
250 if (strcmp(name, "l") == 0) {
251 *value = cpu->l;
252 return true;
253 }
254 if (strcmp(name, "bc") == 0) {
255 *value = cpu->bc;
256 return true;
257 }
258 if (strcmp(name, "de") == 0) {
259 *value = cpu->de;
260 return true;
261 }
262 if (strcmp(name, "hl") == 0) {
263 *value = cpu->hl;
264 return true;
265 }
266 if (strcmp(name, "af") == 0) {
267 *value = cpu->af;
268 return true;
269 }
270 if (strcmp(name, "pc") == 0) {
271 *value = cpu->pc;
272 return true;
273 }
274 if (strcmp(name, "sp") == 0) {
275 *value = cpu->sp;
276 return true;
277 }
278 if (strcmp(name, "f") == 0) {
279 *value = cpu->f.packed;
280 return true;
281 }
282 return false;
283}
284
285bool LR35902DebuggerSetRegister(struct mDebuggerPlatform* d, const char* name, int32_t value) {
286 struct LR35902Debugger* debugger = (struct LR35902Debugger*) d;
287 struct LR35902Core* cpu = debugger->cpu;
288
289 if (strcmp(name, "a") == 0) {
290 cpu->a = value;
291 return true;
292 }
293 if (strcmp(name, "b") == 0) {
294 cpu->b = value;
295 return true;
296 }
297 if (strcmp(name, "c") == 0) {
298 cpu->c = value;
299 return true;
300 }
301 if (strcmp(name, "d") == 0) {
302 cpu->d = value;
303 return true;
304 }
305 if (strcmp(name, "e") == 0) {
306 cpu->e = value;
307 return true;
308 }
309 if (strcmp(name, "h") == 0) {
310 cpu->h = value;
311 return true;
312 }
313 if (strcmp(name, "l") == 0) {
314 cpu->l = value;
315 return true;
316 }
317 if (strcmp(name, "bc") == 0) {
318 cpu->bc = value;
319 return true;
320 }
321 if (strcmp(name, "de") == 0) {
322 cpu->de = value;
323 return true;
324 }
325 if (strcmp(name, "hl") == 0) {
326 cpu->hl = value;
327 return true;
328 }
329 if (strcmp(name, "af") == 0) {
330 cpu->af = value;
331 cpu->f.packed &= 0xF0;
332 return true;
333 }
334 if (strcmp(name, "pc") == 0) {
335 cpu->pc = value;
336 cpu->memory.setActiveRegion(cpu, cpu->pc);
337 return true;
338 }
339 if (strcmp(name, "sp") == 0) {
340 cpu->sp = value;
341 return true;
342 }
343 if (strcmp(name, "f") == 0) {
344 cpu->f.packed = value & 0xF0;
345 return true;
346 }
347 return false;
348}