all repos — mgba @ 8a5183df0b927429ac42cc2a4a141c2f7d56202a

mGBA Game Boy Advance Emulator

src/debugger/cli-debugger.c (view raw)

  1/* Copyright (c) 2013-2014 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/debugger/cli-debugger.h>
  7
  8#include <mgba/internal/debugger/symbols.h>
  9
 10#include <mgba/core/core.h>
 11#include <mgba/core/version.h>
 12#include <mgba/internal/debugger/parser.h>
 13#include <mgba-util/string.h>
 14
 15#if ENABLE_SCRIPTING
 16#include <mgba/core/scripting.h>
 17#endif
 18
 19#if !defined(NDEBUG) && !defined(_WIN32)
 20#include <signal.h>
 21#endif
 22
 23#ifdef USE_PTHREADS
 24#include <pthread.h>
 25#endif
 26
 27const char* ERROR_MISSING_ARGS = "Arguments missing"; // TODO: share
 28const char* ERROR_OVERFLOW = "Arguments overflow";
 29
 30#if !defined(NDEBUG) && !defined(_WIN32)
 31static void _breakInto(struct CLIDebugger*, struct CLIDebugVector*);
 32#endif
 33static void _continue(struct CLIDebugger*, struct CLIDebugVector*);
 34static void _disassemble(struct CLIDebugger*, struct CLIDebugVector*);
 35static void _next(struct CLIDebugger*, struct CLIDebugVector*);
 36static void _print(struct CLIDebugger*, struct CLIDebugVector*);
 37static void _printBin(struct CLIDebugger*, struct CLIDebugVector*);
 38static void _printHex(struct CLIDebugger*, struct CLIDebugVector*);
 39static void _printStatus(struct CLIDebugger*, struct CLIDebugVector*);
 40static void _printHelp(struct CLIDebugger*, struct CLIDebugVector*);
 41static void _quit(struct CLIDebugger*, struct CLIDebugVector*);
 42static void _readByte(struct CLIDebugger*, struct CLIDebugVector*);
 43static void _reset(struct CLIDebugger*, struct CLIDebugVector*);
 44static void _readHalfword(struct CLIDebugger*, struct CLIDebugVector*);
 45static void _readWord(struct CLIDebugger*, struct CLIDebugVector*);
 46static void _setBreakpoint(struct CLIDebugger*, struct CLIDebugVector*);
 47static void _clearBreakpoint(struct CLIDebugger*, struct CLIDebugVector*);
 48static void _setWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
 49static void _setReadWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
 50static void _setWriteWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
 51static void _trace(struct CLIDebugger*, struct CLIDebugVector*);
 52static void _writeByte(struct CLIDebugger*, struct CLIDebugVector*);
 53static void _writeHalfword(struct CLIDebugger*, struct CLIDebugVector*);
 54static void _writeWord(struct CLIDebugger*, struct CLIDebugVector*);
 55static void _dumpByte(struct CLIDebugger*, struct CLIDebugVector*);
 56static void _dumpHalfword(struct CLIDebugger*, struct CLIDebugVector*);
 57static void _dumpWord(struct CLIDebugger*, struct CLIDebugVector*);
 58#ifdef ENABLE_SCRIPTING
 59static void _source(struct CLIDebugger*, struct CLIDebugVector*);
 60#endif
 61
 62static struct CLIDebuggerCommandSummary _debuggerCommands[] = {
 63	{ "b", _setBreakpoint, CLIDVParse, "Set a breakpoint" },
 64	{ "break", _setBreakpoint, CLIDVParse, "Set a breakpoint" },
 65	{ "c", _continue, 0, "Continue execution" },
 66	{ "continue", _continue, 0, "Continue execution" },
 67	{ "d", _clearBreakpoint, CLIDVParse, "Delete a breakpoint" },
 68	{ "delete", _clearBreakpoint, CLIDVParse, "Delete a breakpoint" },
 69	{ "dis", _disassemble, CLIDVParse, "Disassemble instructions" },
 70	{ "disasm", _disassemble, CLIDVParse, "Disassemble instructions" },
 71	{ "disassemble", _disassemble, CLIDVParse, "Disassemble instructions" },
 72	{ "h", _printHelp, CLIDVStringParse, "Print help" },
 73	{ "help", _printHelp, CLIDVStringParse, "Print help" },
 74	{ "i", _printStatus, 0, "Print the current status" },
 75	{ "info", _printStatus, 0, "Print the current status" },
 76	{ "n", _next, 0, "Execute next instruction" },
 77	{ "next", _next, 0, "Execute next instruction" },
 78	{ "p", _print, CLIDVParse, "Print a value" },
 79	{ "p/t", _printBin, CLIDVParse, "Print a value as binary" },
 80	{ "p/x", _printHex, CLIDVParse, "Print a value as hexadecimal" },
 81	{ "print", _print, CLIDVParse, "Print a value" },
 82	{ "print/t", _printBin, CLIDVParse, "Print a value as binary" },
 83	{ "print/x", _printHex, CLIDVParse, "Print a value as hexadecimal" },
 84	{ "q", _quit, 0, "Quit the emulator" },
 85	{ "quit", _quit, 0, "Quit the emulator" },
 86	{ "reset", _reset, 0, "Reset the emulation" },
 87	{ "r/1", _readByte, CLIDVParse, "Read a byte from a specified offset" },
 88	{ "r/2", _readHalfword, CLIDVParse, "Read a halfword from a specified offset" },
 89	{ "r/4", _readWord, CLIDVParse, "Read a word from a specified offset" },
 90	{ "status", _printStatus, 0, "Print the current status" },
 91	{ "trace", _trace, CLIDVParse, "Trace a fixed number of instructions" },
 92	{ "w", _setWatchpoint, CLIDVParse, "Set a watchpoint" },
 93	{ "w/1", _writeByte, CLIDVParse, "Write a byte at a specified offset" },
 94	{ "w/2", _writeHalfword, CLIDVParse, "Write a halfword at a specified offset" },
 95	{ "w/4", _writeWord, CLIDVParse, "Write a word at a specified offset" },
 96	{ "watch", _setWatchpoint, CLIDVParse, "Set a watchpoint" },
 97	{ "watch/r", _setReadWatchpoint, CLIDVParse, "Set a read watchpoint" },
 98	{ "watch/w", _setWriteWatchpoint, CLIDVParse, "Set a write watchpoint" },
 99	{ "x/1", _dumpByte, CLIDVParse, "Examine bytes at a specified offset" },
100	{ "x/2", _dumpHalfword, CLIDVParse, "Examine halfwords at a specified offset" },
101	{ "x/4", _dumpWord, CLIDVParse, "Examine words at a specified offset" },
102#ifdef ENABLE_SCRIPTING
103	{ "source", _source, CLIDVStringParse, "Load a script" },
104#endif
105#if !defined(NDEBUG) && !defined(_WIN32)
106	{ "!", _breakInto, 0, "Break into attached debugger (for developers)" },
107#endif
108	{ 0, 0, 0, 0 }
109};
110
111#if !defined(NDEBUG) && !defined(_WIN32)
112static void _handleDeath(int sig) {
113	UNUSED(sig);
114	printf("No debugger attached!\n");
115}
116
117static void _breakInto(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
118	UNUSED(debugger);
119	UNUSED(dv);
120	struct sigaction sa, osa;
121	sa.sa_handler = _handleDeath;
122	sigemptyset(&sa.sa_mask);
123	sigaddset(&sa.sa_mask, SIGTRAP);
124	sa.sa_flags = SA_RESTART;
125	sigaction(SIGTRAP, &sa, &osa);
126#ifdef USE_PTHREADS
127	pthread_kill(pthread_self(), SIGTRAP);
128#else
129	kill(getpid(), SIGTRAP);
130#endif
131	sigaction(SIGTRAP, &osa, 0);
132}
133#endif
134
135static void _continue(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
136	UNUSED(dv);
137	debugger->d.state = DEBUGGER_RUNNING;
138}
139
140static void _next(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
141	UNUSED(dv);
142	debugger->d.core->step(debugger->d.core);
143	_printStatus(debugger, 0);
144}
145
146static void _disassemble(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
147	debugger->system->disassemble(debugger->system, dv);
148}
149
150static void _print(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
151	for (; dv; dv = dv->next) {
152		if (dv->segmentValue >= 0) {
153			debugger->backend->printf(debugger->backend, " $%02X:%04X", dv->segmentValue, dv->intValue);
154			continue;
155		}
156		debugger->backend->printf(debugger->backend, " %u", dv->intValue);
157	}
158	debugger->backend->printf(debugger->backend, "\n");
159}
160
161static void _printBin(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
162	for (; dv; dv = dv->next) {
163		debugger->backend->printf(debugger->backend, " 0b");
164		int i = 32;
165		while (i--) {
166			debugger->backend->printf(debugger->backend, "%u", (dv->intValue >> i) & 1);
167		}
168	}
169	debugger->backend->printf(debugger->backend, "\n");
170}
171
172static void _printHex(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
173	for (; dv; dv = dv->next) {
174		debugger->backend->printf(debugger->backend, " 0x%08X", dv->intValue);
175	}
176	debugger->backend->printf(debugger->backend, "\n");
177}
178
179static void _printHelp(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
180	UNUSED(dv);
181	if (!dv) {
182		debugger->backend->printf(debugger->backend, "Generic commands:\n");
183		int i;
184		for (i = 0; _debuggerCommands[i].name; ++i) {
185			debugger->backend->printf(debugger->backend, "%-10s %s\n", _debuggerCommands[i].name, _debuggerCommands[i].summary);
186		}
187		if (debugger->system) {
188			debugger->backend->printf(debugger->backend, "%s commands:\n", debugger->system->platformName);
189			for (i = 0; debugger->system->platformCommands[i].name; ++i) {
190				debugger->backend->printf(debugger->backend, "%-10s %s\n", debugger->system->platformCommands[i].name, debugger->system->platformCommands[i].summary);
191			}
192			debugger->backend->printf(debugger->backend, "%s commands:\n", debugger->system->name);
193			for (i = 0; debugger->system->commands[i].name; ++i) {
194				debugger->backend->printf(debugger->backend, "%-10s %s\n", debugger->system->commands[i].name, debugger->system->commands[i].summary);
195			}
196		}
197	} else {
198		int i;
199		for (i = 0; _debuggerCommands[i].name; ++i) {
200			if (strcmp(_debuggerCommands[i].name, dv->charValue) == 0) {
201				debugger->backend->printf(debugger->backend, " %s\n", _debuggerCommands[i].summary);
202			}
203		}
204		if (debugger->system) {
205			for (i = 0; debugger->system->platformCommands[i].name; ++i) {
206				if (strcmp(debugger->system->platformCommands[i].name, dv->charValue) == 0) {
207					debugger->backend->printf(debugger->backend, " %s\n", debugger->system->platformCommands[i].summary);
208				}
209			}
210			for (i = 0; debugger->system->commands[i].name; ++i) {
211				if (strcmp(debugger->system->commands[i].name, dv->charValue) == 0) {
212					debugger->backend->printf(debugger->backend, " %s\n", debugger->system->commands[i].summary);
213				}
214			}
215		}
216	}
217}
218
219static void _quit(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
220	UNUSED(dv);
221	debugger->d.state = DEBUGGER_SHUTDOWN;
222}
223
224static void _readByte(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
225	if (!dv || dv->type != CLIDV_INT_TYPE) {
226		debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
227		return;
228	}
229	uint32_t address = dv->intValue;
230	uint8_t value;
231	if (dv->segmentValue >= 0) {
232		value = debugger->d.core->rawRead8(debugger->d.core, address, dv->segmentValue);
233	} else {
234		value = debugger->d.core->busRead8(debugger->d.core, address);
235	}
236	debugger->backend->printf(debugger->backend, " 0x%02X\n", value);
237}
238
239static void _reset(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
240	UNUSED(dv);
241	debugger->d.core->reset(debugger->d.core);
242	_printStatus(debugger, 0);
243}
244
245static void _readHalfword(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
246	if (!dv || dv->type != CLIDV_INT_TYPE) {
247		debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
248		return;
249	}
250	uint32_t address = dv->intValue;
251	uint16_t value;
252	if (dv->segmentValue >= 0) {
253		value = debugger->d.core->rawRead16(debugger->d.core, address & -1, dv->segmentValue);
254	} else {
255		value = debugger->d.core->busRead16(debugger->d.core, address & ~1);
256	}
257	debugger->backend->printf(debugger->backend, " 0x%04X\n", value);
258}
259
260static void _readWord(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
261	if (!dv || dv->type != CLIDV_INT_TYPE) {
262		debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
263		return;
264	}
265	uint32_t address = dv->intValue;
266	uint32_t value;
267	if (dv->segmentValue >= 0) {
268		value = debugger->d.core->rawRead32(debugger->d.core, address & -3, dv->segmentValue);
269	} else {
270		value = debugger->d.core->busRead32(debugger->d.core, address & ~3);
271	}
272	debugger->backend->printf(debugger->backend, " 0x%08X\n", value);
273}
274
275static void _writeByte(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
276	if (!dv || dv->type != CLIDV_INT_TYPE) {
277		debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
278		return;
279	}
280	if (!dv->next || dv->next->type != CLIDV_INT_TYPE) {
281		debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
282		return;
283	}
284	uint32_t address = dv->intValue;
285	uint32_t value = dv->next->intValue;
286	if (value > 0xFF) {
287		debugger->backend->printf(debugger->backend, "%s\n", ERROR_OVERFLOW);
288		return;
289	}
290	if (dv->segmentValue >= 0) {
291		debugger->d.core->rawWrite8(debugger->d.core, address, value, dv->segmentValue);
292	} else {
293		debugger->d.core->busWrite8(debugger->d.core, address, value);
294	}
295}
296
297static void _writeHalfword(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
298	if (!dv || dv->type != CLIDV_INT_TYPE) {
299		debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
300		return;
301	}
302	if (!dv->next || dv->next->type != CLIDV_INT_TYPE) {
303		debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
304		return;
305	}
306	uint32_t address = dv->intValue;
307	uint32_t value = dv->next->intValue;
308	if (value > 0xFFFF) {
309		debugger->backend->printf(debugger->backend, "%s\n", ERROR_OVERFLOW);
310		return;
311	}
312	if (dv->segmentValue >= 0) {
313		debugger->d.core->rawWrite16(debugger->d.core, address, value, dv->segmentValue);
314	} else {
315		debugger->d.core->busWrite16(debugger->d.core, address, value);
316	}
317}
318
319static void _writeWord(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
320	if (!dv || dv->type != CLIDV_INT_TYPE) {
321		debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
322		return;
323	}
324	if (!dv->next || dv->next->type != CLIDV_INT_TYPE) {
325		debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
326		return;
327	}
328	uint32_t address = dv->intValue;
329	uint32_t value = dv->next->intValue;
330	if (dv->segmentValue >= 0) {
331		debugger->d.core->rawWrite32(debugger->d.core, address, value, dv->segmentValue);
332	} else {
333		debugger->d.core->busWrite32(debugger->d.core, address, value);
334	}
335}
336
337static void _dumpByte(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
338	if (!dv || dv->type != CLIDV_INT_TYPE) {
339		debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
340		return;
341	}
342	uint32_t address = dv->intValue;
343	uint32_t words = 16;
344	if (dv->next && dv->next->type == CLIDV_INT_TYPE) {
345		words = dv->next->intValue;
346	}
347	while (words) {
348		uint32_t line = 16;
349		if (line > words) {
350			line = words;
351		}
352		debugger->backend->printf(debugger->backend, "0x%08X:", address);
353		for (; line > 0; --line, ++address, --words) {
354			uint32_t value;
355			if (dv->segmentValue >= 0) {
356				value = debugger->d.core->rawRead8(debugger->d.core, address, dv->segmentValue);
357			} else {
358				value = debugger->d.core->busRead8(debugger->d.core, address);
359			}
360			debugger->backend->printf(debugger->backend, " %02X", value);
361		}
362		debugger->backend->printf(debugger->backend, "\n");
363	}
364}
365
366static void _dumpHalfword(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
367	if (!dv || dv->type != CLIDV_INT_TYPE) {
368		debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
369		return;
370	}
371	uint32_t address = dv->intValue;
372	uint32_t words = 8;
373	if (dv->next && dv->next->type == CLIDV_INT_TYPE) {
374		words = dv->next->intValue;
375	}
376	while (words) {
377		uint32_t line = 8;
378		if (line > words) {
379			line = words;
380		}
381		debugger->backend->printf(debugger->backend, "0x%08X:", address);
382		for (; line > 0; --line, address += 2, --words) {
383			uint32_t value;
384			if (dv->segmentValue >= 0) {
385				value = debugger->d.core->rawRead16(debugger->d.core, address, dv->segmentValue);
386			} else {
387				value = debugger->d.core->busRead16(debugger->d.core, address);
388			}
389			debugger->backend->printf(debugger->backend, " %04X", value);
390		}
391		debugger->backend->printf(debugger->backend, "\n");
392	}
393}
394
395static void _dumpWord(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
396	if (!dv || dv->type != CLIDV_INT_TYPE) {
397		debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
398		return;
399	}
400	uint32_t address = dv->intValue;
401	uint32_t words = 4;
402	if (dv->next && dv->next->type == CLIDV_INT_TYPE) {
403		words = dv->next->intValue;
404	}
405	while (words) {
406		uint32_t line = 4;
407		if (line > words) {
408			line = words;
409		}
410		debugger->backend->printf(debugger->backend, "0x%08X:", address);
411		for (; line > 0; --line, address += 4, --words) {
412			uint32_t value;
413			if (dv->segmentValue >= 0) {
414				value = debugger->d.core->rawRead32(debugger->d.core, address, dv->segmentValue);
415			} else {
416				value = debugger->d.core->busRead32(debugger->d.core, address);
417			}
418			debugger->backend->printf(debugger->backend, " %08X", value);
419		}
420		debugger->backend->printf(debugger->backend, "\n");
421	}
422}
423
424#ifdef ENABLE_SCRIPTING
425static void _source(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
426	if (!dv) {
427		debugger->backend->printf(debugger->backend, "Needs a filename\n");
428		return;
429	}
430	if (debugger->d.bridge && mScriptBridgeLoadScript(debugger->d.bridge, dv->charValue)) {
431		mScriptBridgeRun(debugger->d.bridge);
432	} else {
433		debugger->backend->printf(debugger->backend, "Failed to load script\n");
434	}
435}
436#endif
437
438static void _setBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
439	if (!dv || dv->type != CLIDV_INT_TYPE) {
440		debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
441		return;
442	}
443	uint32_t address = dv->intValue;
444	debugger->d.platform->setBreakpoint(debugger->d.platform, address, dv->segmentValue);
445}
446
447static void _setWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
448	if (!dv || dv->type != CLIDV_INT_TYPE) {
449		debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
450		return;
451	}
452	if (!debugger->d.platform->setWatchpoint) {
453		debugger->backend->printf(debugger->backend, "Watchpoints are not supported by this platform.\n");
454		return;
455	}
456	uint32_t address = dv->intValue;
457	debugger->d.platform->setWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_RW);
458}
459
460static void _setReadWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
461	if (!dv || dv->type != CLIDV_INT_TYPE) {
462		debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
463		return;
464	}
465	if (!debugger->d.platform->setWatchpoint) {
466		debugger->backend->printf(debugger->backend, "Watchpoints are not supported by this platform.\n");
467		return;
468	}
469	uint32_t address = dv->intValue;
470	debugger->d.platform->setWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_READ);
471}
472
473static void _setWriteWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
474	if (!dv || dv->type != CLIDV_INT_TYPE) {
475		debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
476		return;
477	}
478	if (!debugger->d.platform->setWatchpoint) {
479		debugger->backend->printf(debugger->backend, "Watchpoints are not supported by this platform.\n");
480		return;
481	}
482	uint32_t address = dv->intValue;
483	debugger->d.platform->setWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_WRITE);
484}
485
486static void _clearBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
487	if (!dv || dv->type != CLIDV_INT_TYPE) {
488		debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
489		return;
490	}
491	uint32_t address = dv->intValue;
492	debugger->d.platform->clearBreakpoint(debugger->d.platform, address, dv->segmentValue);
493	if (debugger->d.platform->clearWatchpoint) {
494		debugger->d.platform->clearWatchpoint(debugger->d.platform, address, dv->segmentValue);
495	}
496}
497
498static void _trace(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
499	if (!dv || dv->type != CLIDV_INT_TYPE) {
500		debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
501		return;
502	}
503
504	char trace[1024];
505	trace[sizeof(trace) - 1] = '\0';
506
507	int i;
508	for (i = 0; i < dv->intValue; ++i) {
509		debugger->d.core->step(debugger->d.core);
510		size_t traceSize = sizeof(trace) - 1;
511		debugger->d.platform->trace(debugger->d.platform, trace, &traceSize);
512		if (traceSize + 1 < sizeof(trace)) {
513			trace[traceSize + 1] = '\0';
514		}
515		debugger->backend->printf(debugger->backend, "%s\n", trace);
516	}
517}
518
519static void _printStatus(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
520	UNUSED(dv);
521	debugger->system->printStatus(debugger->system);
522}
523
524static uint32_t _performOperation(enum Operation operation, uint32_t current, uint32_t next, struct CLIDebugVector* dv) {
525	switch (operation) {
526	case OP_ASSIGN:
527		current = next;
528		break;
529	case OP_ADD:
530		current += next;
531		break;
532	case OP_SUBTRACT:
533		current -= next;
534		break;
535	case OP_MULTIPLY:
536		current *= next;
537		break;
538	case OP_DIVIDE:
539		if (next != 0) {
540			current /= next;
541		} else {
542			dv->type = CLIDV_ERROR_TYPE;
543			return 0;
544		}
545		break;
546	}
547	return current;
548}
549
550static void _lookupIdentifier(struct mDebugger* debugger, const char* name, struct CLIDebugVector* dv) {
551	struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
552	if (cliDebugger->system) {
553		uint32_t value;
554#ifdef ENABLE_SCRIPTING
555		if (debugger->bridge && mScriptBridgeLookupSymbol(debugger->bridge, name, &dv->intValue)) {
556			return;
557		}
558#endif
559		if (debugger->core->symbolTable && mDebuggerSymbolLookup(debugger->core->symbolTable, name, &dv->intValue, &dv->segmentValue)) {
560			return;
561		}
562		value = cliDebugger->system->lookupPlatformIdentifier(cliDebugger->system, name, dv);
563		if (dv->type != CLIDV_ERROR_TYPE) {
564			dv->intValue = value;
565			return;
566		}
567		dv->type = CLIDV_INT_TYPE;
568		value = cliDebugger->system->lookupIdentifier(cliDebugger->system, name, dv);
569		if (dv->type != CLIDV_ERROR_TYPE) {
570			dv->intValue = value;
571			return;
572		}
573	}
574	dv->type = CLIDV_ERROR_TYPE;
575}
576
577static void _evaluateParseTree(struct mDebugger* debugger, struct ParseTree* tree, struct CLIDebugVector* dv) {
578	int32_t lhs, rhs;
579	switch (tree->token.type) {
580	case TOKEN_UINT_TYPE:
581		dv->intValue = tree->token.uintValue;
582		break;
583	case TOKEN_SEGMENT_TYPE:
584		_evaluateParseTree(debugger, tree->lhs, dv);
585		dv->segmentValue = dv->intValue;
586		_evaluateParseTree(debugger, tree->rhs, dv);
587		break;
588	case TOKEN_OPERATOR_TYPE:
589		_evaluateParseTree(debugger, tree->lhs, dv);
590		lhs = dv->intValue;
591		_evaluateParseTree(debugger, tree->rhs, dv);
592		rhs = dv->intValue;
593		dv->intValue = _performOperation(tree->token.operatorValue, lhs, rhs, dv);
594		break;
595	case TOKEN_IDENTIFIER_TYPE:
596		_lookupIdentifier(debugger, tree->token.identifierValue, dv);
597		break;
598	case TOKEN_ERROR_TYPE:
599	default:
600		dv->type = CLIDV_ERROR_TYPE;
601	}
602}
603
604struct CLIDebugVector* CLIDVParse(struct CLIDebugger* debugger, const char* string, size_t length) {
605	if (!string || length < 1) {
606		return 0;
607	}
608
609	struct CLIDebugVector dvTemp = { .type = CLIDV_INT_TYPE, .segmentValue = -1 };
610
611	struct LexVector lv = { .next = 0 };
612	size_t adjusted = lexExpression(&lv, string, length);
613	if (adjusted > length) {
614		dvTemp.type = CLIDV_ERROR_TYPE;
615		lexFree(lv.next);
616	}
617
618	struct ParseTree tree;
619	parseLexedExpression(&tree, &lv);
620	if (tree.token.type == TOKEN_ERROR_TYPE) {
621		dvTemp.type = CLIDV_ERROR_TYPE;
622	} else {
623		_evaluateParseTree(&debugger->d, &tree, &dvTemp);
624	}
625
626	parseFree(tree.lhs);
627	parseFree(tree.rhs);
628
629	length -= adjusted;
630	string += adjusted;
631
632	struct CLIDebugVector* dv = malloc(sizeof(struct CLIDebugVector));
633	if (dvTemp.type == CLIDV_ERROR_TYPE) {
634		dv->type = CLIDV_ERROR_TYPE;
635		dv->next = 0;
636	} else {
637		*dv = dvTemp;
638		if (string[0] == ' ') {
639			dv->next = CLIDVParse(debugger, string + 1, length - 1);
640			if (dv->next && dv->next->type == CLIDV_ERROR_TYPE) {
641				dv->type = CLIDV_ERROR_TYPE;
642			}
643		}
644	}
645	return dv;
646}
647
648struct CLIDebugVector* CLIDVStringParse(struct CLIDebugger* debugger, const char* string, size_t length) {
649	if (!string || length < 1) {
650		return 0;
651	}
652
653	struct CLIDebugVector dvTemp = { .type = CLIDV_CHAR_TYPE };
654
655	size_t adjusted;
656	const char* next = strchr(string, ' ');
657	if (next) {
658		adjusted = next - string;
659	} else {
660		adjusted = length;
661	}
662	dvTemp.charValue = strndup(string, adjusted);
663
664	length -= adjusted;
665	string += adjusted;
666
667	struct CLIDebugVector* dv = malloc(sizeof(struct CLIDebugVector));
668	*dv = dvTemp;
669	if (string[0] == ' ') {
670		dv->next = CLIDVStringParse(debugger, string + 1, length - 1);
671		if (dv->next && dv->next->type == CLIDV_ERROR_TYPE) {
672			dv->type = CLIDV_ERROR_TYPE;
673		}
674	}
675	return dv;
676}
677
678static void _DVFree(struct CLIDebugVector* dv) {
679	struct CLIDebugVector* next;
680	while (dv) {
681		next = dv->next;
682		if (dv->type == CLIDV_CHAR_TYPE) {
683			free(dv->charValue);
684		}
685		free(dv);
686		dv = next;
687	}
688}
689
690static int _tryCommands(struct CLIDebugger* debugger, struct CLIDebuggerCommandSummary* commands, const char* command, size_t commandLen, const char* args, size_t argsLen) {
691	struct CLIDebugVector* dv = 0;
692	int i;
693	const char* name;
694	for (i = 0; (name = commands[i].name); ++i) {
695		if (strlen(name) != commandLen) {
696			continue;
697		}
698		if (strncasecmp(name, command, commandLen) == 0) {
699			if (commands[i].parser) {
700				if (args) {
701					dv = commands[i].parser(debugger, args, argsLen);
702					if (dv && dv->type == CLIDV_ERROR_TYPE) {
703						debugger->backend->printf(debugger->backend, "Parse error\n");
704						_DVFree(dv);
705						return false;
706					}
707				}
708			} else if (args) {
709				debugger->backend->printf(debugger->backend, "Wrong number of arguments\n");
710				return false;
711			}
712			commands[i].command(debugger, dv);
713			_DVFree(dv);
714			return true;
715		}
716	}
717	return -1;
718}
719
720static bool _parse(struct CLIDebugger* debugger, const char* line, size_t count) {
721	const char* firstSpace = strchr(line, ' ');
722	size_t cmdLength;
723	if (firstSpace) {
724		cmdLength = firstSpace - line;
725	} else {
726		cmdLength = count;
727	}
728
729	const char* args = 0;
730	if (firstSpace) {
731		args = firstSpace + 1;
732	}
733	int result = _tryCommands(debugger, _debuggerCommands, line, cmdLength, args, count - cmdLength - 1);
734	if (result < 0 && debugger->system) {
735		result = _tryCommands(debugger, debugger->system->commands, line, cmdLength, args, count - cmdLength - 1);
736		if (result < 0) {
737			result = _tryCommands(debugger, debugger->system->platformCommands, line, cmdLength, args, count - cmdLength - 1);
738		}
739	}
740	if (result < 0) {
741		debugger->backend->printf(debugger->backend, "Command not found\n");
742	}
743	return false;
744}
745
746static void _commandLine(struct mDebugger* debugger) {
747	struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
748	const char* line;
749		size_t len;
750	_printStatus(cliDebugger, 0);
751	while (debugger->state == DEBUGGER_PAUSED) {
752		line = cliDebugger->backend->readline(cliDebugger->backend, &len);
753		if (!line || len == 0) {
754			debugger->state = DEBUGGER_SHUTDOWN;
755			return;
756		}
757		if (line[0] == '\n') {
758			line = cliDebugger->backend->historyLast(cliDebugger->backend, &len);
759			if (line && len) {
760				_parse(cliDebugger, line, len);
761			}
762		} else {
763			_parse(cliDebugger, line, len);
764			cliDebugger->backend->historyAppend(cliDebugger->backend, line);
765		}
766	}
767}
768
769static void _reportEntry(struct mDebugger* debugger, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) {
770	struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
771	switch (reason) {
772	case DEBUGGER_ENTER_MANUAL:
773	case DEBUGGER_ENTER_ATTACHED:
774		break;
775	case DEBUGGER_ENTER_BREAKPOINT:
776		if (info) {
777			cliDebugger->backend->printf(cliDebugger->backend, "Hit breakpoint at 0x%08X\n", info->address);
778		} else {
779			cliDebugger->backend->printf(cliDebugger->backend, "Hit breakpoint\n");
780		}
781		break;
782	case DEBUGGER_ENTER_WATCHPOINT:
783		if (info) {
784			if (info->accessType & WATCHPOINT_WRITE) {
785				cliDebugger->backend->printf(cliDebugger->backend, "Hit watchpoint at 0x%08X: (new value = 0x%08x, old value = 0x%08X)\n", info->address, info->newValue, info->oldValue);
786			} else {
787				cliDebugger->backend->printf(cliDebugger->backend, "Hit watchpoint at 0x%08X: (value = 0x%08x)\n", info->address, info->oldValue);
788			}
789		} else {
790			cliDebugger->backend->printf(cliDebugger->backend, "Hit watchpoint\n");
791		}
792		break;
793	case DEBUGGER_ENTER_ILLEGAL_OP:
794		if (info) {
795			cliDebugger->backend->printf(cliDebugger->backend, "Hit illegal opcode at 0x%08X: 0x%08X\n", info->address, info->opcode);
796		} else {
797			cliDebugger->backend->printf(cliDebugger->backend, "Hit illegal opcode\n");
798		}
799		break;
800	}
801}
802
803static void _cliDebuggerInit(struct mDebugger* debugger) {
804	struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
805	cliDebugger->backend->init(cliDebugger->backend);
806}
807
808static void _cliDebuggerDeinit(struct mDebugger* debugger) {
809	struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
810	if (cliDebugger->system) {
811		if (cliDebugger->system->deinit) {
812			cliDebugger->system->deinit(cliDebugger->system);
813		}
814		free(cliDebugger->system);
815		cliDebugger->system = NULL;
816	}
817	if (cliDebugger->backend && cliDebugger->backend->deinit) {
818		cliDebugger->backend->deinit(cliDebugger->backend);
819		cliDebugger->backend = NULL;
820	}
821}
822
823static void _cliDebuggerCustom(struct mDebugger* debugger) {
824	struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
825	bool retain = false;
826	if (cliDebugger->system) {
827		retain = cliDebugger->system->custom(cliDebugger->system);
828	}
829	if (!retain && debugger->state == DEBUGGER_CUSTOM) {
830		debugger->state = DEBUGGER_RUNNING;
831	}
832}
833
834void CLIDebuggerCreate(struct CLIDebugger* debugger) {
835	debugger->d.init = _cliDebuggerInit;
836	debugger->d.deinit = _cliDebuggerDeinit;
837	debugger->d.custom = _cliDebuggerCustom;
838	debugger->d.paused = _commandLine;
839	debugger->d.entered = _reportEntry;
840	debugger->d.type = DEBUGGER_CLI;
841
842	debugger->system = NULL;
843	debugger->backend = NULL;
844}
845
846void CLIDebuggerAttachSystem(struct CLIDebugger* debugger, struct CLIDebuggerSystem* system) {
847	if (debugger->system) {
848		if (debugger->system->deinit) {
849			debugger->system->deinit(debugger->system);
850		}
851		free(debugger->system);
852	}
853
854	debugger->system = system;
855	system->p = debugger;
856}
857
858void CLIDebuggerAttachBackend(struct CLIDebugger* debugger, struct CLIDebuggerBackend* backend) {
859	if (debugger->backend == backend) {
860		return;
861	}
862	if (debugger->backend && debugger->backend->deinit) {
863		debugger->backend->deinit(debugger->backend);
864	}
865
866	debugger->backend = backend;
867	backend->p = debugger;
868}
869
870bool CLIDebuggerTabComplete(struct CLIDebugger* debugger, const char* token, bool initial, size_t tokenLen) {
871	size_t cmd = 0;
872	size_t len;
873	const char* name = 0;
874	for (len = 1; len <= tokenLen; ++len) {
875		for (; (name = _debuggerCommands[cmd].name); ++cmd) {
876			int cmp = strncasecmp(name, token, len);
877			if (cmp > 0) {
878				return false;
879			}
880			if (cmp == 0) {
881				break;
882			}
883		}
884	}
885	if (!name) {
886		return false;
887	}
888	if (_debuggerCommands[cmd + 1].name && strlen(_debuggerCommands[cmd + 1].name) >= len && name[len - 1] == _debuggerCommands[cmd + 1].name[len - 1]) {
889		--len;
890		const char* next = 0;
891		int i;
892		for (i = cmd + 1; _debuggerCommands[i].name; ++i) {
893			if (strncasecmp(name, _debuggerCommands[i].name, len)) {
894				break;
895			}
896			next = _debuggerCommands[i].name;
897		}
898		if (!next) {
899			return false;
900		}
901
902		for (; name[len]; ++len) {
903			if (name[len] != next[len]) {
904				break;
905			}
906			char out[2] = { name[len], '\0' };
907			debugger->backend->lineAppend(debugger->backend, out);
908		}
909		return true;
910	}
911	name += len - 1;
912	debugger->backend->lineAppend(debugger->backend, name);
913	debugger->backend->lineAppend(debugger->backend, " ");
914	return true;
915}