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