all repos — mgba @ a09d8649ee508e87e3d9629f2116b5c6528416ba

mGBA Game Boy Advance Emulator

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

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