all repos — mgba @ bc4924cef5ad81407c7e99aa34a5e7a6246d565d

mGBA Game Boy Advance Emulator

src/debugger.c (view raw)

  1#include "debugger.h"
  2
  3#include "arm.h"
  4
  5#include <signal.h>
  6#include <stdio.h>
  7#include <stdarg.h>
  8#include <stdlib.h>
  9#include <strings.h>
 10#include <unistd.h>
 11#include "linenoise.h"
 12
 13struct DebugVector {
 14	struct DebugVector* next;
 15	enum DVType {
 16		ERROR_TYPE,
 17		INT_TYPE,
 18		CHAR_TYPE
 19	} type;
 20	union {
 21		int32_t intValue;
 22		const char* charValue;
 23	};
 24};
 25
 26static const char* ERROR_MISSING_ARGS = "Arguments missing";
 27
 28typedef void (DebuggerComamnd)(struct ARMDebugger*, struct DebugVector*);
 29
 30static void _breakInto(struct ARMDebugger*, struct DebugVector*);
 31static void _print(struct ARMDebugger*, struct DebugVector*);
 32static void _printHex(struct ARMDebugger*, struct DebugVector*);
 33static void _printStatus(struct ARMDebugger*, struct DebugVector*);
 34static void _readByte(struct ARMDebugger*, struct DebugVector*);
 35static void _readHalfword(struct ARMDebugger*, struct DebugVector*);
 36static void _readWord(struct ARMDebugger*, struct DebugVector*);
 37static void _quit(struct ARMDebugger*, struct DebugVector*);
 38
 39struct {
 40	const char* name;
 41	DebuggerComamnd* command;
 42} debuggerCommands[] = {
 43	{ "i", _printStatus },
 44	{ "info", _printStatus },
 45	{ "p", _print },
 46	{ "print", _print },
 47	{ "p/x", _printHex },
 48	{ "print/x", _printHex },
 49	{ "q", _quit },
 50	{ "quit", _quit },
 51	{ "rb", _readByte },
 52	{ "rh", _readHalfword },
 53	{ "rw", _readWord },
 54	{ "status", _printStatus },
 55	{ "x", _breakInto },
 56	{ 0, 0 }
 57};
 58
 59static inline void _printPSR(union PSR psr) {
 60	printf("%08X [%c%c%c%c%c%c%c]\n", psr.packed,
 61		psr.n ? 'N' : '-',
 62		psr.z ? 'Z' : '-',
 63		psr.c ? 'C' : '-',
 64		psr.v ? 'V' : '-',
 65		psr.i ? 'I' : '-',
 66		psr.f ? 'F' : '-',
 67		psr.t ? 'T' : '-');
 68}
 69
 70static void _handleDeath(int sig) {
 71	(void)(sig);
 72	printf("No debugger attached!\n");
 73}
 74
 75static void _breakInto(struct ARMDebugger* debugger, struct DebugVector* dv) {
 76	(void)(debugger);
 77	(void)(dv);
 78	sig_t oldSignal = signal(SIGTRAP, _handleDeath);
 79	kill(getpid(), SIGTRAP);
 80	signal(SIGTRAP, oldSignal);
 81}
 82
 83static void _print(struct ARMDebugger* debugger, struct DebugVector* dv) {
 84	(void)(debugger);
 85	for ( ; dv; dv = dv->next) {
 86		printf(" %u", dv->intValue);
 87	}
 88	printf("\n");
 89}
 90
 91static void _printHex(struct ARMDebugger* debugger, struct DebugVector* dv) {
 92	(void)(debugger);
 93	for ( ; dv; dv = dv->next) {
 94		printf(" 0x%08X", dv->intValue);
 95	}
 96	printf("\n");
 97}
 98
 99static inline void _printLine(struct ARMDebugger* debugger, uint32_t address, enum ExecutionMode mode) {
100	// TODO: write a disassembler
101	if (mode == MODE_ARM) {
102		uint32_t instruction = debugger->cpu->memory->load32(debugger->cpu->memory, address);
103		printf("%08X\n", instruction);
104	} else {
105		uint16_t instruction = debugger->cpu->memory->loadU16(debugger->cpu->memory, address);
106		printf("%04X\n", instruction);
107	}
108}
109
110static void _printStatus(struct ARMDebugger* debugger, struct DebugVector* dv) {
111	(void)(dv);
112	int r;
113	for (r = 0; r < 4; ++r) {
114		printf("%08X %08X %08X %08X\n",
115			debugger->cpu->gprs[r << 2],
116			debugger->cpu->gprs[(r << 2) + 1],
117			debugger->cpu->gprs[(r << 2) + 2],
118			debugger->cpu->gprs[(r << 2) + 3]);
119	}
120	_printPSR(debugger->cpu->cpsr);
121	int instructionLength;
122	enum ExecutionMode mode = debugger->cpu->cpsr.t;
123	if (mode == MODE_ARM) {
124		instructionLength = WORD_SIZE_ARM;
125	} else {
126		instructionLength = WORD_SIZE_THUMB;
127	}
128	_printLine(debugger, debugger->cpu->gprs[ARM_PC] - instructionLength, mode);
129}
130
131static void _quit(struct ARMDebugger* debugger, struct DebugVector* dv) {
132	(void)(dv);
133	debugger->state = DEBUGGER_EXITING;
134}
135
136static void _readByte(struct ARMDebugger* debugger, struct DebugVector* dv) {
137	if (!dv || dv->type != INT_TYPE) {
138		printf("%s\n", ERROR_MISSING_ARGS);
139		return;
140	}
141	uint32_t address = dv->intValue;
142	uint8_t value = debugger->cpu->memory->loadU8(debugger->cpu->memory, address);
143	printf(" 0x%02X\n", value);
144}
145
146static void _readHalfword(struct ARMDebugger* debugger, struct DebugVector* dv) {
147	if (!dv || dv->type != INT_TYPE) {
148		printf("%s\n", ERROR_MISSING_ARGS);
149		return;
150	}
151	uint32_t address = dv->intValue;
152	uint16_t value = debugger->cpu->memory->loadU16(debugger->cpu->memory, address);
153	printf(" 0x%04X\n", value);
154}
155
156static void _readWord(struct ARMDebugger* debugger, struct DebugVector* dv) {
157	if (!dv || dv->type != INT_TYPE) {
158		printf("%s\n", ERROR_MISSING_ARGS);
159		return;
160	}
161	uint32_t address = dv->intValue;
162	uint32_t value = debugger->cpu->memory->load32(debugger->cpu->memory, address);
163	printf(" 0x%08X\n", value);
164}
165
166enum _DVParseState {
167	PARSE_ERROR = -1,
168	PARSE_ROOT = 0,
169	PARSE_EXPECT_REGISTER,
170	PARSE_EXPECT_REGISTER_2,
171	PARSE_EXPECT_LR,
172	PARSE_EXPECT_PC,
173	PARSE_EXPECT_SP,
174	PARSE_EXPECT_DECIMAL,
175	PARSE_EXPECT_HEX,
176	PARSE_EXPECT_PREFIX,
177	PARSE_EXPECT_SUFFIX,
178};
179
180static struct DebugVector* _DVParse(struct ARMDebugger* debugger, const char* string) {
181	if (!string || !string[0]) {
182		return 0;
183	}
184
185	enum _DVParseState state = PARSE_ROOT;
186	struct DebugVector dvTemp = { .type = INT_TYPE };
187	uint32_t current = 0;
188
189	while (string[0] && string[0] != ' ' && state != PARSE_ERROR) {
190		char token = string[0];
191		++string;
192		switch (state) {
193		case PARSE_ROOT:
194			switch (token) {
195			case 'r':
196				state = PARSE_EXPECT_REGISTER;
197				break;
198			case 'p':
199				state = PARSE_EXPECT_PC;
200				break;
201			case 's':
202				state = PARSE_EXPECT_SP;
203				break;
204			case 'l':
205				state = PARSE_EXPECT_LR;
206				break;
207			case '1':
208			case '2':
209			case '3':
210			case '4':
211			case '5':
212			case '6':
213			case '7':
214			case '8':
215			case '9':
216				state = PARSE_EXPECT_DECIMAL;
217				current = token - '0';
218				break;
219			case '0':
220				state = PARSE_EXPECT_PREFIX;
221				break;
222			case '$':
223				state = PARSE_EXPECT_HEX;
224				current = 0;
225				break;
226			default:
227				state = PARSE_ERROR;
228				break;
229			};
230			break;
231		case PARSE_EXPECT_LR:
232			switch (token) {
233			case 'r':
234				current = debugger->cpu->gprs[ARM_LR];
235				state = PARSE_EXPECT_SUFFIX;
236				break;
237			default:
238				state = PARSE_ERROR;
239				break;
240			}
241			break;
242		case PARSE_EXPECT_PC:
243			switch (token) {
244			case 'c':
245				current = debugger->cpu->gprs[ARM_PC];
246				state = PARSE_EXPECT_SUFFIX;
247				break;
248			default:
249				state = PARSE_ERROR;
250				break;
251			}
252			break;
253		case PARSE_EXPECT_SP:
254			switch (token) {
255			case 'p':
256				current = debugger->cpu->gprs[ARM_SP];
257				state = PARSE_EXPECT_SUFFIX;
258				break;
259			default:
260				state = PARSE_ERROR;
261				break;
262			}
263			break;
264		case PARSE_EXPECT_REGISTER:
265			switch (token) {
266			case '0':
267			case '2':
268			case '3':
269			case '4':
270			case '5':
271			case '6':
272			case '7':
273			case '8':
274			case '9':
275				current = debugger->cpu->gprs[token - '0'];
276				state = PARSE_EXPECT_SUFFIX;
277				break;
278			case '1':
279				state = PARSE_EXPECT_REGISTER_2;
280				break;
281			default:
282				state = PARSE_ERROR;
283				break;
284			}
285			break;
286		case PARSE_EXPECT_REGISTER_2:
287			switch (token) {
288			case '0':
289			case '1':
290			case '2':
291			case '3':
292			case '4':
293			case '5':
294				current = debugger->cpu->gprs[token - '0' + 10];
295				state = PARSE_EXPECT_SUFFIX;
296				break;
297			default:
298				state = PARSE_ERROR;
299				break;
300			}
301			break;
302		case PARSE_EXPECT_DECIMAL:
303			switch (token) {
304			case '0':
305			case '1':
306			case '2':
307			case '3':
308			case '4':
309			case '5':
310			case '6':
311			case '7':
312			case '8':
313			case '9':
314				// TODO: handle overflow
315				current *= 10;
316				current += token - '0';
317				break;
318			default:
319				state = PARSE_ERROR;
320			}
321			break;
322		case PARSE_EXPECT_HEX:
323			switch (token) {
324			case '0':
325			case '1':
326			case '2':
327			case '3':
328			case '4':
329			case '5':
330			case '6':
331			case '7':
332			case '8':
333			case '9':
334				// TODO: handle overflow
335				current *= 16;
336				current += token - '0';
337				break;
338			case 'A':
339			case 'B':
340			case 'C':
341			case 'D':
342			case 'E':
343			case 'F':
344				// TODO: handle overflow
345				current *= 16;
346				current += token - 'A' + 10;
347				break;
348			case 'a':
349			case 'b':
350			case 'c':
351			case 'd':
352			case 'e':
353			case 'f':
354				// TODO: handle overflow
355				current *= 16;
356				current += token - 'a' + 10;
357				break;
358			default:
359				state = PARSE_ERROR;
360				break;
361			}
362			break;
363		case PARSE_EXPECT_PREFIX:
364			switch (token) {
365			case 'X':
366			case 'x':
367				current = 0;
368				state = PARSE_EXPECT_HEX;
369				break;
370			default:
371				state = PARSE_ERROR;
372				break;
373			}
374			break;
375		case PARSE_EXPECT_SUFFIX:
376			// TODO
377			state = PARSE_ERROR;
378			break;
379		case PARSE_ERROR:
380			// This shouldn't be reached
381			break;
382		}
383	}
384
385	struct DebugVector* dv = malloc(sizeof(struct DebugVector));
386	if (state == PARSE_ERROR) {
387		dv->type = ERROR_TYPE;
388		dv->next = 0;
389	} else {
390		dvTemp.intValue = current;
391		*dv = dvTemp;
392		if (string[0] == ' ') {
393			dv->next = _DVParse(debugger, string + 1);
394		}
395	}
396	return dv;
397}
398
399static void _DVFree(struct DebugVector* dv) {
400	struct DebugVector* next;
401	while (dv) {
402		next = dv->next;
403		free(dv);
404		dv = next;
405	}
406}
407
408static void _parse(struct ARMDebugger* debugger, const char* line) {
409	char* firstSpace = strchr(line, ' ');
410	size_t cmdLength;
411	struct DebugVector* dv = 0;
412	if (firstSpace) {
413		cmdLength = firstSpace - line;
414		dv = _DVParse(debugger, firstSpace + 1);
415		if (dv->type == ERROR_TYPE) {
416			printf("Parse error\n");
417			_DVFree(dv);
418			return;
419		}
420	} else {
421		cmdLength = strlen(line);
422	}
423
424	int i;
425	const char* name;
426	for (i = 0; (name = debuggerCommands[i].name); ++i) {
427		if (strlen(name) != cmdLength) {
428			continue;
429		}
430		if (strncasecmp(name, line, cmdLength) == 0) {
431			debuggerCommands[i].command(debugger, dv);
432			_DVFree(dv);
433			linenoiseHistoryAdd(line);
434			return;
435		}
436	}
437	_DVFree(dv);
438	ARMRun(debugger->cpu);
439	_printStatus(debugger, 0);
440}
441
442void ARMDebuggerInit(struct ARMDebugger* debugger, struct ARMCore* cpu) {
443	debugger->cpu = cpu;
444}
445
446void ARMDebuggerEnter(struct ARMDebugger* debugger) {
447	char* line;
448	_printStatus(debugger, 0);
449	while ((line = linenoise("> "))) {
450		_parse(debugger, line);
451		free(line);
452		switch (debugger->state) {
453		case DEBUGGER_EXITING:
454			return;
455		default:
456			break;
457		}
458	}
459}