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