all repos — mgba @ 4b4be372c0ff200e2da2e2855bc942a08e544618

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