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