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