|
|
1.1 root 1: /*
2: Hatari - debugui.c
3:
4: This file is distributed under the GNU Public License, version 2 or at
5: your option any later version. Read the file gpl.txt for details.
6:
7: debugui.c - this is the code for the mini-debugger. When the pause button is
8: pressed, the emulator is (hopefully) halted and this little CLI can be used
9: (in the terminal box) for debugging tasks like memory and register dumps.
10: */
11: const char DebugUI_fileid[] = "Hatari debugui.c : " __DATE__ " " __TIME__;
12:
13: #include <ctype.h>
14: #include <stdio.h>
15: #include <unistd.h>
16:
17: #include "config.h"
18:
19: #if HAVE_LIBREADLINE
20: #include <readline/readline.h>
21: #include <readline/history.h>
22: #endif
23:
24: #include "main.h"
25: #include "change.h"
26: #include "configuration.h"
27: #include "file.h"
28: #include "log.h"
29: #include "m68000.h"
30: #include "memorySnapShot.h"
31: #include "options.h"
32: #include "screen.h"
33: #include "statusbar.h"
34: #include "str.h"
35:
36: #include "debug_priv.h"
37: #include "breakcond.h"
38: #include "debugcpu.h"
39: #include "debugdsp.h"
40: #include "debugInfo.h"
41: #include "debugui.h"
42: #include "evaluate.h"
43: #include "symbols.h"
44:
45: int bExceptionDebugging;
46:
47: FILE *debugOutput;
48:
49: static dbgcommand_t *debugCommand;
50: static int debugCommands;
51:
52: /* stores last 'e' command result as hex, used for TAB-completion */
53: static char lastResult[10];
54:
55: static const char *parseFileName;
56:
57:
58: /**
59: * Save/Restore snapshot of debugging session variables
60: */
61: void DebugUI_MemorySnapShot_Capture(const char *path, bool bSave)
62: {
63: char *filename;
64:
65: filename = malloc(strlen(path) + strlen(".debug") + 1);
66: assert(filename);
67: strcpy(filename, path);
68: strcat(filename, ".debug");
69:
70: if (bSave)
71: {
72: /* save breakpoints as debugger input file */
73: BreakCond_Save(filename);
74: }
75: else
76: {
77: /* remove current CPU and DSP breakpoints */
78: BreakCond_Command("all", false);
79: BreakCond_Command("all", true);
80:
81: if (File_Exists(filename))
82: {
83: /* and parse back the saved breakpoints */
84: DebugUI_ParseFile(filename);
85: }
86: }
87: free(filename);
88: }
89:
90:
91: /**
92: * Close a log file if open, and set it to default stream.
93: */
94: static void DebugUI_SetLogDefault(void)
95: {
96: if (debugOutput != stderr)
97: {
98: if (debugOutput)
99: {
100: File_Close(debugOutput);
101: fprintf(stderr, "Debug log closed.\n");
102: }
103: debugOutput = stderr;
104: }
105: }
106:
107:
108: /**
109: * Open (or close) given log file.
110: */
111: static int DebugUI_SetLogFile(int nArgc, char *psArgs[])
112: {
113: File_Close(debugOutput);
114: debugOutput = NULL;
115:
116: if (nArgc > 1)
117: debugOutput = File_Open(psArgs[1], "w");
118:
119: if (debugOutput)
120: fprintf(stderr, "Debug log '%s' opened.\n", psArgs[1]);
121: else
122: debugOutput = stderr;
123:
124: return DEBUGGER_CMDDONE;
125: }
126:
127:
128: /**
129: * Helper to print given value in all supported number bases
130: */
131: static void DebugUI_PrintValue(Uint32 value)
132: {
133: bool one, ones;
134: int bit;
135:
136: fputs("= %", stderr);
137: ones = false;
138: for (bit = 31; bit >= 0; bit--)
139: {
140: one = value & (1<<bit);
141: if (one || ones)
142: {
143: fputc(one ? '1':'0', stderr);
144: ones = true;
145: }
146: }
147: if (!ones)
148: fputc('0', stderr);
149: if (value & 0x80000000)
150: fprintf(stderr, " (bin), #%u/%d (dec), $%x (hex)\n", value, (int)value, value);
151: else
152: fprintf(stderr, " (bin), #%u (dec), $%x (hex)\n", value, value);
153:
154: sprintf(lastResult, "%x", value);
155: }
156:
157:
158: /**
159: * Commmand: Evaluate an expression with CPU reg and symbol parsing.
160: */
161: static int DebugUI_Evaluate(int nArgc, char *psArgs[])
162: {
163: const char *errstr, *expression = (const char *)psArgs[1];
164: Uint32 result;
165: int offset;
166:
167: if (nArgc < 2)
168: {
169: DebugUI_PrintCmdHelp(psArgs[0]);
170: return DEBUGGER_CMDDONE;
171: }
172:
173: errstr = Eval_Expression(expression, &result, &offset, false);
174: if (errstr)
175: fprintf(stderr, "ERROR in the expression:\n'%s'\n%*c-%s\n",
176: expression, offset+3, '^', errstr);
177: else
178: DebugUI_PrintValue(result);
179: return DEBUGGER_CMDDONE;
180: }
181:
182:
183: /**
184: * Check whether given string is a two letter command starting with 'd'
185: * or a long command starting with "dsp". String should be trimmed.
186: * Return true if given string is command for DSP, false otherwise.
187: */
188: static bool DebugUI_IsForDsp(const char *cmd)
189: {
190: return ((cmd[0] == 'd' && cmd[1] && !cmd[2])
191: || strncmp(cmd, "dsp", 3) == 0);
192: }
193:
194: /**
195: * Evaluate everything include within "" and replace them with the result.
1.1.1.2 ! root 196: * Caller needs to free the returned string separately if its address
! 197: * doesn't match the given input string.
1.1 root 198: *
1.1.1.2 ! root 199: * Return string with expressions (potentially) expanded, or
! 200: * NULL when there's an error in the expression.
1.1 root 201: */
202: static char *DebugUI_EvaluateExpressions(char *input)
203: {
204: int offset, count, diff, inputlen;
1.1.1.2 ! root 205: char *end, *start, *initial;
1.1 root 206: const char *errstr;
207: char valuestr[12];
208: Uint32 value;
209: bool fordsp;
210:
211: /* input is split later on, need to save len here */
212: fordsp = DebugUI_IsForDsp(input);
213: inputlen = strlen(input);
1.1.1.2 ! root 214: initial = start = input;
1.1 root 215:
216: while ((start = strchr(start, '"')))
217: {
218: end = strchr(start+1, '"');
219: if (!end)
220: {
221: fprintf(stderr, "ERROR: matching '\"' missing from '%s'!\n", start);
222: return NULL;
223: }
224:
225: if (end == start+1)
226: {
227: /* empty expression */
228: memmove(start, start+2, strlen(start+2)+1);
229: continue;
230: }
231:
232: *end = '\0';
233: errstr = Eval_Expression(start+1, &value, &offset, fordsp);
234: if (errstr) {
235: *end = '"';
236: fprintf(stderr, "Expression ERROR:\n'%s'\n%*c-%s\n",
237: input, (int)(start-input)+offset+3, '^', errstr);
238: return NULL;
239: }
240: end++;
241:
242: count = sprintf(valuestr, "$%x", value);
243: fprintf(stderr, "- \"%s\" -> %s\n", start+1, valuestr);
244:
245: diff = end-start;
246: if (count < diff)
247: {
248: memcpy(start, valuestr, count);
249: start += count;
250: memmove(start, end, strlen(end) + 1);
251: } else {
252: /* value won't fit to expression, expand string */
253: char *tmp;
254: inputlen += count-diff+1;
255: tmp = malloc(inputlen+1);
256: if (!tmp)
257: {
258: perror("ERROR: Input string alloc failed\n");
259: return NULL;
260: }
261:
262: memcpy(tmp, input, start-input);
263: start = tmp+(start-input);
264: memcpy(start, valuestr, count);
265: start += count;
266: memcpy(start, end, strlen(end) + 1);
267:
1.1.1.2 ! root 268: if (input != initial)
! 269: free(input);
1.1 root 270: input = tmp;
271: }
272: }
273: /* no (more) expressions to evaluate */
274: return input;
275: }
276:
277:
278: /**
279: * Command: Store and restore emulation state
280: */
281: static int DebugUI_DoMemorySnap(int argc, char *argv[])
282: {
283: const char *file;
284:
285: if (argc > 1)
286: file = argv[1];
287: else
288: file = ConfigureParams.Memory.szMemoryCaptureFileName;
289:
290: if (strcmp(argv[0], "stateload") == 0)
291: MemorySnapShot_Restore(file, true);
292: else
293: MemorySnapShot_Capture(file, true);
294:
295: return DEBUGGER_CMDDONE;
296: }
297:
298:
299: /**
300: * Command: Set command line and debugger options
301: */
302: static int DebugUI_SetOptions(int argc, char *argv[])
303: {
304: CNF_PARAMS current;
305: static const struct {
306: const char name[4];
307: int base;
308: } bases[] = {
309: { "bin", 2 },
310: { "dec", 10 },
311: { "hex", 16 }
312: };
313: const char *arg;
314: int i;
315:
316: if (argc < 2)
317: {
318: DebugUI_PrintCmdHelp(argv[0]);
319: return DEBUGGER_CMDDONE;
320: }
321: arg = argv[1];
322:
323: for (i = 0; i < ARRAYSIZE(bases); i++)
324: {
325: if (strcasecmp(bases[i].name, arg) == 0)
326: {
327: if (ConfigureParams.Debugger.nNumberBase != bases[i].base)
328: {
329: fprintf(stderr, "Switched default number base from %d to %d-based (%s) values.\n",
330: ConfigureParams.Debugger.nNumberBase,
331: bases[i].base, bases[i].name);
332: ConfigureParams.Debugger.nNumberBase = bases[i].base;
333: } else {
334: fprintf(stderr, "Already in '%s' mode.\n", bases[i].name);
335: }
336: return DEBUGGER_CMDDONE;
337: }
338: }
339:
340: /* get configuration changes */
341: current = ConfigureParams;
342:
343: /* Parse and apply options */
1.1.1.2 ! root 344: if (Opt_ParseParameters(argc, (const char * const *)argv))
1.1 root 345: {
346: ConfigureParams.Screen.bFullScreen = false;
347: Change_CopyChangedParamsToConfiguration(¤t, &ConfigureParams, false);
348: }
349: else
350: {
351: ConfigureParams = current;
352: }
353:
354: return DEBUGGER_CMDDONE;
355: }
356:
357:
358: /**
359: * Command: Set tracing
360: */
361: static int DebugUI_SetTracing(int argc, char *argv[])
362: {
363: const char *errstr;
364: if (argc != 2)
365: {
366: DebugUI_PrintCmdHelp(argv[0]);
367: return DEBUGGER_CMDDONE;
368: }
369: errstr = Log_SetTraceOptions(argv[1]);
370: if (errstr && errstr[0])
371: fprintf(stderr, "ERROR: %s\n", errstr);
372:
373: return DEBUGGER_CMDDONE;
374: }
375:
376:
377: /**
378: * Command: Change Hatari work directory
379: */
380: static int DebugUI_ChangeDir(int argc, char *argv[])
381: {
382: if (argc == 2)
383: {
384: if (chdir(argv[1]) == 0)
385: return DEBUGGER_CMDDONE;
386: perror("ERROR");
387: }
388: DebugUI_PrintCmdHelp(argv[0]);
389: return DEBUGGER_CMDDONE;
390: }
391:
392:
393: /**
394: * Command: Read debugger commands from a file
395: */
396: static int DebugUI_CommandsFromFile(int argc, char *argv[])
397: {
398: if (argc == 2)
399: DebugUI_ParseFile(argv[1]);
400: else
401: DebugUI_PrintCmdHelp(argv[0]);
402: return DEBUGGER_CMDDONE;
403: }
404:
405:
406: /**
407: * Command: Quit emulator
408: */
409: static int DebugUI_QuitEmu(int nArgc, char *psArgv[])
410: {
411: bQuitProgram = true;
412: M68000_SetSpecial(SPCFLAG_BRK); /* Assure that CPU core shuts down */
413: return DEBUGGER_END;
414: }
415:
416:
417: /**
418: * Print help text for one command
419: */
420: void DebugUI_PrintCmdHelp(const char *psCmd)
421: {
422: dbgcommand_t *cmd;
423: int i;
424:
425: /* Search the command ... */
426: for (cmd = debugCommand, i = 0; i < debugCommands; i++, cmd++)
427: {
428: if (!debugCommand[i].pFunction)
429: continue;
430: if ((*(cmd->sShortName) && !strcmp(psCmd, cmd->sShortName))
431: || !strcmp(psCmd, cmd->sLongName))
432: {
433: bool bShort = *(cmd->sShortName);
434: /* ... and print help text */
435: if (bShort)
436: {
437: fprintf(stderr, "'%s' or '%s' - %s\n",
438: cmd->sLongName,
439: cmd->sShortName,
440: cmd->sShortDesc);
441: }
442: else
443: {
444: fprintf(stderr, "'%s' - %s\n",
445: cmd->sLongName,
446: cmd->sShortDesc);
447: }
448: fprintf(stderr, "Usage: %s %s\n",
449: bShort ? cmd->sShortName : cmd->sLongName,
450: cmd->sUsage);
451: return;
452: }
453: }
454:
455: fprintf(stderr, "Unknown command '%s'\n", psCmd);
456: }
457:
458:
459: /**
460: * Command: Print debugger help screen.
461: */
462: static int DebugUI_Help(int nArgc, char *psArgs[])
463: {
464: int i;
465:
466: if (nArgc > 1)
467: {
468: DebugUI_PrintCmdHelp(psArgs[1]);
469: return DEBUGGER_CMDDONE;
470: }
471:
472: for (i = 0; i < debugCommands; i++)
473: {
474: if (!debugCommand[i].pFunction)
475: {
476: fprintf(stderr, "\n%s:\n", debugCommand[i].sLongName);
477: continue;
478: }
479: fprintf(stderr, " %12s (%2s) : %s\n", debugCommand[i].sLongName,
480: debugCommand[i].sShortName, debugCommand[i].sShortDesc);
481: }
482:
483: fprintf(stderr,
484: "\n"
485: "If value is prefixed with '$', it's a hexadecimal, if with '#', it's\n"
486: "a normal decimal, if with '%%', it's a binary decimal. Prefix can\n"
487: "be skipped for numbers in the default number base (currently %d).\n"
488: "\n"
489: "Any expression given in quotes (within \"\"), will be evaluated\n"
490: "before given to the debugger command. Any register and symbol\n"
491: "names in the expression are replaced by their values.\n"
492: "\n"
493: "Note that address ranges like '$fc0000-$fc0100' should have no\n"
494: "spaces between the range numbers.\n"
495: "\n"
496: "'help <command>' gives more help.\n", ConfigureParams.Debugger.nNumberBase);
497: return DEBUGGER_CMDDONE;
498: }
499:
500:
501: /**
502: * Parse debug command and execute it.
503: */
1.1.1.2 ! root 504: static int DebugUI_ParseCommand(const char *input_orig)
1.1 root 505: {
1.1.1.2 ! root 506: char *psArgs[64], *input;
1.1 root 507: const char *delim;
508: static char sLastCmd[80] = { '\0' };
509: int nArgc, cmd = -1;
510: int i, retval;
511:
1.1.1.2 ! root 512: input = strdup(input_orig);
1.1 root 513: psArgs[0] = strtok(input, " \t");
514:
515: if (psArgs[0] == NULL)
516: {
517: if (strlen(sLastCmd) > 0)
518: psArgs[0] = sLastCmd;
519: else
1.1.1.2 ! root 520: {
! 521: free(input);
1.1 root 522: return DEBUGGER_CMDDONE;
1.1.1.2 ! root 523: }
1.1 root 524: }
525:
526: /* Search the command ... */
527: for (i = 0; i < debugCommands; i++)
528: {
529: if (!debugCommand[i].pFunction)
530: continue;
531: if (!strcmp(psArgs[0], debugCommand[i].sShortName) ||
532: !strcmp(psArgs[0], debugCommand[i].sLongName))
533: {
534: cmd = i;
535: break;
536: }
537: }
538: if (cmd == -1)
539: {
540: fprintf(stderr, "Command '%s' not found.\n"
541: "Use 'help' to view a list of available commands.\n",
542: psArgs[0]);
1.1.1.2 ! root 543: free(input);
1.1 root 544: return DEBUGGER_CMDDONE;
545: }
546:
547: if (debugCommand[cmd].bNoParsing)
548: delim = "";
549: else
550: delim = " \t";
551:
552: /* Separate arguments and put the pointers into psArgs */
553: for (nArgc = 1; nArgc < ARRAYSIZE(psArgs); nArgc++)
554: {
555: psArgs[nArgc] = strtok(NULL, delim);
556: if (psArgs[nArgc] == NULL)
557: break;
558: }
559:
560: if (!debugOutput) {
561: /* make sure also calls from control.c work */
562: DebugUI_SetLogDefault();
563: }
564:
565: /* ... and execute the function */
566: retval = debugCommand[i].pFunction(nArgc, psArgs);
567: /* Save commando string if it can be repeated */
568: if (retval == DEBUGGER_CMDCONT)
569: strncpy(sLastCmd, psArgs[0], sizeof(sLastCmd));
570: else
571: sLastCmd[0] = '\0';
1.1.1.2 ! root 572: free(input);
1.1 root 573: return retval;
574: }
575:
576:
577: /* See "info:readline" e.g. in Konqueror for readline usage. */
578:
579: /**
580: * Readline match callback for long command name completion.
581: * STATE = 0 -> different text from previous one.
582: * Return next match or NULL if no matches.
583: */
584: static char *DebugUI_MatchCommand(const char *text, int state)
585: {
586: static int i, len;
587: const char *name;
588:
589: if (!state)
590: {
591: /* first match */
592: len = strlen(text);
593: i = 0;
594: }
595: /* next match */
596: while (i < debugCommands)
597: {
598: name = debugCommand[i].sLongName;
599: if (debugCommand[i++].pFunction &&
600: strncmp(name, text, len) == 0)
601: return (strdup(name));
602: }
603: return NULL;
604: }
605:
606:
607: #if HAVE_LIBREADLINE
608: /**
609: * Readline match callback returning last result.
610: */
611: static char *DebugUI_MatchLast(const char *text, int state)
612: {
613: if (state)
614: return NULL;
615: return strdup(lastResult);
616: }
617:
618: /**
619: * Readline completion callback. Returns matches.
620: */
621: static char **DebugUI_Completion(const char *text, int a, int b)
622: {
623: int i, cmd, quotes, end, start = 0;
624: char *str, buf[32];
625: size_t len;
626:
627: /* check where's the first word (ignore white space) */
628: while (start < rl_point && isspace(rl_line_buffer[start]))
629: start++;
630: end = start;
631: while (end < rl_point && !isspace(rl_line_buffer[end]))
632: end++;
633:
634: if (end >= rl_point)
635: /* first word on line */
636: return rl_completion_matches(text, DebugUI_MatchCommand);
637:
638: /* complete '$' with last result? */
639: if (lastResult[0] && rl_line_buffer[rl_point-1] == '$')
640: return rl_completion_matches(text, DebugUI_MatchLast);
641:
642: /* check which command args are to be completed */
643: len = end - start;
644: if (len >= sizeof(buf))
645: len = sizeof(buf)-1;
646: memcpy(buf, &(rl_line_buffer[start]), len);
647: buf[len] = '\0';
648:
649: /* expression completion needed (= open quote)? */
650: str = strchr(&(rl_line_buffer[end]), '"');
651: quotes = 0;
652: while (str)
653: {
654: quotes++;
655: str = strchr(str+1, '"');
656: }
657: if (quotes & 1)
658: {
659: if (DebugUI_IsForDsp(buf))
660: return rl_completion_matches(text, Symbols_MatchDspAddress);
661: return rl_completion_matches(text, Symbols_MatchCpuAddress);
662: }
663:
664: /* do command argument completion */
665: cmd = -1;
666: for (i = 0; i < debugCommands; i++)
667: {
668: if (!debugCommand[i].pFunction)
669: continue;
670: if (!strcmp(buf, debugCommand[i].sShortName) ||
671: !strcmp(buf, debugCommand[i].sLongName))
672: {
673: cmd = i;
674: break;
675: }
676: }
677: if (cmd < 0)
678: {
679: rl_attempted_completion_over = true;
680: return NULL;
681: }
682: if (debugCommand[cmd].pMatch)
683: return rl_completion_matches(text, debugCommand[cmd].pMatch);
684: else
685: return rl_completion_matches(text, rl_filename_completion_function);
686: }
687:
688:
689: /**
690: * Read a command line from the keyboard and return a pointer to the string.
1.1.1.2 ! root 691: * Only string returned by this function can be given for it as argument!
! 692: * The string will be stored into command history buffer.
! 693: * @return Pointer to the string which should be deallocated after
! 694: * use or given back to this function for re-use/history.
! 695: * Returns NULL when error occured.
1.1 root 696: */
1.1.1.2 ! root 697: static char *DebugUI_GetCommand(char *input)
1.1 root 698: {
699: /* Allow conditional parsing of the ~/.inputrc file. */
700: rl_readline_name = "Hatari";
701:
702: /* Tell the completer that we want a crack first. */
703: rl_attempted_completion_function = DebugUI_Completion;
704:
1.1.1.2 ! root 705: if (input && *input)
! 706: {
! 707: HIST_ENTRY *hist = previous_history();
! 708: /* don't store successive duplicate entries */
! 709: if (!hist || !hist->line || strcmp(hist->line, input) != 0)
! 710: add_history(input);
! 711: free(input);
! 712: }
1.1 root 713:
1.1.1.2 ! root 714: return Str_Trim(readline("> "));
1.1 root 715: }
716:
717: #else /* !HAVE_LIBREADLINE */
718:
719: /**
720: * Read a command line from the keyboard and return a pointer to the string.
1.1.1.2 ! root 721: * Only string returned by this function can be given for it as argument!
! 722: * @return Pointer to the string which should be deallocated after
! 723: * use or given back to this function for re-use.
! 724: * Returns NULL when error occured.
1.1 root 725: */
1.1.1.2 ! root 726: static char *DebugUI_GetCommand(char *input)
1.1 root 727: {
728: fprintf(stderr, "> ");
729: if (!input)
1.1.1.2 ! root 730: {
! 731: input = malloc(256);
! 732: assert(input);
! 733: }
1.1 root 734: input[0] = '\0';
735: if (fgets(input, 256, stdin) == NULL)
736: {
737: free(input);
738: return NULL;
739: }
740: return Str_Trim(input);
741: }
742:
743: #endif /* !HAVE_LIBREADLINE */
744:
745:
746: static const dbgcommand_t uicommand[] =
747: {
748: { NULL, NULL, "Generic commands", NULL, NULL, NULL, false },
749: /* NULL as match function will complete file names */
750: { DebugUI_ChangeDir, NULL,
751: "cd", "",
752: "change directory",
753: "<directory>\n"
754: "\tChange Hatari work directory.",
755: false },
756: { DebugUI_Evaluate, Symbols_MatchCpuAddress,
757: "evaluate", "e",
758: "evaluate an expression",
759: "<expression>\n"
760: "\tEvaluate an expression and show the result. Expression can\n"
761: "\tinclude CPU register and symbol names, those are replaced\n"
762: "\tby their values. Supported operators in expressions are,\n"
763: "\tin the decending order of precedence:\n"
764: "\t\t(), +, -, ~, *, /, +, -, >>, <<, ^, &, |\n"
1.1.1.2 ! root 765: "\tParenthesis will fetch a _long_ value from the address\n"
! 766: "\tto what the value inside it evaluates to. Prefixes can be\n"
! 767: "\tused only in start of line or parenthesis.\n"
1.1 root 768: "\tFor example:\n"
1.1.1.2 ! root 769: "\t\t~%101 & $f0f0f ^ (d0 + 0x21)\n"
1.1 root 770: "\tResult value is shown as binary, decimal and hexadecimal.\n"
771: "\tAfter this, '$' will TAB-complete to last result value.",
772: true },
773: { DebugUI_Help, DebugUI_MatchCommand,
774: "help", "h",
775: "print help",
776: "[command]\n"
777: "\tPrint help text for available commands.",
778: false },
779: { DebugInfo_Command, DebugInfo_MatchInfo,
780: "info", "i",
781: "show machine/OS information",
782: "[subject [arg]]\n"
783: "\tPrint information on requested subject or list them if\n"
784: "\tno subject given.",
785: false },
786: { DebugInfo_Command, DebugInfo_MatchLock,
787: "lock", "",
1.1.1.2 ! root 788: "specify information to show on entering the debugger",
1.1 root 789: "[subject [args]]\n"
1.1.1.2 ! root 790: "\tLock what information should be shown every time debugger\n"
! 791: "\tis entered, or list available options if no subject's given.",
1.1 root 792: false },
793: { DebugUI_SetLogFile, NULL,
794: "logfile", "f",
795: "open or close log file",
796: "[filename]\n"
797: "\tOpen log file, no argument closes the log file. Output of\n"
798: "\tregister & memory dumps and disassembly will be written to it.",
799: false },
800: { DebugUI_CommandsFromFile, NULL,
801: "parse", "p",
802: "get debugger commands from file",
803: "[filename]\n"
804: "\tRead debugger commands from given file and do them.",
805: false },
806: { DebugUI_SetOptions, Opt_MatchOption,
807: "setopt", "o",
808: "set Hatari command line and debugger options",
809: "[<bin|dec|hex>|<command line options>]\n"
810: "\tSet Hatari options. For example to enable exception catching,\n"
811: "\tuse following command line option: 'setopt --debug'. Special\n"
812: "\t'bin', 'dec' and 'hex' arguments change the default number base\n"
813: "\tused in debugger.",
814: false },
815: { DebugUI_DoMemorySnap, NULL,
816: "stateload", "",
817: "restore emulation state",
818: "[filename]\n"
819: "\tRestore emulation snapshot from default or given file",
820: false },
821: { DebugUI_DoMemorySnap, NULL,
822: "statesave", "",
823: "save emulation state",
824: "[filename]\n"
825: "\tSave emulation snapshot to default or given file",
826: false },
827: { DebugUI_SetTracing, Log_MatchTrace,
828: "trace", "t",
829: "select Hatari tracing settings",
830: "[set1,set2...]\n"
1.1.1.2 ! root 831: "\tSelect Hatari tracing settings. 'help' shows all the available\n"
! 832: "\tsettings. For example, to enable CPU disassembly and VBL\n"
! 833: "\ttracing, use:\n\t\ttrace cpu_disasm,video_hbl",
1.1 root 834: false },
835: { DebugUI_QuitEmu, NULL,
836: "quit", "q",
837: "quit emulator",
838: "\n"
839: "\tLeave debugger and quit emulator.",
840: false }
841: };
842:
843:
844: /**
845: * Debugger user interface initialization.
846: */
847: void DebugUI_Init(void)
848: {
849: const dbgcommand_t *cpucmd, *dspcmd;
850: int cpucmds, dspcmds;
851:
852: /* already intialized? */
853: if (debugCommands)
854: return;
855:
856: /* if you want disassembly or memdumping to start/continue from
857: * specific address, you can set them in these functions.
858: */
859: dspcmds = DebugDsp_Init(&dspcmd);
860: cpucmds = DebugCpu_Init(&cpucmd);
861:
862: /* on first time copy the command structures to a single table */
863: debugCommands = ARRAYSIZE(uicommand);
864: debugCommand = malloc(sizeof(dbgcommand_t) * (dspcmds + cpucmds + debugCommands));
865: assert(debugCommand);
866:
867: memcpy(debugCommand, uicommand, sizeof(dbgcommand_t) * debugCommands);
868: memcpy(&debugCommand[debugCommands], cpucmd, sizeof(dbgcommand_t) * cpucmds);
869: debugCommands += cpucmds;
870: memcpy(&debugCommand[debugCommands], dspcmd, sizeof(dbgcommand_t) * dspcmds);
871: debugCommands += dspcmds;
872:
873: if (parseFileName)
874: DebugUI_ParseFile(parseFileName);
875: }
876:
877:
878: /**
1.1.1.2 ! root 879: * Set debugger commands file during Hatari startup before things
! 880: * needed by the debugger are initialized so that it can be parsed
! 881: * when debugger itself gets initialized.
1.1 root 882: * Return true if file exists, false otherwise.
883: */
884: bool DebugUI_SetParseFile(const char *path)
885: {
886: if (File_Exists(path))
887: {
888: parseFileName = path;
889: return true;
890: }
891: fprintf(stderr, "ERROR: debugger input file '%s' missing.\n", path);
892: return false;
893: }
894:
895:
896: /**
897: * Debugger user interface main function.
898: */
899: void DebugUI(void)
900: {
901: int cmdret, alertLevel;
1.1.1.2 ! root 902: char *expCmd, *psCmd = NULL;
1.1 root 903: static const char *welcome =
904: "\n----------------------------------------------------------------------"
905: "\nYou have entered debug mode. Type c to continue emulation, h for help.\n";
906:
907: if (bInFullScreen)
908: Screen_ReturnFromFullScreen();
909:
910: DebugUI_Init();
911:
912: if (welcome)
913: {
914: fputs(welcome, stderr);
915: welcome = NULL;
916: }
917: DebugCpu_InitSession();
918: DebugDsp_InitSession();
919: DebugInfo_ShowSessionInfo();
920:
921: /* override paused message so that user knows to look into console
922: * on how to continue in case he invoked the debugger by accident.
923: */
924: Statusbar_AddMessage("Console Debugger", 100);
925: Statusbar_Update(sdlscrn);
926:
927: /* disable normal GUI alerts while on console */
928: alertLevel = Log_SetAlertLevel(LOG_FATAL);
929:
930: cmdret = DEBUGGER_CMDDONE;
931: do
932: {
1.1.1.2 ! root 933: /* Read command from the keyboard and give previous
! 934: * command for freeing / adding to history
! 935: */
! 936: psCmd = DebugUI_GetCommand(psCmd);
1.1 root 937: if (!psCmd)
938: break;
939:
1.1.1.2 ! root 940: /* returns new string if expressions needed expanding! */
! 941: if (!(expCmd = DebugUI_EvaluateExpressions(psCmd)))
1.1 root 942: continue;
943:
944: /* Parse and execute the command string */
1.1.1.2 ! root 945: cmdret = DebugUI_ParseCommand(expCmd);
! 946: if (expCmd != psCmd)
! 947: free(expCmd);
1.1 root 948: }
949: while (cmdret != DEBUGGER_END);
950:
1.1.1.2 ! root 951: /* free (and ignore) exit command */
! 952: if (psCmd)
! 953: free(psCmd);
! 954:
1.1 root 955: Log_SetAlertLevel(alertLevel);
956: DebugUI_SetLogDefault();
957:
958: DebugCpu_SetDebugging();
959: DebugDsp_SetDebugging();
960: }
961:
962:
963: /**
964: * Read debugger commands from a file.
965: * return false for error, true for success.
966: */
1.1.1.2 ! root 967: bool DebugUI_ParseFile(const char *path)
1.1 root 968: {
1.1.1.2 ! root 969: char *olddir, *dir, *cmd, *input, *expanded, *slash;
1.1 root 970: FILE *fp;
971:
972: fprintf(stderr, "Reading debugger commands from '%s'...\n", path);
973: if (!(fp = fopen(path, "r")))
974: {
975: perror("ERROR");
976: return false;
977: }
978:
979: /* change to directory where the debugger file resides */
1.1.1.2 ! root 980: olddir = NULL;
1.1 root 981: dir = strdup(path);
982: slash = strrchr(dir, PATHSEP);
983: if (slash)
984: {
1.1.1.2 ! root 985: olddir = malloc(FILENAME_MAX);
! 986: if (olddir)
! 987: {
! 988: if (!getcwd(olddir, FILENAME_MAX))
! 989: strcpy(olddir, ".");
! 990: }
1.1 root 991: *slash = '\0';
1.1.1.2 ! root 992: if (chdir(dir) != 0)
1.1 root 993: {
994: perror("ERROR");
1.1.1.2 ! root 995: if (olddir)
! 996: free(olddir);
1.1 root 997: free(dir);
998: return false;
999: }
1.1.1.2 ! root 1000: fprintf(stderr, "Changed to input file dir '%s'.\n", dir);
1.1 root 1001: }
1002: free(dir);
1003:
1004: input = NULL;
1005: for (;;)
1006: {
1007: if (!input)
1008: {
1009: input = malloc(256);
1010: assert(input);
1011: }
1012: if (!fgets(input, 256, fp))
1013: break;
1014:
1.1.1.2 ! root 1015: /* ignore empty and comment lines */
! 1016: cmd = Str_Trim(input);
! 1017: if (!*cmd || *cmd == '#')
1.1 root 1018: continue;
1019:
1.1.1.2 ! root 1020: /* returns new string if input needed expanding! */
! 1021: expanded = DebugUI_EvaluateExpressions(input);
! 1022: if (!expanded)
! 1023: continue;
! 1024:
! 1025: cmd = Str_Trim(expanded);
! 1026: fprintf(stderr, "> %s\n", cmd);
! 1027: DebugUI_ParseCommand(cmd);
! 1028: if (expanded != input)
! 1029: free(expanded);
1.1 root 1030: }
1031:
1032: free(input);
1.1.1.2 ! root 1033: if (olddir)
! 1034: {
! 1035: if (chdir(olddir) != 0)
! 1036: perror("ERROR");
! 1037: else
! 1038: fprintf(stderr, "Changed back to '%s' dir.\n", olddir);
! 1039: free(olddir);
! 1040: }
1.1 root 1041:
1042: DebugCpu_SetDebugging();
1043: DebugDsp_SetDebugging();
1044: return true;
1045: }
1046:
1047:
1048: /**
1049: * Remote/parallel debugger usage API.
1050: * Return false for failed command, true for success.
1051: */
1052: bool DebugUI_RemoteParse(char *input)
1053: {
1054: int ret;
1055:
1056: DebugUI_Init();
1057:
1058: ret = DebugUI_ParseCommand(input);
1059:
1060: DebugCpu_SetDebugging();
1061: DebugDsp_SetDebugging();
1062:
1063: return (ret == DEBUGGER_CMDDONE);
1064: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.