|
|
1.1 root 1: /*
2: Hatari - debugui.c
3:
4: This file is distributed under the GNU Public License, version 2 or at
5: your option any later version. Read the file gpl.txt for details.
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 "screen.h"
31: #include "statusbar.h"
32: #include "str.h"
33:
34: #include "debug_priv.h"
35: #include "breakcond.h"
36: #include "debugcpu.h"
37: #include "debugInfo.h"
38: #include "debugui.h"
39: #include "evaluate.h"
40: #include "symbols.h"
41:
42: int bExceptionDebugging;
43:
44: FILE *debugOutput = NULL;
45:
46: static dbgcommand_t *debugCommand;
47: static int debugCommands;
48:
49: /* stores last 'e' command result as hex, used for TAB-completion */
50: static char lastResult[10];
51:
52: static const char *parseFileName;
53:
54:
55: /**
56: * Save/Restore snapshot of debugging session variables
57: */
58: void DebugUI_MemorySnapShot_Capture(const char *path, bool bSave)
59: {
60: char *filename;
61:
62: filename = malloc(strlen(path) + strlen(".debug") + 1);
63: assert(filename);
64: strcpy(filename, path);
65: strcat(filename, ".debug");
66:
67: if (bSave)
68: {
69: /* save breakpoints as debugger input file */
70: BreakCond_Save(filename);
71: }
72: else
73: {
74: /* remove current CPU and DSP breakpoints */
75: BreakCond_Command("all", false);
76: BreakCond_Command("all", true);
77:
78: if (File_Exists(filename))
79: {
80: /* and parse back the saved breakpoints */
81: DebugUI_ParseFile(filename);
82: }
83: }
84: free(filename);
85: }
86:
87:
88: /**
89: * Close a log file if open, and set it to default stream.
90: */
91: static void DebugUI_SetLogDefault(void)
92: {
93: if (debugOutput != stderr)
94: {
95: if (debugOutput)
96: {
97: File_Close(debugOutput);
98: fprintf(stderr, "Debug log closed.\n");
99: }
100: debugOutput = stderr;
101: }
102: }
103:
104:
105: /**
106: * Open (or close) given log file.
107: */
108: static int DebugUI_SetLogFile(int nArgc, char *psArgs[])
109: {
110: File_Close(debugOutput);
111: debugOutput = NULL;
112:
113: if (nArgc > 1)
114: debugOutput = File_Open(psArgs[1], "w");
115:
116: if (debugOutput)
117: fprintf(stderr, "Debug log '%s' opened.\n", psArgs[1]);
118: else
119: debugOutput = stderr;
120:
121: return DEBUGGER_CMDDONE;
122: }
123:
124:
125: /**
126: * Helper to print given value in all supported number bases
127: */
128: static void DebugUI_PrintValue(Uint32 value)
129: {
130: bool one, ones;
131: int bit;
132:
133: fputs("= %", stderr);
134: ones = false;
135: for (bit = 31; bit >= 0; bit--)
136: {
137: one = value & (1<<bit);
138: if (one || ones)
139: {
140: fputc(one ? '1':'0', stderr);
141: ones = true;
142: }
143: }
144: if (!ones)
145: fputc('0', stderr);
146: if (value & 0x80000000)
147: fprintf(stderr, " (bin), #%u/%d (dec), $%x (hex)\n", value, (int)value, value);
148: else
149: fprintf(stderr, " (bin), #%u (dec), $%x (hex)\n", value, value);
150:
151: sprintf(lastResult, "%x", value);
152: }
153:
154:
155: /**
156: * Commmand: Evaluate an expression with CPU reg and symbol parsing.
157: */
158: static int DebugUI_Evaluate(int nArgc, char *psArgs[])
159: {
160: const char *errstr, *expression = (const char *)psArgs[1];
161: Uint32 result;
162: int offset;
163:
164: if (nArgc < 2)
165: {
166: DebugUI_PrintCmdHelp(psArgs[0]);
167: return DEBUGGER_CMDDONE;
168: }
169:
170: errstr = Eval_Expression(expression, &result, &offset, false);
171: if (errstr)
172: fprintf(stderr, "ERROR in the expression:\n'%s'\n%*c-%s\n",
173: expression, offset+3, '^', errstr);
174: else
175: DebugUI_PrintValue(result);
176: return DEBUGGER_CMDDONE;
177: }
178:
179:
180: /**
181: * Check whether given string is a two letter command starting with 'd'
182: * or a long command starting with "dsp". String should be trimmed.
183: * Return true if given string is command for DSP, false otherwise.
184: */
185: static bool DebugUI_IsForDsp(const char *cmd)
186: {
187: return ((cmd[0] == 'd' && cmd[1] && !cmd[2])
188: || strncmp(cmd, "dsp", 3) == 0);
189: }
190:
191: /**
192: * Evaluate everything include within "" and replace them with the result.
193: * Caller needs to free the returned string separately if its address
194: * doesn't match the given input string.
195: *
196: * Return string with expressions (potentially) expanded, or
197: * NULL when there's an error in the expression.
198: */
199: static char *DebugUI_EvaluateExpressions(char *input)
200: {
201: int offset, count, diff, inputlen;
202: char *end, *start, *initial;
203: const char *errstr;
204: char valuestr[12];
205: Uint32 value;
206: bool fordsp;
207:
208: /* input is split later on, need to save len here */
209: fordsp = DebugUI_IsForDsp(input);
210: inputlen = strlen(input);
211: initial = start = input;
212:
213: while ((start = strchr(start, '"')))
214: {
215: end = strchr(start+1, '"');
216: if (!end)
217: {
218: fprintf(stderr, "ERROR: matching '\"' missing from '%s'!\n", start);
219: return NULL;
220: }
221:
222: if (end == start+1)
223: {
224: /* empty expression */
225: memmove(start, start+2, strlen(start+2)+1);
226: continue;
227: }
228:
229: *end = '\0';
230: errstr = Eval_Expression(start+1, &value, &offset, fordsp);
231: if (errstr) {
232: *end = '"';
233: fprintf(stderr, "Expression ERROR:\n'%s'\n%*c-%s\n",
234: input, (int)(start-input)+offset+3, '^', errstr);
235: return NULL;
236: }
237: end++;
238:
239: count = sprintf(valuestr, "$%x", value);
240: fprintf(stderr, "- \"%s\" -> %s\n", start+1, valuestr);
241:
242: diff = end-start;
243: if (count < diff)
244: {
245: memcpy(start, valuestr, count);
246: start += count;
247: memmove(start, end, strlen(end) + 1);
248: } else {
249: /* value won't fit to expression, expand string */
250: char *tmp;
251: inputlen += count-diff+1;
252: tmp = malloc(inputlen+1);
253: if (!tmp)
254: {
255: perror("ERROR: Input string alloc failed\n");
256: return NULL;
257: }
258:
259: memcpy(tmp, input, start-input);
260: start = tmp+(start-input);
261: memcpy(start, valuestr, count);
262: start += count;
263: memcpy(start, end, strlen(end) + 1);
264:
265: if (input != initial)
266: free(input);
267: input = tmp;
268: }
269: }
270: /* no (more) expressions to evaluate */
271: return input;
272: }
273:
274: /**
275: * Command: Set command line and debugger options
276: */
277: static int DebugUI_SetOptions(int argc, char *argv[])
278: {
279: CNF_PARAMS current;
280: static const struct {
281: const char name[4];
282: int base;
283: } bases[] = {
284: { "bin", 2 },
285: { "dec", 10 },
286: { "hex", 16 }
287: };
288: const char *arg;
289: int i;
290:
291: if (argc < 2)
292: {
293: DebugUI_PrintCmdHelp(argv[0]);
294: return DEBUGGER_CMDDONE;
295: }
296: arg = argv[1];
297:
298: for (i = 0; i < ARRAYSIZE(bases); i++)
299: {
300: if (strcasecmp(bases[i].name, arg) == 0)
301: {
302: if (ConfigureParams.Debugger.nNumberBase != bases[i].base)
303: {
304: fprintf(stderr, "Switched default number base from %d to %d-based (%s) values.\n",
305: ConfigureParams.Debugger.nNumberBase,
306: bases[i].base, bases[i].name);
307: ConfigureParams.Debugger.nNumberBase = bases[i].base;
308: } else {
309: fprintf(stderr, "Already in '%s' mode.\n", bases[i].name);
310: }
311: return DEBUGGER_CMDDONE;
312: }
313: }
314:
315: /* get configuration changes */
316: current = ConfigureParams;
317: #if 0
318: /* Parse and apply options */
319: if (Opt_ParseParameters(argc, (const char * const *)argv))
320: {
321: ConfigureParams.Screen.bFullScreen = false;
322: Change_CopyChangedParamsToConfiguration(¤t, &ConfigureParams, false);
323: }
324: else
325: #endif
326: {
327: ConfigureParams = current;
328: }
329:
330: return DEBUGGER_CMDDONE;
331: }
332:
333:
334: /**
335: * Command: Set tracing
336: */
337: static int DebugUI_SetTracing(int argc, char *argv[])
338: {
339: const char *errstr;
340: if (argc != 2)
341: {
342: DebugUI_PrintCmdHelp(argv[0]);
343: return DEBUGGER_CMDDONE;
344: }
345: errstr = Log_SetTraceOptions(argv[1]);
346: if (errstr && errstr[0])
347: fprintf(stderr, "ERROR: %s\n", errstr);
348:
349: return DEBUGGER_CMDDONE;
350: }
351:
352:
353: /**
354: * Command: Change Hatari work directory
355: */
356: static int DebugUI_ChangeDir(int argc, char *argv[])
357: {
358: if (argc == 2)
359: {
360: if (chdir(argv[1]) == 0)
361: return DEBUGGER_CMDDONE;
362: perror("ERROR");
363: }
364: DebugUI_PrintCmdHelp(argv[0]);
365: return DEBUGGER_CMDDONE;
366: }
367:
368:
369: /**
370: * Command: Read debugger commands from a file
371: */
372: static int DebugUI_CommandsFromFile(int argc, char *argv[])
373: {
374: if (argc == 2)
375: DebugUI_ParseFile(argv[1]);
376: else
377: DebugUI_PrintCmdHelp(argv[0]);
378: return DEBUGGER_CMDDONE;
379: }
380:
381:
382: /**
383: * Command: Quit emulator
384: */
385: static int DebugUI_QuitEmu(int nArgc, char *psArgv[])
386: {
387: bQuitProgram = true;
388: M68000_SetSpecial(SPCFLAG_BRK); /* Assure that CPU core shuts down */
389: return DEBUGGER_END;
390: }
391:
392:
393: /**
394: * Print help text for one command
395: */
396: void DebugUI_PrintCmdHelp(const char *psCmd)
397: {
398: dbgcommand_t *cmd;
399: int i;
400:
401: /* Search the command ... */
402: for (cmd = debugCommand, i = 0; i < debugCommands; i++, cmd++)
403: {
404: if (!debugCommand[i].pFunction)
405: continue;
406: if ((*(cmd->sShortName) && !strcmp(psCmd, cmd->sShortName))
407: || !strcmp(psCmd, cmd->sLongName))
408: {
409: bool bShort = *(cmd->sShortName);
410: /* ... and print help text */
411: if (bShort)
412: {
413: fprintf(stderr, "'%s' or '%s' - %s\n",
414: cmd->sLongName,
415: cmd->sShortName,
416: cmd->sShortDesc);
417: }
418: else
419: {
420: fprintf(stderr, "'%s' - %s\n",
421: cmd->sLongName,
422: cmd->sShortDesc);
423: }
424: fprintf(stderr, "Usage: %s %s\n",
425: bShort ? cmd->sShortName : cmd->sLongName,
426: cmd->sUsage);
427: return;
428: }
429: }
430:
431: fprintf(stderr, "Unknown command '%s'\n", psCmd);
432: }
433:
434:
435: /**
436: * Command: Print debugger help screen.
437: */
438: static int DebugUI_Help(int nArgc, char *psArgs[])
439: {
440: int i;
441:
442: if (nArgc > 1)
443: {
444: DebugUI_PrintCmdHelp(psArgs[1]);
445: return DEBUGGER_CMDDONE;
446: }
447:
448: for (i = 0; i < debugCommands; i++)
449: {
450: if (!debugCommand[i].pFunction)
451: {
452: fprintf(stderr, "\n%s:\n", debugCommand[i].sLongName);
453: continue;
454: }
455: fprintf(stderr, " %12s (%2s) : %s\n", debugCommand[i].sLongName,
456: debugCommand[i].sShortName, debugCommand[i].sShortDesc);
457: }
458:
459: fprintf(stderr,
460: "\n"
461: "If value is prefixed with '$', it's a hexadecimal, if with '#', it's\n"
462: "a normal decimal, if with '%%', it's a binary decimal. Prefix can\n"
463: "be skipped for numbers in the default number base (currently %d).\n"
464: "\n"
465: "Any expression given in quotes (within \"\"), will be evaluated\n"
466: "before given to the debugger command. Any register and symbol\n"
467: "names in the expression are replaced by their values.\n"
468: "\n"
469: "Note that address ranges like '$fc0000-$fc0100' should have no\n"
470: "spaces between the range numbers.\n"
471: "\n"
472: "'help <command>' gives more help.\n", ConfigureParams.Debugger.nNumberBase);
473: return DEBUGGER_CMDDONE;
474: }
475:
476:
477: /**
478: * Parse debug command and execute it.
479: */
480: static int DebugUI_ParseCommand(const char *input_orig)
481: {
482: char *psArgs[64], *input;
483: const char *delim;
484: static char sLastCmd[80] = { '\0' };
485: int nArgc, cmd = -1;
486: int i, retval;
487:
488: input = strdup(input_orig);
489: psArgs[0] = strtok(input, " \t");
490:
491: if (psArgs[0] == NULL)
492: {
493: if (strlen(sLastCmd) > 0)
494: psArgs[0] = sLastCmd;
495: else
496: {
497: free(input);
498: return DEBUGGER_CMDDONE;
499: }
500: }
501:
502: /* Search the command ... */
503: for (i = 0; i < debugCommands; i++)
504: {
505: if (!debugCommand[i].pFunction)
506: continue;
507: if (!strcmp(psArgs[0], debugCommand[i].sShortName) ||
508: !strcmp(psArgs[0], debugCommand[i].sLongName))
509: {
510: cmd = i;
511: break;
512: }
513: }
514: if (cmd == -1)
515: {
516: fprintf(stderr, "Command '%s' not found.\n"
517: "Use 'help' to view a list of available commands.\n",
518: psArgs[0]);
519: free(input);
520: return DEBUGGER_CMDDONE;
521: }
522:
523: if (debugCommand[cmd].bNoParsing)
524: delim = "";
525: else
526: delim = " \t";
527:
528: /* Separate arguments and put the pointers into psArgs */
529: for (nArgc = 1; nArgc < ARRAYSIZE(psArgs); nArgc++)
530: {
531: psArgs[nArgc] = strtok(NULL, delim);
532: if (psArgs[nArgc] == NULL)
533: break;
534: }
535:
536: if (!debugOutput) {
537: /* make sure also calls from control.c work */
538: DebugUI_SetLogDefault();
539: }
540:
541: /* ... and execute the function */
542: retval = debugCommand[i].pFunction(nArgc, psArgs);
543: /* Save commando string if it can be repeated */
544: if (retval == DEBUGGER_CMDCONT)
545: strncpy(sLastCmd, psArgs[0], sizeof(sLastCmd));
546: else
547: sLastCmd[0] = '\0';
548: free(input);
549: return retval;
550: }
551:
552:
553: /* See "info:readline" e.g. in Konqueror for readline usage. */
554:
555: /**
556: * Readline match callback for long command name completion.
557: * STATE = 0 -> different text from previous one.
558: * Return next match or NULL if no matches.
559: */
560: static char *DebugUI_MatchCommand(const char *text, int state)
561: {
562: static int i, len;
563: const char *name;
564:
565: if (!state)
566: {
567: /* first match */
568: len = strlen(text);
569: i = 0;
570: }
571: /* next match */
572: while (i < debugCommands)
573: {
574: name = debugCommand[i].sLongName;
575: if (debugCommand[i++].pFunction &&
576: strncmp(name, text, len) == 0)
577: return (strdup(name));
578: }
579: return NULL;
580: }
581:
582:
583: #if HAVE_LIBREADLINE
584: /**
585: * Readline match callback returning last result.
586: */
587: static char *DebugUI_MatchLast(const char *text, int state)
588: {
589: if (state)
590: return NULL;
591: return strdup(lastResult);
592: }
593:
594: /**
595: * Readline completion callback. Returns matches.
596: */
597: static char **DebugUI_Completion(const char *text, int a, int b)
598: {
599: int i, cmd, quotes, end, start = 0;
600: char *str, buf[32];
601: size_t len;
602:
603: /* check where's the first word (ignore white space) */
604: while (start < rl_point && isspace(rl_line_buffer[start]))
605: start++;
606: end = start;
607: while (end < rl_point && !isspace(rl_line_buffer[end]))
608: end++;
609:
610: if (end >= rl_point)
611: /* first word on line */
612: return rl_completion_matches(text, DebugUI_MatchCommand);
613:
614: /* complete '$' with last result? */
615: if (lastResult[0] && rl_line_buffer[rl_point-1] == '$')
616: return rl_completion_matches(text, DebugUI_MatchLast);
617:
618: /* check which command args are to be completed */
619: len = end - start;
620: if (len >= sizeof(buf))
621: len = sizeof(buf)-1;
622: memcpy(buf, &(rl_line_buffer[start]), len);
623: buf[len] = '\0';
624:
625: /* expression completion needed (= open quote)? */
626: str = strchr(&(rl_line_buffer[end]), '"');
627: quotes = 0;
628: while (str)
629: {
630: quotes++;
631: str = strchr(str+1, '"');
632: }
633: if (quotes & 1)
634: {
635: if (DebugUI_IsForDsp(buf))
636: return rl_completion_matches(text, Symbols_MatchDspAddress);
637: return rl_completion_matches(text, Symbols_MatchCpuAddress);
638: }
639:
640: /* do command argument completion */
641: cmd = -1;
642: for (i = 0; i < debugCommands; i++)
643: {
644: if (!debugCommand[i].pFunction)
645: continue;
646: if (!strcmp(buf, debugCommand[i].sShortName) ||
647: !strcmp(buf, debugCommand[i].sLongName))
648: {
649: cmd = i;
650: break;
651: }
652: }
653: if (cmd < 0)
654: {
655: rl_attempted_completion_over = true;
656: return NULL;
657: }
658: if (debugCommand[cmd].pMatch)
659: return rl_completion_matches(text, debugCommand[cmd].pMatch);
660: else
661: return rl_completion_matches(text, rl_filename_completion_function);
662: }
663:
664:
665: /**
666: * Read a command line from the keyboard and return a pointer to the string.
667: * Only string returned by this function can be given for it as argument!
668: * The string will be stored into command history buffer.
669: * @return Pointer to the string which should be deallocated after
670: * use or given back to this function for re-use/history.
671: * Returns NULL when error occured.
672: */
673: static char *DebugUI_GetCommand(char *input)
674: {
675: /* Allow conditional parsing of the ~/.inputrc file. */
676: rl_readline_name = "Hatari";
677:
678: /* Tell the completer that we want a crack first. */
679: rl_attempted_completion_function = DebugUI_Completion;
680:
681: if (input && *input)
682: {
683: HIST_ENTRY *hist = previous_history();
684: /* don't store successive duplicate entries */
685: if (!hist || !hist->line || strcmp(hist->line, input) != 0)
686: add_history(input);
687: free(input);
688: }
689:
690: return Str_Trim(readline("> "));
691: }
692:
693: #else /* !HAVE_LIBREADLINE */
694:
695: /**
696: * Read a command line from the keyboard and return a pointer to the string.
697: * Only string returned by this function can be given for it as argument!
698: * @return Pointer to the string which should be deallocated after
699: * use or given back to this function for re-use.
700: * Returns NULL when error occured.
701: */
702: static char *DebugUI_GetCommand(char *input)
703: {
704: fprintf(stderr, "> ");
705: if (!input)
706: {
707: input = malloc(256);
708: assert(input);
709: }
710: input[0] = '\0';
711: if (fgets(input, 256, stdin) == NULL)
712: {
713: free(input);
714: return NULL;
715: }
716: return Str_Trim(input);
717: }
718:
719: #endif /* !HAVE_LIBREADLINE */
720:
721:
722: static const dbgcommand_t uicommand[] =
723: {
724: { NULL, NULL, "Generic commands", NULL, NULL, NULL, false },
725: /* NULL as match function will complete file names */
726: { DebugUI_ChangeDir, NULL,
727: "cd", "",
728: "change directory",
729: "<directory>\n"
730: "\tChange Hatari work directory.",
731: false },
732: { DebugUI_Evaluate, Symbols_MatchCpuAddress,
733: "evaluate", "e",
734: "evaluate an expression",
735: "<expression>\n"
736: "\tEvaluate an expression and show the result. Expression can\n"
737: "\tinclude CPU register and symbol names, those are replaced\n"
738: "\tby their values. Supported operators in expressions are,\n"
739: "\tin the decending order of precedence:\n"
740: "\t\t(), +, -, ~, *, /, +, -, >>, <<, ^, &, |\n"
741: "\tParenthesis will fetch a _long_ value from the address\n"
742: "\tto what the value inside it evaluates to. Prefixes can be\n"
743: "\tused only in start of line or parenthesis.\n"
744: "\tFor example:\n"
745: "\t\t~%101 & $f0f0f ^ (d0 + 0x21)\n"
746: "\tResult value is shown as binary, decimal and hexadecimal.\n"
747: "\tAfter this, '$' will TAB-complete to last result value.",
748: true },
749: { DebugUI_Help, DebugUI_MatchCommand,
750: "help", "h",
751: "print help",
752: "[command]\n"
753: "\tPrint help text for available commands.",
754: false },
755: { DebugInfo_Command, DebugInfo_MatchInfo,
756: "info", "i",
757: "show machine/OS information",
758: "[subject [arg]]\n"
759: "\tPrint information on requested subject or list them if\n"
760: "\tno subject given.",
761: false },
762: { DebugInfo_Command, DebugInfo_MatchLock,
763: "lock", "",
764: "specify information to show on entering the debugger",
765: "[subject [args]]\n"
766: "\tLock what information should be shown every time debugger\n"
767: "\tis entered, or list available options if no subject's given.",
768: false },
769: { DebugUI_SetLogFile, NULL,
770: "logfile", "f",
771: "open or close log file",
772: "[filename]\n"
773: "\tOpen log file, no argument closes the log file. Output of\n"
774: "\tregister & memory dumps and disassembly will be written to it.",
775: false },
776: { DebugUI_CommandsFromFile, NULL,
777: "parse", "p",
778: "get debugger commands from file",
779: "[filename]\n"
780: "\tRead debugger commands from given file and do them.",
781: false },
782: { DebugUI_SetOptions, NULL /* Opt_MatchOption */,
783: "setopt", "o",
784: "set Hatari command line and debugger options",
785: "[<bin|dec|hex>|<command line options>]\n"
786: "\tSet Hatari options. For example to enable exception catching,\n"
787: "\tuse following command line option: 'setopt --debug'. Special\n"
788: "\t'bin', 'dec' and 'hex' arguments change the default number base\n"
789: "\tused in debugger.",
790: false },
791: { DebugUI_SetTracing, Log_MatchTrace,
792: "trace", "t",
793: "select Hatari tracing settings",
794: "[set1,set2...]\n"
795: "\tSelect Hatari tracing settings. 'help' shows all the available\n"
796: "\tsettings. For example, to enable CPU disassembly and VBL\n"
797: "\ttracing, use:\n\t\ttrace cpu_disasm",
798: false },
799: { DebugUI_QuitEmu, NULL,
800: "quit", "q",
801: "quit emulator",
802: "\n"
803: "\tLeave debugger and quit emulator.",
804: false }
805: };
806:
807:
808: /**
809: * Debugger user interface initialization.
810: */
811: void DebugUI_Init(void)
812: {
813: const dbgcommand_t *cpucmd;
814: int cpucmds;
815:
816: if(!(debugOutput)){
817: debugOutput = stderr;
818: }
819:
820: /* already intialized? */
821: if (debugCommands)
822: return;
823:
824: /* if you want disassembly or memdumping to start/continue from
825: * specific address, you can set them in these functions.
826: */
827: // dspcmds = DebugDsp_Init(&dspcmd);
828: cpucmds = DebugCpu_Init(&cpucmd);
829:
830: /* on first time copy the command structures to a single table */
831: debugCommands = ARRAYSIZE(uicommand);
832: debugCommand = malloc(sizeof(dbgcommand_t) * (cpucmds + debugCommands));
833: assert(debugCommand);
834:
835: memcpy(debugCommand, uicommand, sizeof(dbgcommand_t) * debugCommands);
836: memcpy(&debugCommand[debugCommands], cpucmd, sizeof(dbgcommand_t) * cpucmds);
837: debugCommands += cpucmds;
838: // memcpy(&debugCommand[debugCommands], dspcmd, sizeof(dbgcommand_t) * dspcmds);
839: // debugCommands += dspcmds;
840:
841: if (parseFileName)
842: DebugUI_ParseFile(parseFileName);
843: }
844:
845:
846: /**
847: * Set debugger commands file during Hatari startup before things
848: * needed by the debugger are initialized so that it can be parsed
849: * when debugger itself gets initialized.
850: * Return true if file exists, false otherwise.
851: */
852: bool DebugUI_SetParseFile(const char *path)
853: {
854: if (File_Exists(path))
855: {
856: parseFileName = path;
857: return true;
858: }
859: //fprintf(stderr, "ERROR: debugger input file '%s' missing.\n", path);
860: return false;
861: }
862:
863:
864: /**
865: * Debugger user interface main function.
866: */
867: void DebugUI(void)
868: {
869: int cmdret, alertLevel;
870: char *expCmd, *psCmd = NULL;
871: static const char *welcome =
872: "\n----------------------------------------------------------------------"
873: "\nYou have entered debug mode. Type c to continue emulation, h for help.\n";
874:
875: if (bInFullScreen)
876: Screen_ReturnFromFullScreen();
877:
878: DebugUI_Init();
879:
880: if (welcome)
881: {
882: fputs(welcome, stderr);
883: welcome = NULL;
884: }
885: DebugCpu_InitSession();
886: // DebugDsp_InitSession();
887: DebugInfo_ShowSessionInfo();
888:
889: /* override paused message so that user knows to look into console
890: * on how to continue in case he invoked the debugger by accident.
891: */
892: Statusbar_AddMessage("M68K Console Debugger", 100);
893: Statusbar_Update(sdlscrn);
894:
895: /* disable normal GUI alerts while on console */
896: alertLevel = Log_SetAlertLevel(LOG_FATAL);
897:
898: cmdret = DEBUGGER_CMDDONE;
899: do
900: {
901: /* Read command from the keyboard and give previous
902: * command for freeing / adding to history
903: */
904: psCmd = DebugUI_GetCommand(psCmd);
905: if (!psCmd)
906: break;
907:
908: /* returns new string if expressions needed expanding! */
909: if (!(expCmd = DebugUI_EvaluateExpressions(psCmd)))
910: continue;
911:
912: /* Parse and execute the command string */
913: cmdret = DebugUI_ParseCommand(expCmd);
914: if (expCmd != psCmd)
915: free(expCmd);
916: }
917: while (cmdret != DEBUGGER_END);
918:
919: /* free (and ignore) exit command */
920: if (psCmd) {
921: free(psCmd);
922: }
923:
924: Log_SetAlertLevel(alertLevel);
925: DebugUI_SetLogDefault();
926:
927: DebugCpu_SetDebugging();
928: }
929:
930:
931: /**
932: * Read debugger commands from a file.
933: * return false for error, true for success.
934: */
935: bool DebugUI_ParseFile(const char *path)
936: {
937: char *olddir, *dir, *cmd, *input, *expanded, *slash;
938: FILE *fp;
939:
940: fprintf(stderr, "Reading debugger commands from '%s'...\n", path);
941: if (!(fp = fopen(path, "r")))
942: {
943: perror("ERROR");
944: return false;
945: }
946:
947: /* change to directory where the debugger file resides */
948: olddir = NULL;
949: dir = strdup(path);
950: slash = strrchr(dir, PATHSEP);
951: if (slash)
952: {
953: olddir = malloc(FILENAME_MAX);
954: if (olddir)
955: {
956: if (!getcwd(olddir, FILENAME_MAX))
957: strcpy(olddir, ".");
958: }
959: *slash = '\0';
960: if (chdir(dir) != 0)
961: {
962: perror("ERROR");
963: if (olddir)
964: free(olddir);
965: free(dir);
966: return false;
967: }
968: fprintf(stderr, "Changed to input file dir '%s'.\n", dir);
969: }
970: free(dir);
971:
972: input = NULL;
973: for (;;)
974: {
975: if (!input)
976: {
977: input = malloc(256);
978: assert(input);
979: }
980: if (!fgets(input, 256, fp))
981: break;
982:
983: /* ignore empty and comment lines */
984: cmd = Str_Trim(input);
985: if (!*cmd || *cmd == '#')
986: continue;
987:
988: /* returns new string if input needed expanding! */
989: expanded = DebugUI_EvaluateExpressions(input);
990: if (!expanded)
991: continue;
992:
993: cmd = Str_Trim(expanded);
994: fprintf(stderr, "> %s\n", cmd);
995: DebugUI_ParseCommand(cmd);
996: if (expanded != input)
997: free(expanded);
998: }
999:
1000: free(input);
1001: if (olddir)
1002: {
1003: if (chdir(olddir) != 0)
1004: perror("ERROR");
1005: else
1006: fprintf(stderr, "Changed back to '%s' dir.\n", olddir);
1007: free(olddir);
1008: }
1009:
1010: DebugCpu_SetDebugging();
1011: // DebugDsp_SetDebugging();
1012: return true;
1013: }
1014:
1015:
1016: /**
1017: * Remote/parallel debugger usage API.
1018: * Return false for failed command, true for success.
1019: */
1020: bool DebugUI_RemoteParse(char *input)
1021: {
1022: int ret;
1023:
1024: DebugUI_Init();
1025:
1026: ret = DebugUI_ParseCommand(input);
1027:
1028: DebugCpu_SetDebugging();
1029: // DebugDsp_SetDebugging();
1030:
1031: return (ret == DEBUGGER_CMDDONE);
1032: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.