all repos — mgba @ a214481b760b8aaf444bdb2bd18ba445ce1885ae

mGBA Game Boy Advance Emulator

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

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