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