all repos — mgba @ 5b7da978d1944a5bdb16e60232aead38f80eefc7

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