--- hatari/src/debug/debugui.c 2019/04/09 08:48:37 1.1.1.1 +++ hatari/src/debug/debugui.c 2019/04/09 08:56:50 1.1.1.9 @@ -1,8 +1,8 @@ /* Hatari - debugui.c - This file is distributed under the GNU Public License, version 2 or at - your option any later version. Read the file gpl.txt for details. + This file is distributed under the GNU General Public License, version 2 + or at your option any later version. Read the file gpl.txt for details. debugui.c - this is the code for the mini-debugger. When the pause button is pressed, the emulator is (hopefully) halted and this little CLI can be used @@ -29,6 +29,7 @@ const char DebugUI_fileid[] = "Hatari de #include "m68000.h" #include "memorySnapShot.h" #include "options.h" +#include "reset.h" #include "screen.h" #include "statusbar.h" #include "str.h" @@ -37,12 +38,13 @@ const char DebugUI_fileid[] = "Hatari de #include "breakcond.h" #include "debugcpu.h" #include "debugdsp.h" +#include "68kDisass.h" #include "debugInfo.h" #include "debugui.h" #include "evaluate.h" +#include "history.h" #include "symbols.h" - -int bExceptionDebugging; +#include "vars.h" FILE *debugOutput; @@ -52,8 +54,8 @@ static int debugCommands; /* stores last 'e' command result as hex, used for TAB-completion */ static char lastResult[10]; +/* parse debugger commands from here on init */ static const char *parseFileName; -static bool DebugUI_ParseFile(const char *path); /** @@ -82,7 +84,7 @@ void DebugUI_MemorySnapShot_Capture(cons if (File_Exists(filename)) { /* and parse back the saved breakpoints */ - DebugUI_ParseFile(filename); + DebugUI_ParseFile(filename, true); } } free(filename); @@ -111,17 +113,25 @@ static void DebugUI_SetLogDefault(void) */ static int DebugUI_SetLogFile(int nArgc, char *psArgs[]) { - File_Close(debugOutput); - debugOutput = NULL; + if (debugOutput != stderr) + { + fprintf(stderr, "Debug log closed.\n"); + File_Close(debugOutput); + } + debugOutput = stderr; if (nArgc > 1) - debugOutput = File_Open(psArgs[1], "w"); - - if (debugOutput) - fprintf(stderr, "Debug log '%s' opened.\n", psArgs[1]); - else - debugOutput = stderr; - + { + if ((debugOutput = File_Open(psArgs[1], "w"))) + { + fprintf(stderr, "Debug log '%s' opened.\n", psArgs[1]); + } + else + { + fprintf(stderr, "Debug log '%s' opening FAILED.\n", psArgs[1]); + debugOutput = stderr; + } + } return DEBUGGER_CMDDONE; } @@ -167,8 +177,7 @@ static int DebugUI_Evaluate(int nArgc, c if (nArgc < 2) { - DebugUI_PrintCmdHelp(psArgs[0]); - return DEBUGGER_CMDDONE; + return DebugUI_PrintCmdHelp(psArgs[0]); } errstr = Eval_Expression(expression, &result, &offset, false); @@ -188,38 +197,46 @@ static int DebugUI_Evaluate(int nArgc, c */ static bool DebugUI_IsForDsp(const char *cmd) { - return ((cmd[0] == 'd' && cmd[1] && !cmd[2]) - || strncmp(cmd, "dsp", 3) == 0); + return ((cmd[0] == 'd' && isalpha((unsigned char)cmd[1]) + && !isalpha((unsigned char)cmd[2])) + || strncmp(cmd, "dsp", 3) == 0); } /** - * Evaluate everything include within "" and replace them with the result. - * String given as an argument needs to be allocated, it may be freed and - * re-allocated if it needs to be expanded. Caller needs to free the returned - * string if it's not NULL. + * Evaluate everything include within single or double quotes ("" or '') + * and replace them with the result. + * Caller needs to free the returned string separately. * - * Return string with expressions expanded or NULL for an error. + * Return new string with expressions (potentially) expanded, or + * NULL when there's an error in the expression. */ -static char *DebugUI_EvaluateExpressions(char *input) +static char *DebugUI_EvaluateExpressions(const char *initial) { int offset, count, diff, inputlen; - char *end, *start; + char *end, *start, *input; const char *errstr; char valuestr[12]; Uint32 value; bool fordsp; /* input is split later on, need to save len here */ + input = strdup(initial); + if (!input) + { + perror("ERROR: Input string alloc failed\n"); + return NULL; + } fordsp = DebugUI_IsForDsp(input); inputlen = strlen(input); start = input; - - while ((start = strchr(start, '"'))) + + while ((count = strcspn(start, "\"'")) && *(start+count)) { - end = strchr(start+1, '"'); + start += count; + end = strchr(start+1, *start); if (!end) { - fprintf(stderr, "ERROR: matching '\"' missing from '%s'!\n", start); + fprintf(stderr, "ERROR: matching '%c' missing from '%s'!\n", *start, start); free(input); return NULL; } @@ -234,16 +251,16 @@ static char *DebugUI_EvaluateExpressions *end = '\0'; errstr = Eval_Expression(start+1, &value, &offset, fordsp); if (errstr) { - *end = '"'; + *end = *start; /* restore expression mark */ fprintf(stderr, "Expression ERROR:\n'%s'\n%*c-%s\n", input, (int)(start-input)+offset+3, '^', errstr); free(input); return NULL; } end++; - + count = sprintf(valuestr, "$%x", value); - fprintf(stderr, "- \"%s\" -> %s\n", start+1, valuestr); + fprintf(stderr, "- '%s' -> %s\n", start+1, valuestr); diff = end-start; if (count < diff) @@ -318,12 +335,11 @@ static int DebugUI_SetOptions(int argc, if (argc < 2) { - DebugUI_PrintCmdHelp(argv[0]); - return DEBUGGER_CMDDONE; + return DebugUI_PrintCmdHelp(argv[0]); } arg = argv[1]; - - for (i = 0; i < ARRAYSIZE(bases); i++) + + for (i = 0; i < ARRAY_SIZE(bases); i++) { if (strcasecmp(bases[i].name, arg) == 0) { @@ -344,7 +360,7 @@ static int DebugUI_SetOptions(int argc, current = ConfigureParams; /* Parse and apply options */ - if (Opt_ParseParameters(argc, (const char**)argv)) + if (Opt_ParseParameters(argc, (const char * const *)argv)) { ConfigureParams.Screen.bFullScreen = false; Change_CopyChangedParamsToConfiguration(¤t, &ConfigureParams, false); @@ -366,8 +382,7 @@ static int DebugUI_SetTracing(int argc, const char *errstr; if (argc != 2) { - DebugUI_PrintCmdHelp(argv[0]); - return DEBUGGER_CMDDONE; + return DebugUI_PrintCmdHelp(argv[0]); } errstr = Log_SetTraceOptions(argv[1]); if (errstr && errstr[0]) @@ -388,44 +403,58 @@ static int DebugUI_ChangeDir(int argc, c return DEBUGGER_CMDDONE; perror("ERROR"); } - DebugUI_PrintCmdHelp(argv[0]); - return DEBUGGER_CMDDONE; + return DebugUI_PrintCmdHelp(argv[0]); +} + +/** + * Command: Rename file + */ +static int DebugUI_Rename(int argc, char *argv[]) +{ + if (argc == 3) + { + if (rename(argv[1], argv[2]) == 0) + return DEBUGGER_CMDDONE; + perror("ERROR"); + } + return DebugUI_PrintCmdHelp(argv[0]); } /** - * Command: Read debugger commands from a file + * Command: Reset emulation */ -static int DebugUI_CommandsFromFile(int argc, char *argv[]) +static char *DebugUI_MatchReset(const char *text, int state) { - if (argc == 2) - DebugUI_ParseFile(argv[1]); + static const char* types[] = { "cold", "hard", "soft", "warm" }; + return DebugUI_MatchHelper(types, ARRAY_SIZE(types), text, state); +} +static int DebugUI_Reset(int argc, char *argv[]) +{ + if (argc != 2) + return DebugUI_PrintCmdHelp(argv[0]); + + if (strcmp(argv[1], "soft") == 0 || strcmp(argv[1], "warm") == 0) + Reset_Warm(); + else if (strcmp(argv[1], "cold") == 0 || strcmp(argv[1], "hard") == 0) + Reset_Cold(); else - DebugUI_PrintCmdHelp(argv[0]); - return DEBUGGER_CMDDONE; + return DebugUI_PrintCmdHelp(argv[0]); + return DEBUGGER_END; } /** - * Command: Execute a system command + * Command: Read debugger commands from a file */ -#if ENABLE_SYSTEM_DEBUG_CALL /* Disabled by default - could be a security risk? */ -static int DebugUI_Exec(int argc, char *argv[]) +static int DebugUI_CommandsFromFile(int argc, char *argv[]) { if (argc == 2) - { - int ret = system(argv[1]); - if (ret) - { - /* error -> show return code */ - fprintf(stderr, "Error code = %d\n", ret); - } - } + DebugUI_ParseFile(argv[1], true); else DebugUI_PrintCmdHelp(argv[0]); return DEBUGGER_CMDDONE; } -#endif /** @@ -433,8 +462,18 @@ static int DebugUI_Exec(int argc, char * */ static int DebugUI_QuitEmu(int nArgc, char *psArgv[]) { - bQuitProgram = true; - M68000_SetSpecial(SPCFLAG_BRK); /* Assure that CPU core shuts down */ + int exitval; + + if (nArgc > 2) + return DebugUI_PrintCmdHelp(psArgv[0]); + + if (nArgc == 2) + exitval = atoi(psArgv[1]); + else + exitval = 0; + + ConfigureParams.Log.bConfirmQuit = false; + Main_RequestQuit(exitval); return DEBUGGER_END; } @@ -442,7 +481,7 @@ static int DebugUI_QuitEmu(int nArgc, ch /** * Print help text for one command */ -void DebugUI_PrintCmdHelp(const char *psCmd) +int DebugUI_PrintCmdHelp(const char *psCmd) { dbgcommand_t *cmd; int i; @@ -473,11 +512,12 @@ void DebugUI_PrintCmdHelp(const char *ps fprintf(stderr, "Usage: %s %s\n", bShort ? cmd->sShortName : cmd->sLongName, cmd->sUsage); - return; + return DEBUGGER_CMDDONE; } } fprintf(stderr, "Unknown command '%s'\n", psCmd); + return DEBUGGER_CMDDONE; } @@ -490,8 +530,7 @@ static int DebugUI_Help(int nArgc, char if (nArgc > 1) { - DebugUI_PrintCmdHelp(psArgs[1]); - return DEBUGGER_CMDDONE; + return DebugUI_PrintCmdHelp(psArgs[1]); } for (i = 0; i < debugCommands; i++) @@ -526,14 +565,15 @@ static int DebugUI_Help(int nArgc, char /** * Parse debug command and execute it. */ -static int DebugUI_ParseCommand(char *input) +static int DebugUI_ParseCommand(const char *input_orig) { - char *psArgs[64]; + char *psArgs[64], *input; const char *delim; static char sLastCmd[80] = { '\0' }; int nArgc, cmd = -1; int i, retval; + input = strdup(input_orig); psArgs[0] = strtok(input, " \t"); if (psArgs[0] == NULL) @@ -541,7 +581,10 @@ static int DebugUI_ParseCommand(char *in if (strlen(sLastCmd) > 0) psArgs[0] = sLastCmd; else + { + free(input); return DEBUGGER_CMDDONE; + } } /* Search the command ... */ @@ -561,6 +604,7 @@ static int DebugUI_ParseCommand(char *in fprintf(stderr, "Command '%s' not found.\n" "Use 'help' to view a list of available commands.\n", psArgs[0]); + free(input); return DEBUGGER_CMDDONE; } @@ -570,25 +614,24 @@ static int DebugUI_ParseCommand(char *in delim = " \t"; /* Separate arguments and put the pointers into psArgs */ - for (nArgc = 1; nArgc < ARRAYSIZE(psArgs); nArgc++) + for (nArgc = 1; nArgc < ARRAY_SIZE(psArgs); nArgc++) { psArgs[nArgc] = strtok(NULL, delim); if (psArgs[nArgc] == NULL) break; } - if (!debugOutput) { - /* make sure also calls from control.c work */ - DebugUI_SetLogDefault(); - } - /* ... and execute the function */ retval = debugCommand[i].pFunction(nArgc, psArgs); /* Save commando string if it can be repeated */ if (retval == DEBUGGER_CMDCONT) - strncpy(sLastCmd, psArgs[0], sizeof(sLastCmd)); + { + if (psArgs[0] != sLastCmd) + strlcpy(sLastCmd, psArgs[0], sizeof(sLastCmd)); + } else sLastCmd[0] = '\0'; + free(input); return retval; } @@ -596,6 +639,29 @@ static int DebugUI_ParseCommand(char *in /* See "info:readline" e.g. in Konqueror for readline usage. */ /** + * Generic readline match callback helper. + * STATE = 0 -> different text from previous one. + * Return next match or NULL if no matches. + */ +char *DebugUI_MatchHelper(const char **strings, int items, const char *text, int state) +{ + static int i, len; + + if (!state) + { + /* first match */ + len = strlen(text); + i = 0; + } + /* next match */ + while (i < items) { + if (strncasecmp(strings[i++], text, len) == 0) + return (strdup(strings[i-1])); + } + return NULL; +} + +/** * Readline match callback for long command name completion. * STATE = 0 -> different text from previous one. * Return next match or NULL if no matches. @@ -644,10 +710,10 @@ static char **DebugUI_Completion(const c size_t len; /* check where's the first word (ignore white space) */ - while (start < rl_point && isspace(rl_line_buffer[start])) + while (start < rl_point && isspace((unsigned char)rl_line_buffer[start])) start++; end = start; - while (end < rl_point && !isspace(rl_line_buffer[end])) + while (end < rl_point && !isspace((unsigned char)rl_line_buffer[end])) end++; if (end >= rl_point) @@ -704,47 +770,68 @@ static char **DebugUI_Completion(const c return rl_completion_matches(text, rl_filename_completion_function); } +/** + * Add non-repeated command to readline history + * and free the given string + */ +static void DebugUI_FreeCommand(char *input) +{ + if (input && *input) + { + HIST_ENTRY *hist = history_get(history_length); + /* don't store duplicate successive entries */ + if (!hist || !hist->line || strcmp(hist->line, input) != 0) + { + add_history(input); + } + free(input); + } +} /** * Read a command line from the keyboard and return a pointer to the string. - * @return Pointer to the string which should be deallocated free() - * after use. Returns NULL when error occured. + * Only string returned by this function can be given for it as argument! + * The string will be stored into command history buffer. + * @return Pointer to the string which should be given back to this + * function or DebugUI_FreeCommand() for re-use/history. + * Returns NULL when error occurred. */ -static char *DebugUI_GetCommand(void) +static char *DebugUI_GetCommand(char *input) { - char *input; - /* Allow conditional parsing of the ~/.inputrc file. */ rl_readline_name = "Hatari"; /* Tell the completer that we want a crack first. */ rl_attempted_completion_function = DebugUI_Completion; - - input = readline("> "); - if (!input) - return NULL; - - input = Str_Trim(input); - if (input[0]) - add_history(input); - - return input; + DebugUI_FreeCommand(input); + return Str_Trim(readline("> ")); } #else /* !HAVE_LIBREADLINE */ /** + * Free Command input string + */ +static void DebugUI_FreeCommand(char *input) +{ + free(input); +} + +/** * Read a command line from the keyboard and return a pointer to the string. - * @return Pointer to the string which should be deallocated free() - * after use. Returns NULL when error occured. + * Only string returned by this function can be given for it as argument! + * @return Pointer to the string which should be given back to this + * function or DebugUI_FreeCommand() for re-use/freeing. + * Returns NULL when error occurred. */ -static char *DebugUI_GetCommand(void) +static char *DebugUI_GetCommand(char *input) { - char *input; fprintf(stderr, "> "); - input = malloc(256); if (!input) - return NULL; + { + input = malloc(256); + assert(input); + } input[0] = '\0'; if (fgets(input, 256, stdin) == NULL) { @@ -767,34 +854,39 @@ static const dbgcommand_t uicommand[] = "\n" "\tChange Hatari work directory.", false }, - { DebugUI_Evaluate, Symbols_MatchCpuAddress, + { DebugUI_Evaluate, Vars_MatchCpuVariable, "evaluate", "e", "evaluate an expression", "\n" "\tEvaluate an expression and show the result. Expression can\n" - "\tinclude CPU register and symbol names, those are replaced\n" - "\tby their values. Supported operators in expressions are,\n" - "\tin the decending order of precedence:\n" + "\tinclude CPU register & symbol and Hatari variable names.\n" + "\tThose are replaced by their values. Supported operators in\n" + "\texpressions are, in the decending order of precedence:\n" "\t\t(), +, -, ~, *, /, +, -, >>, <<, ^, &, |\n" + "\tParenthesis will fetch a _long_ value from the address\n" + "\tto what the value inside it evaluates to. Prefixes can be\n" + "\tused only in start of line or parenthesis.\n" "\tFor example:\n" - "\t\t((0x21 * 0x200) + (-5)) ^ (~%111 & $f0f0f0)\n" + "\t\t~%101 & $f0f0f ^ (d0 + 0x21)\n" "\tResult value is shown as binary, decimal and hexadecimal.\n" "\tAfter this, '$' will TAB-complete to last result value.", true }, -#if ENABLE_SYSTEM_DEBUG_CALL - { DebugUI_Exec, NULL, - "exec", "", - "execute a shell command", - "\n" - "\tRun the given command with the system shell.", - true }, -#endif { DebugUI_Help, DebugUI_MatchCommand, "help", "h", "print help", "[command]\n" "\tPrint help text for available commands.", false }, + { History_Parse, History_Match, + "history", "hi", + "show last CPU/DSP PC values & executed instructions", + "cpu|dsp|on|off| [limit]|save \n" + "\t'cpu' and 'dsp' enable instruction history tracking for just given\n" + "\tprocessor, 'on' tracks them both, 'off' will disable history.\n" + "\tOptional 'limit' will set how many past instructions are tracked.\n" + "\tGiving just count will show (at max) given number of last saved PC\n" + "\tvalues and instructions currently at corresponding RAM addresses.", + false }, { DebugInfo_Command, DebugInfo_MatchInfo, "info", "i", "show machine/OS information", @@ -804,10 +896,10 @@ static const dbgcommand_t uicommand[] = false }, { DebugInfo_Command, DebugInfo_MatchLock, "lock", "", - "lock info to show on entering debugger", + "specify information to show on entering the debugger", "[subject [args]]\n" - "\tLock requested information to be shown every time debugger\n" - "\tis entered or list available options if no subject's given.", + "\tLock what information should be shown every time debugger\n" + "\tis entered, or list available options if no subject's given.", false }, { DebugUI_SetLogFile, NULL, "logfile", "f", @@ -822,14 +914,24 @@ static const dbgcommand_t uicommand[] = "[filename]\n" "\tRead debugger commands from given file and do them.", false }, + { DebugUI_Rename, NULL, + "rename", "", + "rename given file", + "old new\n" + "\tRenames file with 'old' name to 'new'.", + false }, + { DebugUI_Reset, DebugUI_MatchReset, + "reset", "", + "reset emulation", + "\n", + false }, { DebugUI_SetOptions, Opt_MatchOption, "setopt", "o", "set Hatari command line and debugger options", - "[|]\n" - "\tSet Hatari options. For example to enable exception catching,\n" - "\tuse following command line option: 'setopt --debug'. Special\n" - "\t'bin', 'dec' and 'hex' arguments change the default number base\n" - "\tused in debugger.", + "[bin|dec|hex|]\n" + "\tSpecial 'bin', 'dec' and 'hex' arguments change the default\n" + "\tnumber base used in debugger. lists available command\n" + "\tline options, 'setopt --help' their descriptions.", false }, { DebugUI_DoMemorySnap, NULL, "stateload", "", @@ -847,14 +949,22 @@ static const dbgcommand_t uicommand[] = "trace", "t", "select Hatari tracing settings", "[set1,set2...]\n" - "\tSelect Hatari tracing settings. For example to enable CPU\n" - "\tdisassembly and VBL tracing, use: trace cpu_disasm,video_hbl", + "\tSelect Hatari tracing settings. 'help' shows all the available\n" + "\tsettings. For example, to enable CPU disassembly and VBL\n" + "\ttracing, use:\n\t\ttrace cpu_disasm,video_hbl", + false }, + { Vars_List, NULL, + "variables", "v", + "List builtin symbols / variables", + "\n" + "\tList Hatari debugger builtin symbols / variables and their values.\n" + "\tThey're accepted by breakpoints and evaluate command.", false }, { DebugUI_QuitEmu, NULL, "quit", "q", "quit emulator", - "\n" - "\tLeave debugger and quit emulator.", + "[exit value]\n" + "\tLeave debugger and quit emulator with given exit value.", false } }; @@ -871,6 +981,9 @@ void DebugUI_Init(void) if (debugCommands) return; + if (!debugOutput) + DebugUI_SetLogDefault(); + /* if you want disassembly or memdumping to start/continue from * specific address, you can set them in these functions. */ @@ -878,7 +991,7 @@ void DebugUI_Init(void) cpucmds = DebugCpu_Init(&cpucmd); /* on first time copy the command structures to a single table */ - debugCommands = ARRAYSIZE(uicommand); + debugCommands = ARRAY_SIZE(uicommand); debugCommand = malloc(sizeof(dbgcommand_t) * (dspcmds + cpucmds + debugCommands)); assert(debugCommand); @@ -889,12 +1002,14 @@ void DebugUI_Init(void) debugCommands += dspcmds; if (parseFileName) - DebugUI_ParseFile(parseFileName); + DebugUI_ParseFile(parseFileName, true); } /** - * Set debugger commands file. + * Set debugger commands file during Hatari startup before things + * needed by the debugger are initialized so that it can be parsed + * when debugger itself gets initialized. * Return true if file exists, false otherwise. */ bool DebugUI_SetParseFile(const char *path) @@ -912,17 +1027,34 @@ bool DebugUI_SetParseFile(const char *pa /** * Debugger user interface main function. */ -void DebugUI(void) +void DebugUI(debug_reason_t reason) { int cmdret, alertLevel; - char *psCmd; + char *expCmd, *psCmd = NULL; static const char *welcome = "\n----------------------------------------------------------------------" "\nYou have entered debug mode. Type c to continue emulation, h for help.\n"; - + static bool recursing; + + if (recursing) + { + fprintf(stderr, "WARNING: recursive call to DebugUI (through profiler debug option?)!\n"); + recursing = false; + return; + } + recursing = true; + + History_Mark(reason); + if (bInFullScreen) Screen_ReturnFromFullScreen(); + /* Make sure mouse isn't grabbed regardless of where + * this is invoked from. E.g. returning from fullscreen + * enables grab if that was enabled on windowed mode. + */ + SDL_WM_GrabInput(SDL_GRAB_OFF); + DebugUI_Init(); if (welcome) @@ -932,13 +1064,14 @@ void DebugUI(void) } DebugCpu_InitSession(); DebugDsp_InitSession(); + Symbols_LoadCurrentProgram(); DebugInfo_ShowSessionInfo(); /* override paused message so that user knows to look into console * on how to continue in case he invoked the debugger by accident. */ Statusbar_AddMessage("Console Debugger", 100); - Statusbar_Update(sdlscrn); + Statusbar_Update(sdlscrn, true); /* disable normal GUI alerts while on console */ alertLevel = Log_SetAlertLevel(LOG_FATAL); @@ -946,36 +1079,43 @@ void DebugUI(void) cmdret = DEBUGGER_CMDDONE; do { - /* Read command from the keyboard */ - psCmd = DebugUI_GetCommand(); + /* Read command from the keyboard and give previous + * command for freeing / adding to history + */ + psCmd = DebugUI_GetCommand(psCmd); if (!psCmd) break; - /* expand all quoted expressions */ - if (!(psCmd = DebugUI_EvaluateExpressions(psCmd))) + /* returns new expression expanded string */ + if (!(expCmd = DebugUI_EvaluateExpressions(psCmd))) continue; /* Parse and execute the command string */ - cmdret = DebugUI_ParseCommand(psCmd); - free(psCmd); + cmdret = DebugUI_ParseCommand(expCmd); + free(expCmd); } while (cmdret != DEBUGGER_END); + /* free exit command */ + DebugUI_FreeCommand(psCmd); + Log_SetAlertLevel(alertLevel); - DebugUI_SetLogDefault(); DebugCpu_SetDebugging(); DebugDsp_SetDebugging(); + + recursing = false; } /** - * Read debugger commands from a file. - * return false for error, true for success. + * Read debugger commands from a file. If 'reinit' is set + * (as it normally should), reinitialize breakpoints etc. + * afterwards. return false for error, true for success. */ -static bool DebugUI_ParseFile(const char *path) +bool DebugUI_ParseFile(const char *path, bool reinit) { - char *dir, *cmd, *input, *slash; + char *olddir, *dir, *cmd, *input, *expanded, *slash; FILE *fp; fprintf(stderr, "Reading debugger commands from '%s'...\n", path); @@ -986,17 +1126,27 @@ static bool DebugUI_ParseFile(const char } /* change to directory where the debugger file resides */ + olddir = NULL; dir = strdup(path); slash = strrchr(dir, PATHSEP); if (slash) { + olddir = malloc(FILENAME_MAX); + if (olddir) + { + if (!getcwd(olddir, FILENAME_MAX)) + strcpy(olddir, "."); + } *slash = '\0'; - if (chdir(dir)) + if (chdir(dir) != 0) { perror("ERROR"); + free(olddir); free(dir); + fclose(fp); return false; } + fprintf(stderr, "Changed to input file dir '%s'.\n", dir); } free(dir); @@ -1011,40 +1161,91 @@ static bool DebugUI_ParseFile(const char if (!fgets(input, 256, fp)) break; - input = DebugUI_EvaluateExpressions(input); - if (!input) + /* ignore empty and comment lines */ + cmd = Str_Trim(input); + if (!*cmd || *cmd == '#') continue; - cmd = Str_Trim(input); - if (*cmd && *cmd != '#') - { - fprintf(stderr, "> %s\n", cmd); - DebugUI_ParseCommand(cmd); - } + /* returns new string if input needed expanding! */ + expanded = DebugUI_EvaluateExpressions(input); + if (!expanded) + continue; + + cmd = Str_Trim(expanded); + fprintf(stderr, "> %s\n", cmd); + DebugUI_ParseCommand(cmd); + free(expanded); } free(input); + fclose(fp); - DebugCpu_SetDebugging(); - DebugDsp_SetDebugging(); + if (olddir) + { + if (chdir(olddir) != 0) + perror("ERROR"); + else + fprintf(stderr, "Changed back to '%s' dir.\n", olddir); + free(olddir); + } + + if (reinit) + { + DebugCpu_SetDebugging(); + DebugDsp_SetDebugging(); + } return true; } /** - * Remote/parallel debugger usage API. + * Remote/parallel debugger line usage API. * Return false for failed command, true for success. */ -bool DebugUI_RemoteParse(char *input) +bool DebugUI_ParseLine(const char *input) { - int ret; + char *expanded; + int ret = 0; DebugUI_Init(); - - ret = DebugUI_ParseCommand(input); - DebugCpu_SetDebugging(); - DebugDsp_SetDebugging(); + /* returns new string if input needed expanding! */ + expanded = DebugUI_EvaluateExpressions(input); + if (expanded) + { + fprintf(stderr, "> %s\n", expanded); + ret = DebugUI_ParseCommand(expanded); + free(expanded); + DebugCpu_SetDebugging(); + DebugDsp_SetDebugging(); + } return (ret == DEBUGGER_CMDDONE); } + +/** + * Debugger invocation based on exception + */ +void DebugUI_Exceptions(int nr, long pc) +{ + static struct { + int flag; + const char *name; + } ex[] = { + { EXCEPT_BUS, "Bus error" }, /* 2 */ + { EXCEPT_ADDRESS, "Address error" }, /* 3 */ + { EXCEPT_ILLEGAL, "Illegal instruction" }, /* 4 */ + { EXCEPT_ZERODIV, "Div by zero" }, /* 5 */ + { EXCEPT_CHK, "CHK" }, /* 6 */ + { EXCEPT_TRAPV, "TRAPV" }, /* 7 */ + { EXCEPT_PRIVILEGE, "Privilege violation" }, /* 8 */ + { EXCEPT_TRACE, "Trace" } /* 9 */ + }; + nr -= 2; + if (nr < 0 || nr >= ARRAY_SIZE(ex)) + return; + if (!(ExceptionDebugMask & ex[nr].flag)) + return; + fprintf(stderr,"%s exception at 0x%lx!\n", ex[nr].name, pc); + DebugUI(REASON_CPU_EXCEPTION); +}