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