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