all repos — mgba @ 9a87840e7e9b6bceefdb5c3dd3c43d2cbff06ca6

mGBA Game Boy Advance Emulator

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

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