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