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