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