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