all repos — mgba @ c0eb7c81f70d94b21b931b6a1ed44e7b9c9f5dd3

mGBA Game Boy Advance Emulator

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

  1#include "cli-debugger.h"
  2
  3#include <signal.h>
  4
  5#ifdef USE_PTHREADS
  6#include <pthread.h>
  7#endif
  8
  9struct DebugVector {
 10	struct DebugVector* next;
 11	enum DVType {
 12		ERROR_TYPE,
 13		INT_TYPE,
 14		CHAR_TYPE
 15	} type;
 16	union {
 17		int32_t intValue;
 18		const char* charValue;
 19	};
 20};
 21
 22static const char* ERROR_MISSING_ARGS = "Arguments missing";
 23
 24static struct CLIDebugger* _activeDebugger;
 25
 26typedef void (DebuggerCommand)(struct CLIDebugger*, struct DebugVector*);
 27
 28static void _breakInto(struct CLIDebugger*, struct DebugVector*);
 29static void _continue(struct CLIDebugger*, struct DebugVector*);
 30static void _next(struct CLIDebugger*, struct DebugVector*);
 31static void _print(struct CLIDebugger*, struct DebugVector*);
 32static void _printHex(struct CLIDebugger*, struct DebugVector*);
 33static void _printStatus(struct CLIDebugger*, struct DebugVector*);
 34static void _quit(struct CLIDebugger*, struct DebugVector*);
 35static void _readByte(struct CLIDebugger*, struct DebugVector*);
 36static void _readHalfword(struct CLIDebugger*, struct DebugVector*);
 37static void _readWord(struct CLIDebugger*, struct DebugVector*);
 38static void _setBreakpoint(struct CLIDebugger*, struct DebugVector*);
 39static void _clearBreakpoint(struct CLIDebugger*, struct DebugVector*);
 40static void _setWatchpoint(struct CLIDebugger*, struct DebugVector*);
 41
 42static void _breakIntoDefault(int signal);
 43
 44static struct {
 45	const char* name;
 46	DebuggerCommand* command;
 47} _debuggerCommands[] = {
 48	{ "b", _setBreakpoint },
 49	{ "break", _setBreakpoint },
 50	{ "c", _continue },
 51	{ "continue", _continue },
 52	{ "d", _clearBreakpoint },
 53	{ "delete", _clearBreakpoint },
 54	{ "i", _printStatus },
 55	{ "info", _printStatus },
 56	{ "n", _next },
 57	{ "next", _next },
 58	{ "p", _print },
 59	{ "p/x", _printHex },
 60	{ "print", _print },
 61	{ "print/x", _printHex },
 62	{ "q", _quit },
 63	{ "quit", _quit },
 64	{ "rb", _readByte },
 65	{ "rh", _readHalfword },
 66	{ "rw", _readWord },
 67	{ "status", _printStatus },
 68	{ "w", _setWatchpoint },
 69	{ "watch", _setWatchpoint },
 70	{ "x", _breakInto },
 71	{ 0, 0 }
 72};
 73
 74static inline void _printPSR(union PSR psr) {
 75	printf("%08X [%c%c%c%c%c%c%c]\n", psr.packed,
 76		psr.n ? 'N' : '-',
 77		psr.z ? 'Z' : '-',
 78		psr.c ? 'C' : '-',
 79		psr.v ? 'V' : '-',
 80		psr.i ? 'I' : '-',
 81		psr.f ? 'F' : '-',
 82		psr.t ? 'T' : '-');
 83}
 84
 85static void _handleDeath(int sig) {
 86	(void)(sig);
 87	printf("No debugger attached!\n");
 88}
 89
 90static void _breakInto(struct CLIDebugger* debugger, struct DebugVector* dv) {
 91	(void)(debugger);
 92	(void)(dv);
 93	struct sigaction sa, osa;
 94	sa.sa_handler = _handleDeath;
 95	sigemptyset(&sa.sa_mask);
 96	sigaddset(&sa.sa_mask, SIGTRAP);
 97	sa.sa_flags = SA_RESTART;
 98	sigaction(SIGTRAP, &sa, &osa);
 99#ifdef USE_PTHREADS
100	pthread_kill(pthread_self(), SIGTRAP);
101#else
102	kill(getpid(), SIGTRAP);
103#endif
104	sigaction(SIGTRAP, &osa, 0);
105}
106
107static void _continue(struct CLIDebugger* debugger, struct DebugVector* dv) {
108	(void)(dv);
109	debugger->d.state = DEBUGGER_RUNNING;
110}
111
112static void _next(struct CLIDebugger* debugger, struct DebugVector* dv) {
113	(void)(dv);
114	ARMRun(debugger->d.cpu);
115	_printStatus(debugger, 0);
116}
117
118static void _print(struct CLIDebugger* debugger, struct DebugVector* dv) {
119	(void)(debugger);
120	for ( ; dv; dv = dv->next) {
121		printf(" %u", dv->intValue);
122	}
123	printf("\n");
124}
125
126static void _printHex(struct CLIDebugger* debugger, struct DebugVector* dv) {
127	(void)(debugger);
128	for ( ; dv; dv = dv->next) {
129		printf(" 0x%08X", dv->intValue);
130	}
131	printf("\n");
132}
133
134static inline void _printLine(struct CLIDebugger* debugger, uint32_t address, enum ExecutionMode mode) {
135	// TODO: write a disassembler
136	if (mode == MODE_ARM) {
137		uint32_t instruction = debugger->d.cpu->memory.load32(debugger->d.cpu, address, 0);
138		printf("%08X\n", instruction);
139	} else {
140		uint16_t instruction = debugger->d.cpu->memory.loadU16(debugger->d.cpu, address, 0);
141		printf("%04X\n", instruction);
142	}
143}
144
145static void _printStatus(struct CLIDebugger* debugger, struct DebugVector* dv) {
146	(void)(dv);
147	int r;
148	for (r = 0; r < 4; ++r) {
149		printf("%08X %08X %08X %08X\n",
150			debugger->d.cpu->gprs[r << 2],
151			debugger->d.cpu->gprs[(r << 2) + 1],
152			debugger->d.cpu->gprs[(r << 2) + 2],
153			debugger->d.cpu->gprs[(r << 2) + 3]);
154	}
155	_printPSR(debugger->d.cpu->cpsr);
156	int instructionLength;
157	enum ExecutionMode mode = debugger->d.cpu->cpsr.t;
158	if (mode == MODE_ARM) {
159		instructionLength = WORD_SIZE_ARM;
160	} else {
161		instructionLength = WORD_SIZE_THUMB;
162	}
163	_printLine(debugger, debugger->d.cpu->gprs[ARM_PC] - instructionLength, mode);
164}
165
166static void _quit(struct CLIDebugger* debugger, struct DebugVector* dv) {
167	(void)(dv);
168	debugger->d.state = DEBUGGER_SHUTDOWN;
169}
170
171static void _readByte(struct CLIDebugger* debugger, struct DebugVector* dv) {
172	if (!dv || dv->type != INT_TYPE) {
173		printf("%s\n", ERROR_MISSING_ARGS);
174		return;
175	}
176	uint32_t address = dv->intValue;
177	uint8_t value = debugger->d.cpu->memory.loadU8(debugger->d.cpu, address, 0);
178	printf(" 0x%02X\n", value);
179}
180
181static void _readHalfword(struct CLIDebugger* debugger, struct DebugVector* dv) {
182	if (!dv || dv->type != INT_TYPE) {
183		printf("%s\n", ERROR_MISSING_ARGS);
184		return;
185	}
186	uint32_t address = dv->intValue;
187	uint16_t value = debugger->d.cpu->memory.loadU16(debugger->d.cpu, address, 0);
188	printf(" 0x%04X\n", value);
189}
190
191static void _readWord(struct CLIDebugger* debugger, struct DebugVector* dv) {
192	if (!dv || dv->type != INT_TYPE) {
193		printf("%s\n", ERROR_MISSING_ARGS);
194		return;
195	}
196	uint32_t address = dv->intValue;
197	uint32_t value = debugger->d.cpu->memory.load32(debugger->d.cpu, address, 0);
198	printf(" 0x%08X\n", value);
199}
200
201static void _setBreakpoint(struct CLIDebugger* debugger, struct DebugVector* dv) {
202	if (!dv || dv->type != INT_TYPE) {
203		printf("%s\n", ERROR_MISSING_ARGS);
204		return;
205	}
206	uint32_t address = dv->intValue;
207	ARMDebuggerSetBreakpoint(&debugger->d, address);
208}
209
210static void _clearBreakpoint(struct CLIDebugger* debugger, struct DebugVector* dv) {
211	if (!dv || dv->type != INT_TYPE) {
212		printf("%s\n", ERROR_MISSING_ARGS);
213		return;
214	}
215	uint32_t address = dv->intValue;
216	ARMDebuggerClearBreakpoint(&debugger->d, address);
217}
218
219static void _setWatchpoint(struct CLIDebugger* debugger, struct DebugVector* dv) {
220	if (!dv || dv->type != INT_TYPE) {
221		printf("%s\n", ERROR_MISSING_ARGS);
222		return;
223	}
224	// TODO: Redo watchpoints
225	(void) (debugger);
226}
227
228static void _breakIntoDefault(int signal) {
229	(void)(signal);
230	ARMDebuggerEnter(&_activeDebugger->d, DEBUGGER_ENTER_MANUAL);
231}
232
233enum _DVParseState {
234	PARSE_ERROR = -1,
235	PARSE_ROOT = 0,
236	PARSE_EXPECT_REGISTER,
237	PARSE_EXPECT_REGISTER_2,
238	PARSE_EXPECT_LR,
239	PARSE_EXPECT_PC,
240	PARSE_EXPECT_SP,
241	PARSE_EXPECT_DECIMAL,
242	PARSE_EXPECT_HEX,
243	PARSE_EXPECT_PREFIX,
244	PARSE_EXPECT_SUFFIX,
245};
246
247static struct DebugVector* _DVParse(struct CLIDebugger* debugger, const char* string, size_t length) {
248	if (!string || length < 1) {
249		return 0;
250	}
251
252	enum _DVParseState state = PARSE_ROOT;
253	struct DebugVector dvTemp = { .type = INT_TYPE };
254	uint32_t current = 0;
255
256	while (length > 0 && string[0] && string[0] != ' ' && state != PARSE_ERROR) {
257		char token = string[0];
258		++string;
259		--length;
260		switch (state) {
261		case PARSE_ROOT:
262			switch (token) {
263			case 'r':
264				state = PARSE_EXPECT_REGISTER;
265				break;
266			case 'p':
267				state = PARSE_EXPECT_PC;
268				break;
269			case 's':
270				state = PARSE_EXPECT_SP;
271				break;
272			case 'l':
273				state = PARSE_EXPECT_LR;
274				break;
275			case '1':
276			case '2':
277			case '3':
278			case '4':
279			case '5':
280			case '6':
281			case '7':
282			case '8':
283			case '9':
284				state = PARSE_EXPECT_DECIMAL;
285				current = token - '0';
286				break;
287			case '0':
288				state = PARSE_EXPECT_PREFIX;
289				break;
290			case '$':
291				state = PARSE_EXPECT_HEX;
292				current = 0;
293				break;
294			default:
295				state = PARSE_ERROR;
296				break;
297			};
298			break;
299		case PARSE_EXPECT_LR:
300			switch (token) {
301			case 'r':
302				current = debugger->d.cpu->gprs[ARM_LR];
303				state = PARSE_EXPECT_SUFFIX;
304				break;
305			default:
306				state = PARSE_ERROR;
307				break;
308			}
309			break;
310		case PARSE_EXPECT_PC:
311			switch (token) {
312			case 'c':
313				current = debugger->d.cpu->gprs[ARM_PC];
314				state = PARSE_EXPECT_SUFFIX;
315				break;
316			default:
317				state = PARSE_ERROR;
318				break;
319			}
320			break;
321		case PARSE_EXPECT_SP:
322			switch (token) {
323			case 'p':
324				current = debugger->d.cpu->gprs[ARM_SP];
325				state = PARSE_EXPECT_SUFFIX;
326				break;
327			default:
328				state = PARSE_ERROR;
329				break;
330			}
331			break;
332		case PARSE_EXPECT_REGISTER:
333			switch (token) {
334			case '0':
335			case '2':
336			case '3':
337			case '4':
338			case '5':
339			case '6':
340			case '7':
341			case '8':
342			case '9':
343				current = debugger->d.cpu->gprs[token - '0'];
344				state = PARSE_EXPECT_SUFFIX;
345				break;
346			case '1':
347				state = PARSE_EXPECT_REGISTER_2;
348				break;
349			default:
350				state = PARSE_ERROR;
351				break;
352			}
353			break;
354		case PARSE_EXPECT_REGISTER_2:
355			switch (token) {
356			case '0':
357			case '1':
358			case '2':
359			case '3':
360			case '4':
361			case '5':
362				current = debugger->d.cpu->gprs[token - '0' + 10];
363				state = PARSE_EXPECT_SUFFIX;
364				break;
365			default:
366				state = PARSE_ERROR;
367				break;
368			}
369			break;
370		case PARSE_EXPECT_DECIMAL:
371			switch (token) {
372			case '0':
373			case '1':
374			case '2':
375			case '3':
376			case '4':
377			case '5':
378			case '6':
379			case '7':
380			case '8':
381			case '9':
382				// TODO: handle overflow
383				current *= 10;
384				current += token - '0';
385				break;
386			default:
387				state = PARSE_ERROR;
388			}
389			break;
390		case PARSE_EXPECT_HEX:
391			switch (token) {
392			case '0':
393			case '1':
394			case '2':
395			case '3':
396			case '4':
397			case '5':
398			case '6':
399			case '7':
400			case '8':
401			case '9':
402				// TODO: handle overflow
403				current *= 16;
404				current += token - '0';
405				break;
406			case 'A':
407			case 'B':
408			case 'C':
409			case 'D':
410			case 'E':
411			case 'F':
412				// TODO: handle overflow
413				current *= 16;
414				current += token - 'A' + 10;
415				break;
416			case 'a':
417			case 'b':
418			case 'c':
419			case 'd':
420			case 'e':
421			case 'f':
422				// TODO: handle overflow
423				current *= 16;
424				current += token - 'a' + 10;
425				break;
426			default:
427				state = PARSE_ERROR;
428				break;
429			}
430			break;
431		case PARSE_EXPECT_PREFIX:
432			switch (token) {
433			case 'X':
434			case 'x':
435				current = 0;
436				state = PARSE_EXPECT_HEX;
437				break;
438			default:
439				state = PARSE_ERROR;
440				break;
441			}
442			break;
443		case PARSE_EXPECT_SUFFIX:
444			// TODO
445			state = PARSE_ERROR;
446			break;
447		case PARSE_ERROR:
448			// This shouldn't be reached
449			break;
450		}
451	}
452
453	struct DebugVector* dv = malloc(sizeof(struct DebugVector));
454	if (state == PARSE_ERROR) {
455		dv->type = ERROR_TYPE;
456		dv->next = 0;
457	} else {
458		dvTemp.intValue = current;
459		*dv = dvTemp;
460		if (string[0] == ' ') {
461			dv->next = _DVParse(debugger, string + 1, length - 1);
462		}
463	}
464	return dv;
465}
466
467static void _DVFree(struct DebugVector* dv) {
468	struct DebugVector* next;
469	while (dv) {
470		next = dv->next;
471		free(dv);
472		dv = next;
473	}
474}
475
476static int _parse(struct CLIDebugger* debugger, const char* line, size_t count) {
477	const char* firstSpace = strchr(line, ' ');
478	size_t cmdLength;
479	struct DebugVector* dv = 0;
480	if (firstSpace) {
481		cmdLength = firstSpace - line;
482		dv = _DVParse(debugger, firstSpace + 1, count - cmdLength - 1);
483		if (dv && dv->type == ERROR_TYPE) {
484			printf("Parse error\n");
485			_DVFree(dv);
486			return 0;
487		}
488	} else {
489		cmdLength = count;
490	}
491
492	int i;
493	const char* name;
494	for (i = 0; (name = _debuggerCommands[i].name); ++i) {
495		if (strlen(name) != cmdLength) {
496			continue;
497		}
498		if (strncasecmp(name, line, cmdLength) == 0) {
499			_debuggerCommands[i].command(debugger, dv);
500			_DVFree(dv);
501			return 1;
502		}
503	}
504	_DVFree(dv);
505	printf("Command not found\n");
506	return 0;
507}
508
509static char* _prompt(EditLine* el) {
510	(void)(el);
511	return "> ";
512}
513
514static void _commandLine(struct ARMDebugger* debugger) {
515	struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
516	const char* line;
517	_printStatus(cliDebugger, 0);
518	int count = 0;
519	HistEvent ev;
520	while (debugger->state == DEBUGGER_PAUSED) {
521		line = el_gets(cliDebugger->elstate, &count);
522		if (!line) {
523			debugger->state = DEBUGGER_EXITING;
524			return;
525		}
526		if (line[0] == '\n') {
527			if (history(cliDebugger->histate, &ev, H_FIRST) >= 0) {
528				_parse(cliDebugger, ev.str, strlen(ev.str) - 1);
529			}
530		} else {
531			if (_parse(cliDebugger, line, count - 1)) {
532				history(cliDebugger->histate, &ev, H_ENTER, line);
533			}
534		}
535	}
536}
537
538static void _reportEntry(struct ARMDebugger* debugger, enum DebuggerEntryReason reason) {
539	(void) (debugger);
540	switch (reason) {
541	case DEBUGGER_ENTER_MANUAL:
542	case DEBUGGER_ENTER_ATTACHED:
543		break;
544	case DEBUGGER_ENTER_BREAKPOINT:
545		printf("Hit breakpoint\n");
546		break;
547	case DEBUGGER_ENTER_WATCHPOINT:
548		printf("Hit watchpoint\n");
549		break;
550	case DEBUGGER_ENTER_ILLEGAL_OP:
551		printf("Hit illegal opcode\n");
552		break;
553	}
554}
555
556static unsigned char _tabComplete(EditLine* elstate, int ch) {
557	(void)(ch);
558	const LineInfo* li = el_line(elstate);
559	const char* commandPtr;
560	int cmd = 0, len = 0;
561	const char* name = 0;
562	for (commandPtr = li->buffer; commandPtr <= li->cursor; ++commandPtr, ++len) {
563		for (; (name = _debuggerCommands[cmd].name); ++cmd) {
564			int cmp = strncasecmp(name, li->buffer, len);
565			if (cmp > 0) {
566				return CC_ERROR;
567			}
568			if (cmp == 0) {
569				break;
570			}
571		}
572	}
573	if (_debuggerCommands[cmd + 1].name && strncasecmp(_debuggerCommands[cmd + 1].name, li->buffer, len - 1) == 0) {
574		return CC_ERROR;
575	}
576	name += len - 1;
577	el_insertstr(elstate, name);
578	el_insertstr(elstate, " ");
579	return CC_REDISPLAY;
580}
581
582static void _cliDebuggerInit(struct ARMDebugger* debugger) {
583	struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
584	// TODO: get argv[0]
585	cliDebugger->elstate = el_init("gbac", stdin, stdout, stderr);
586	el_set(cliDebugger->elstate, EL_PROMPT, _prompt);
587	el_set(cliDebugger->elstate, EL_EDITOR, "emacs");
588
589	el_set(cliDebugger->elstate, EL_CLIENTDATA, cliDebugger);
590	el_set(cliDebugger->elstate, EL_ADDFN, "tab-complete", "Tab completion", _tabComplete);
591	el_set(cliDebugger->elstate, EL_BIND, "\t", "tab-complete", 0);
592	cliDebugger->histate = history_init();
593	HistEvent ev;
594	history(cliDebugger->histate, &ev, H_SETSIZE, 200);
595	el_set(cliDebugger->elstate, EL_HIST, history, cliDebugger->histate);
596	_activeDebugger = cliDebugger;
597	signal(SIGINT, _breakIntoDefault);
598}
599
600static void _cliDebuggerDeinit(struct ARMDebugger* debugger) {
601	struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
602	history_end(cliDebugger->histate);
603	el_end(cliDebugger->elstate);
604}
605
606void CLIDebuggerCreate(struct CLIDebugger* debugger) {
607	debugger->d.init = _cliDebuggerInit;
608	debugger->d.deinit = _cliDebuggerDeinit;
609	debugger->d.paused = _commandLine;
610	debugger->d.entered = _reportEntry;
611}