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