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