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 LR35902DebugBreakpointListInit(&debugger->breakpoints, 0);
103 LR35902DebugWatchpointListInit(&debugger->watchpoints, 0);
104}
105
106void LR35902DebuggerDeinit(struct mDebuggerPlatform* platform) {
107 struct LR35902Debugger* debugger = (struct LR35902Debugger*) platform;
108 size_t i;
109 for (i = 0; i < LR35902DebugBreakpointListSize(&debugger->breakpoints); ++i) {
110 _destroyBreakpoint(LR35902DebugBreakpointListGetPointer(&debugger->breakpoints, i));
111 }
112 LR35902DebugBreakpointListDeinit(&debugger->breakpoints);
113
114 for (i = 0; i < LR35902DebugWatchpointListSize(&debugger->watchpoints); ++i) {
115 _destroyWatchpoint(LR35902DebugWatchpointListGetPointer(&debugger->watchpoints, i));
116 }
117 LR35902DebugWatchpointListDeinit(&debugger->watchpoints);
118}
119
120static void LR35902DebuggerEnter(struct mDebuggerPlatform* platform, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) {
121 UNUSED(reason);
122 UNUSED(info);
123 struct LR35902Debugger* debugger = (struct LR35902Debugger*) platform;
124 struct LR35902Core* cpu = debugger->cpu;
125 cpu->nextEvent = cpu->cycles;
126
127 if (debugger->d.p->entered) {
128 debugger->d.p->entered(debugger->d.p, reason, info);
129 }
130}
131
132static void LR35902DebuggerSetBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) {
133 LR35902DebuggerSetConditionalBreakpoint(d, address, segment, NULL);
134}
135
136static void LR35902DebuggerSetConditionalBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, struct ParseTree* condition) {
137 struct LR35902Debugger* debugger = (struct LR35902Debugger*) d;
138 struct LR35902DebugBreakpoint* breakpoint = LR35902DebugBreakpointListAppend(&debugger->breakpoints);
139 breakpoint->address = address;
140 breakpoint->segment = segment;
141 breakpoint->condition = condition;
142}
143
144static void LR35902DebuggerClearBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) {
145 struct LR35902Debugger* debugger = (struct LR35902Debugger*) d;
146 struct LR35902DebugBreakpointList* breakpoints = &debugger->breakpoints;
147 size_t i;
148 for (i = 0; i < LR35902DebugBreakpointListSize(breakpoints); ++i) {
149 struct LR35902DebugBreakpoint* breakpoint = LR35902DebugBreakpointListGetPointer(breakpoints, i);
150 if (breakpoint->address == address && breakpoint->segment == segment) {
151 _destroyBreakpoint(LR35902DebugBreakpointListGetPointer(breakpoints, i));
152 LR35902DebugBreakpointListShift(breakpoints, i, 1);
153 }
154 }
155}
156
157static bool LR35902DebuggerHasBreakpoints(struct mDebuggerPlatform* d) {
158 struct LR35902Debugger* debugger = (struct LR35902Debugger*) d;
159 return LR35902DebugBreakpointListSize(&debugger->breakpoints) || LR35902DebugWatchpointListSize(&debugger->watchpoints);
160}
161
162static void LR35902DebuggerSetWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, enum mWatchpointType type) {
163 LR35902DebuggerSetConditionalWatchpoint(d, address, segment, type, NULL);
164}
165
166static void LR35902DebuggerSetConditionalWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, enum mWatchpointType type, struct ParseTree* condition) {
167 struct LR35902Debugger* debugger = (struct LR35902Debugger*) d;
168 if (!LR35902DebugWatchpointListSize(&debugger->watchpoints)) {
169 LR35902DebuggerInstallMemoryShim(debugger);
170 }
171 struct LR35902DebugWatchpoint* watchpoint = LR35902DebugWatchpointListAppend(&debugger->watchpoints);
172 watchpoint->address = address;
173 watchpoint->type = type;
174 watchpoint->segment = segment;
175 watchpoint->condition = condition;
176}
177
178static void LR35902DebuggerClearWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) {
179 struct LR35902Debugger* debugger = (struct LR35902Debugger*) d;
180 struct LR35902DebugWatchpointList* watchpoints = &debugger->watchpoints;
181 size_t i;
182 for (i = 0; i < LR35902DebugWatchpointListSize(watchpoints); ++i) {
183 struct LR35902DebugWatchpoint* watchpoint = LR35902DebugWatchpointListGetPointer(watchpoints, i);
184 if (watchpoint->address == address && watchpoint->segment == segment) {
185 LR35902DebugWatchpointListShift(watchpoints, i, 1);
186 }
187 }
188 if (!LR35902DebugWatchpointListSize(&debugger->watchpoints)) {
189 LR35902DebuggerRemoveMemoryShim(debugger);
190 }
191}
192
193static void LR35902DebuggerTrace(struct mDebuggerPlatform* d, char* out, size_t* length) {
194 struct LR35902Debugger* debugger = (struct LR35902Debugger*) d;
195 struct LR35902Core* cpu = debugger->cpu;
196
197 char disassembly[64];
198
199 struct LR35902InstructionInfo info = {{0}};
200 char* disPtr = disassembly;
201 uint8_t instruction;
202 uint16_t address = cpu->pc;
203 size_t bytesRemaining = 1;
204 for (bytesRemaining = 1; bytesRemaining; --bytesRemaining) {
205 instruction = debugger->d.p->core->rawRead8(debugger->d.p->core, address, -1);
206 disPtr += snprintf(disPtr, sizeof(disassembly) - (disPtr - disassembly), "%02X", instruction);
207 ++address;
208 bytesRemaining += LR35902Decode(instruction, &info);
209 };
210 disPtr[0] = ':';
211 disPtr[1] = ' ';
212 disPtr += 2;
213 LR35902Disassemble(&info, disPtr, sizeof(disassembly) - (disPtr - disassembly));
214
215 *length = snprintf(out, *length, "A: %02X F: %02X B: %02X C: %02X D: %02X E: %02X H: %02X L: %02X SP: %04X PC: %04X | %s",
216 cpu->a, cpu->f.packed, cpu->b, cpu->c,
217 cpu->d, cpu->e, cpu->h, cpu->l,
218 cpu->sp, cpu->pc, disassembly);
219}
220
221bool LR35902DebuggerGetRegister(struct mDebuggerPlatform* d, const char* name, int32_t* value) {
222 struct LR35902Debugger* debugger = (struct LR35902Debugger*) d;
223 struct LR35902Core* cpu = debugger->cpu;
224
225 if (strcmp(name, "a") == 0) {
226 *value = cpu->a;
227 return true;
228 }
229 if (strcmp(name, "b") == 0) {
230 *value = cpu->b;
231 return true;
232 }
233 if (strcmp(name, "c") == 0) {
234 *value = cpu->c;
235 return true;
236 }
237 if (strcmp(name, "d") == 0) {
238 *value = cpu->d;
239 return true;
240 }
241 if (strcmp(name, "e") == 0) {
242 *value = cpu->e;
243 return true;
244 }
245 if (strcmp(name, "h") == 0) {
246 *value = cpu->h;
247 return true;
248 }
249 if (strcmp(name, "l") == 0) {
250 *value = cpu->l;
251 return true;
252 }
253 if (strcmp(name, "bc") == 0) {
254 *value = cpu->bc;
255 return true;
256 }
257 if (strcmp(name, "de") == 0) {
258 *value = cpu->de;
259 return true;
260 }
261 if (strcmp(name, "hl") == 0) {
262 *value = cpu->hl;
263 return true;
264 }
265 if (strcmp(name, "af") == 0) {
266 *value = cpu->af;
267 return true;
268 }
269 if (strcmp(name, "pc") == 0) {
270 *value = cpu->pc;
271 return true;
272 }
273 if (strcmp(name, "sp") == 0) {
274 *value = cpu->sp;
275 return true;
276 }
277 if (strcmp(name, "f") == 0) {
278 *value = cpu->f.packed;
279 return true;
280 }
281 return false;
282}
283
284bool LR35902DebuggerSetRegister(struct mDebuggerPlatform* d, const char* name, int32_t value) {
285 struct LR35902Debugger* debugger = (struct LR35902Debugger*) d;
286 struct LR35902Core* cpu = debugger->cpu;
287
288 if (strcmp(name, "a") == 0) {
289 cpu->a = value;
290 return true;
291 }
292 if (strcmp(name, "b") == 0) {
293 cpu->b = value;
294 return true;
295 }
296 if (strcmp(name, "c") == 0) {
297 cpu->c = value;
298 return true;
299 }
300 if (strcmp(name, "d") == 0) {
301 cpu->d = value;
302 return true;
303 }
304 if (strcmp(name, "e") == 0) {
305 cpu->e = value;
306 return true;
307 }
308 if (strcmp(name, "h") == 0) {
309 cpu->h = value;
310 return true;
311 }
312 if (strcmp(name, "l") == 0) {
313 cpu->l = value;
314 return true;
315 }
316 if (strcmp(name, "bc") == 0) {
317 cpu->bc = value;
318 return true;
319 }
320 if (strcmp(name, "de") == 0) {
321 cpu->de = value;
322 return true;
323 }
324 if (strcmp(name, "hl") == 0) {
325 cpu->hl = value;
326 return true;
327 }
328 if (strcmp(name, "af") == 0) {
329 cpu->af = value;
330 cpu->f.packed &= 0xF0;
331 return true;
332 }
333 if (strcmp(name, "pc") == 0) {
334 cpu->pc = value;
335 cpu->memory.setActiveRegion(cpu, cpu->pc);
336 return true;
337 }
338 if (strcmp(name, "sp") == 0) {
339 cpu->sp = value;
340 return true;
341 }
342 if (strcmp(name, "f") == 0) {
343 cpu->f.packed = value & 0xF0;
344 return true;
345 }
346 return false;
347}