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