|
|
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.