all repos — mgba @ 474f1c6e9c8b5008d40a0faf1508e685b72cede3

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