diff options
Diffstat (limited to 'src/emu/debug')
-rw-r--r-- | src/emu/debug/debugcmd.cpp | 173 | ||||
-rw-r--r-- | src/emu/debug/debugcmd.h | 5 | ||||
-rw-r--r-- | src/emu/debug/debugcon.cpp | 246 | ||||
-rw-r--r-- | src/emu/debug/debugcon.h | 18 | ||||
-rw-r--r-- | src/emu/debug/debugcpu.cpp | 127 | ||||
-rw-r--r-- | src/emu/debug/debugcpu.h | 21 | ||||
-rw-r--r-- | src/emu/debug/debughlp.cpp | 71 | ||||
-rw-r--r-- | src/emu/debug/debugvw.cpp | 4 | ||||
-rw-r--r-- | src/emu/debug/dvmemory.cpp | 67 | ||||
-rw-r--r-- | src/emu/debug/dvmemory.h | 1 | ||||
-rw-r--r-- | src/emu/debug/express.cpp | 16 | ||||
-rw-r--r-- | src/emu/debug/express.h | 9 | ||||
-rw-r--r-- | src/emu/debug/points.cpp | 195 | ||||
-rw-r--r-- | src/emu/debug/points.h | 6 |
14 files changed, 611 insertions, 348 deletions
diff --git a/src/emu/debug/debugcmd.cpp b/src/emu/debug/debugcmd.cpp index b8de0298432..781ce93e9a5 100644 --- a/src/emu/debug/debugcmd.cpp +++ b/src/emu/debug/debugcmd.cpp @@ -21,6 +21,7 @@ #include "debugger.h" #include "emuopts.h" +#include "fileio.h" #include "natkeyboard.h" #include "render.h" #include "screen.h" @@ -31,6 +32,7 @@ #include <algorithm> #include <cctype> #include <fstream> +#include <sstream> @@ -232,6 +234,9 @@ debugger_commands::debugger_commands(running_machine& machine, debugger_cpu& cpu m_console.register_command("gtime", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_go_time, this, _1)); m_console.register_command("gt", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_go_time, this, _1)); m_console.register_command("gp", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_go_privilege, this, _1)); + m_console.register_command("gbt", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_go_branch, this, true, _1)); + m_console.register_command("gbf", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_go_branch, this, false, _1)); + m_console.register_command("gni", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_go_next_instruction, this, _1)); m_console.register_command("next", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_next, this, _1)); m_console.register_command("n", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_next, this, _1)); m_console.register_command("focus", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_focus, this, _1)); @@ -240,6 +245,7 @@ debugger_commands::debugger_commands(running_machine& machine, debugger_cpu& cpu m_console.register_command("suspend", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_suspend, this, _1)); m_console.register_command("resume", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_resume, this, _1)); m_console.register_command("cpulist", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_cpulist, this, _1)); + m_console.register_command("time", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_time, this, _1)); m_console.register_command("comadd", CMDFLAG_NONE, 1, 2, std::bind(&debugger_commands::execute_comment_add, this, _1)); m_console.register_command("//", CMDFLAG_NONE, 1, 2, std::bind(&debugger_commands::execute_comment_add, this, _1)); @@ -943,53 +949,51 @@ void debugger_commands::execute_print(const std::vector<std::string> ¶ms) mini_printf - safe printf to a buffer -------------------------------------------------*/ -int debugger_commands::mini_printf(char *buffer, const char *format, int params, u64 *param) +bool debugger_commands::mini_printf(std::ostream &stream, std::string_view format, int params, u64 *param) { - const char *f = format; - char *p = buffer; + auto f = format.begin(); - /* parse the string looking for % signs */ - for (;;) + // parse the string looking for % signs + while (f != format.end()) { char c = *f++; - if (!c) break; - /* escape sequences */ + // escape sequences if (c == '\\') { + if (f == format.end()) break; c = *f++; - if (!c) break; switch (c) { - case '\\': *p++ = c; break; - case 'n': *p++ = '\n'; break; + case '\\': stream << c; break; + case 'n': stream << '\n'; break; default: break; } continue; } - /* formatting */ + // formatting else if (c == '%') { int width = 0; int zerofill = 0; - /* parse out the width */ - for (;;) + // parse out the width + while (f != format.end() && *f >= '0' && *f <= '9') { c = *f++; - if (!c || c < '0' || c > '9') break; if (c == '0' && width == 0) zerofill = 1; width = width * 10 + (c - '0'); } - if (!c) break; + if (f == format.end()) break; - /* get the format */ + // get the format + c = *f++; switch (c) { case '%': - *p++ = c; + stream << c; break; case 'X': @@ -997,13 +1001,13 @@ int debugger_commands::mini_printf(char *buffer, const char *format, int params, if (params == 0) { m_console.printf("Not enough parameters for format!\n"); - return 0; + return false; } if (u32(*param >> 32) != 0) - p += sprintf(p, zerofill ? "%0*X" : "%*X", (width <= 8) ? 1 : width - 8, u32(*param >> 32)); + util::stream_format(stream, zerofill ? "%0*X" : "%*X", (width <= 8) ? 1 : width - 8, u32(*param >> 32)); else if (width > 8) - p += sprintf(p, zerofill ? "%0*X" : "%*X", width - 8, 0); - p += sprintf(p, zerofill ? "%0*X" : "%*X", (width < 8) ? width : 8, u32(*param)); + util::stream_format(stream, zerofill ? "%0*X" : "%*X", width - 8, 0); + util::stream_format(stream, zerofill ? "%0*X" : "%*X", (width < 8) ? width : 8, u32(*param)); param++; params--; break; @@ -1013,23 +1017,23 @@ int debugger_commands::mini_printf(char *buffer, const char *format, int params, if (params == 0) { m_console.printf("Not enough parameters for format!\n"); - return 0; + return false; } if (u32(*param >> 60) != 0) { - p += sprintf(p, zerofill ? "%0*o" : "%*o", (width <= 20) ? 1 : width - 20, u32(*param >> 60)); - p += sprintf(p, "%0*o", 10, u32(BIT(*param, 30, 30))); + util::stream_format(stream, zerofill ? "%0*o" : "%*o", (width <= 20) ? 1 : width - 20, u32(*param >> 60)); + util::stream_format(stream, "%0*o", 10, u32(BIT(*param, 30, 30))); } else { if (width > 20) - p += sprintf(p, zerofill ? "%0*o" : "%*o", width - 20, 0); + util::stream_format(stream, zerofill ? "%0*o" : "%*o", width - 20, 0); if (u32(BIT(*param, 30, 30)) != 0) - p += sprintf(p, zerofill ? "%0*o" : "%*o", (width <= 10) ? 1 : width - 10, u32(BIT(*param, 30, 30))); + util::stream_format(stream, zerofill ? "%0*o" : "%*o", (width <= 10) ? 1 : width - 10, u32(BIT(*param, 30, 30))); else if (width > 10) - p += sprintf(p, zerofill ? "%0*o" : "%*o", width - 10, 0); + util::stream_format(stream, zerofill ? "%0*o" : "%*o", width - 10, 0); } - p += sprintf(p, zerofill ? "%0*o" : "%*o", (width < 10) ? width : 10, u32(BIT(*param, 0, 30))); + util::stream_format(stream, zerofill ? "%0*o" : "%*o", (width < 10) ? width : 10, u32(BIT(*param, 0, 30))); param++; params--; break; @@ -1039,9 +1043,9 @@ int debugger_commands::mini_printf(char *buffer, const char *format, int params, if (params == 0) { m_console.printf("Not enough parameters for format!\n"); - return 0; + return false; } - p += sprintf(p, zerofill ? "%0*d" : "%*d", width, u32(*param)); + util::stream_format(stream, zerofill ? "%0*d" : "%*d", width, u32(*param)); param++; params--; break; @@ -1050,9 +1054,9 @@ int debugger_commands::mini_printf(char *buffer, const char *format, int params, if (params == 0) { m_console.printf("Not enough parameters for format!\n"); - return 0; + return false; } - p += sprintf(p, "%c", char(*param)); + stream << char(*param); param++; params--; break; @@ -1060,14 +1064,12 @@ int debugger_commands::mini_printf(char *buffer, const char *format, int params, } } - /* normal stuff */ + // normal stuff else - *p++ = c; + stream << c; } - /* NULL-terminate and exit */ - *p = 0; - return 1; + return true; } @@ -1115,9 +1117,9 @@ void debugger_commands::execute_printf(const std::vector<std::string> ¶ms) return; /* then do a printf */ - char buffer[1024]; - if (mini_printf(buffer, params[0].c_str(), params.size() - 1, &values[1])) - m_console.printf("%s\n", buffer); + std::ostringstream buffer; + if (mini_printf(buffer, params[0], params.size() - 1, &values[1])) + m_console.printf("%s\n", std::move(buffer).str()); } @@ -1134,9 +1136,9 @@ void debugger_commands::execute_logerror(const std::vector<std::string> ¶ms) return; /* then do a printf */ - char buffer[1024]; - if (mini_printf(buffer, params[0].c_str(), params.size() - 1, &values[1])) - m_machine.logerror("%s", buffer); + std::ostringstream buffer; + if (mini_printf(buffer, params[0], params.size() - 1, &values[1])) + m_machine.logerror("%s", std::move(buffer).str()); } @@ -1153,9 +1155,9 @@ void debugger_commands::execute_tracelog(const std::vector<std::string> ¶ms) return; /* then do a printf */ - char buffer[1024]; - if (mini_printf(buffer, params[0].c_str(), params.size() - 1, &values[1])) - m_console.get_visible_cpu()->debug()->trace_printf("%s", buffer); + std::ostringstream buffer; + if (mini_printf(buffer, params[0], params.size() - 1, &values[1])) + m_console.get_visible_cpu()->debug()->trace_printf("%s", std::move(buffer).str().c_str()); } @@ -1189,9 +1191,9 @@ void debugger_commands::execute_tracesym(const std::vector<std::string> ¶ms) } // then do a printf - char buffer[1024]; - if (mini_printf(buffer, format.str().c_str(), params.size(), values)) - m_console.get_visible_cpu()->debug()->trace_printf("%s", buffer); + std::ostringstream buffer; + if (mini_printf(buffer, format.str(), params.size(), values)) + m_console.get_visible_cpu()->debug()->trace_printf("%s", std::move(buffer).str().c_str()); } @@ -1358,6 +1360,62 @@ void debugger_commands::execute_go_privilege(const std::vector<std::string> &par m_console.get_visible_cpu()->debug()->go_privilege((condition.is_empty()) ? "1" : condition.original_string()); } + +/*------------------------------------------------- + execute_go_branch - execute gbt or gbf command +-------------------------------------------------*/ + +void debugger_commands::execute_go_branch(bool sense, const std::vector<std::string> ¶ms) +{ + parsed_expression condition(m_console.visible_symtable()); + if (params.size() > 0 && !debug_command_parameter_expression(params[0], condition)) + return; + + m_console.get_visible_cpu()->debug()->go_branch(sense, (condition.is_empty()) ? "1" : condition.original_string()); +} + + +/*------------------------------------------------- + execute_go_next_instruction - execute gni command +-------------------------------------------------*/ + +void debugger_commands::execute_go_next_instruction(const std::vector<std::string> ¶ms) +{ + u64 count = 1; + static constexpr u64 MAX_COUNT = 512; + + // if we have a parameter, use it instead */ + if (params.size() > 0 && !validate_number_parameter(params[0], count)) + return; + if (count == 0) + return; + if (count > MAX_COUNT) + { + m_console.printf("Too many instructions (must be %d or fewer)\n", MAX_COUNT); + return; + } + + device_state_interface *stateintf; + device_t *cpu = m_machine.debugger().console().get_visible_cpu(); + if (!cpu->interface(stateintf)) + { + m_console.printf("No state interface available for %s\n", cpu->name()); + return; + } + u32 pc = stateintf->pcbase(); + + debug_disasm_buffer buffer(*cpu); + while (count-- != 0) + { + // disassemble the current instruction and get the length + u32 result = buffer.disassemble_info(pc); + pc = buffer.next_pc_wrap(pc, result & util::disasm_interface::LENGTHMASK); + } + + cpu->debug()->go(pc); +} + + /*------------------------------------------------- execute_next - execute the next command -------------------------------------------------*/ @@ -1620,6 +1678,15 @@ void debugger_commands::execute_cpulist(const std::vector<std::string> ¶ms) } } +//------------------------------------------------- +// execute_time - execute the time command +//------------------------------------------------- + +void debugger_commands::execute_time(const std::vector<std::string> ¶ms) +{ + m_console.printf("%s\n", m_machine.time().as_string()); +} + /*------------------------------------------------- execute_comment - add a comment to a line -------------------------------------------------*/ @@ -2191,8 +2258,7 @@ void debugger_commands::execute_rplist(const std::vector<std::string> ¶ms) void debugger_commands::execute_statesave(const std::vector<std::string> ¶ms) { - const std::string &filename(params[0]); - m_machine.immediate_save(filename.c_str()); + m_machine.immediate_save(params[0]); m_console.printf("State save attempted. Please refer to window message popup for results.\n"); } @@ -2203,8 +2269,7 @@ void debugger_commands::execute_statesave(const std::vector<std::string> ¶ms void debugger_commands::execute_stateload(const std::vector<std::string> ¶ms) { - const std::string &filename(params[0]); - m_machine.immediate_load(filename.c_str()); + m_machine.immediate_load(params[0]); // clear all PC & memory tracks for (device_t &device : device_enumerator(m_machine.root_device())) @@ -4259,7 +4324,7 @@ void debugger_commands::execute_unmount(const std::vector<std::string> ¶ms) void debugger_commands::execute_input(const std::vector<std::string> ¶ms) { - m_machine.natkeyboard().post_coded(params[0].c_str()); + m_machine.natkeyboard().post_coded(params[0]); } diff --git a/src/emu/debug/debugcmd.h b/src/emu/debug/debugcmd.h index fd0bcea673a..f93276d5d8d 100644 --- a/src/emu/debug/debugcmd.h +++ b/src/emu/debug/debugcmd.h @@ -99,7 +99,7 @@ private: u64 global_get(global_entry *global); void global_set(global_entry *global, u64 value); - int mini_printf(char *buffer, const char *format, int params, u64 *param); + bool mini_printf(std::ostream &stream, std::string_view format, int params, u64 *param); template <typename T> void execute_index_command(std::vector<std::string> const ¶ms, T &&apply, char const *unused_message); @@ -121,6 +121,8 @@ private: void execute_go_exception(const std::vector<std::string> ¶ms); void execute_go_time(const std::vector<std::string> ¶ms); void execute_go_privilege(const std::vector<std::string> ¶ms); + void execute_go_branch(bool sense, const std::vector<std::string> ¶ms); + void execute_go_next_instruction(const std::vector<std::string> ¶ms); void execute_focus(const std::vector<std::string> ¶ms); void execute_ignore(const std::vector<std::string> ¶ms); void execute_observe(const std::vector<std::string> ¶ms); @@ -128,6 +130,7 @@ private: void execute_resume(const std::vector<std::string> ¶ms); void execute_next(const std::vector<std::string> ¶ms); void execute_cpulist(const std::vector<std::string> ¶ms); + void execute_time(const std::vector<std::string> ¶ms); void execute_comment_add(const std::vector<std::string> ¶ms); void execute_comment_del(const std::vector<std::string> ¶ms); void execute_comment_save(const std::vector<std::string> ¶ms); diff --git a/src/emu/debug/debugcon.cpp b/src/emu/debug/debugcon.cpp index 32a4888c285..508c2377f54 100644 --- a/src/emu/debug/debugcon.cpp +++ b/src/emu/debug/debugcon.cpp @@ -16,6 +16,7 @@ #include "textbuf.h" #include "debugger.h" +#include "fileio.h" #include "corestr.h" @@ -86,10 +87,14 @@ debugger_console::debugger_console(running_machine &machine) } } +debugger_console::~debugger_console() +{ +} -/*------------------------------------------------- - exit - frees the console system --------------------------------------------------*/ + +//------------------------------------------------- +// exit - frees the console system +//------------------------------------------------- void debugger_console::exit() { @@ -114,41 +119,39 @@ void debugger_console::exit() inline bool debugger_console::debug_command::compare::operator()(const debug_command &a, const debug_command &b) const { - return core_stricmp(a.command.c_str(), b.command.c_str()) < 0; + return a.command < b.command; } inline bool debugger_console::debug_command::compare::operator()(const char *a, const debug_command &b) const { - return core_stricmp(a, b.command.c_str()) < 0; + return strcmp(a, b.command.c_str()) < 0; } inline bool debugger_console::debug_command::compare::operator()(const debug_command &a, const char *b) const { - return core_stricmp(a.command.c_str(), b) < 0; + return strcmp(a.command.c_str(), b) < 0; } -debugger_console::debug_command::debug_command(const char *_command, u32 _flags, int _minparams, int _maxparams, std::function<void (const std::vector<std::string> &)> &&_handler) +debugger_console::debug_command::debug_command(std::string_view _command, u32 _flags, int _minparams, int _maxparams, std::function<void (const std::vector<std::string> &)> &&_handler) : command(_command), params(nullptr), help(nullptr), handler(std::move(_handler)), flags(_flags), minparams(_minparams), maxparams(_maxparams) { } -/*------------------------------------------------------------ - execute_help_custom - execute the helpcustom command -------------------------------------------------------------*/ +//------------------------------------------------------------ +// execute_help_custom - execute the helpcustom command +//------------------------------------------------------------ void debugger_console::execute_help_custom(const std::vector<std::string> ¶ms) { - char buf[64]; for (const debug_command &cmd : m_commandlist) { if (cmd.flags & CMDFLAG_CUSTOM_HELP) { - snprintf(buf, 63, "%s help", cmd.command.c_str()); - buf[63] = 0; - char *temp_params[1] = { buf }; - internal_execute_command(true, 1, &temp_params[0]); + std::string buf = cmd.command + " help"; + std::vector<std::string_view> temp_params = { buf }; + internal_execute_command(true, temp_params); } } } @@ -202,159 +205,145 @@ symbol_table &debugger_console::visible_symtable() -/*------------------------------------------------- - trim_parameter - executes a - command --------------------------------------------------*/ +//------------------------------------------------- +// trim_parameter - trim spaces and quotes around +// a command parameter +//------------------------------------------------- -void debugger_console::trim_parameter(char **paramptr, bool keep_quotes) +std::string_view debugger_console::trim_parameter(std::string_view param, bool keep_quotes) { - char *param = *paramptr; - size_t len = strlen(param); + std::string_view::size_type len = param.length(); bool repeat; - /* loop until all adornments are gone */ + // loop until all adornments are gone do { repeat = false; - /* check for begin/end quotes */ + // check for begin/end quotes if (len >= 2 && param[0] == '"' && param[len - 1] == '"') { if (!keep_quotes) { - param[len - 1] = 0; - param++; + param = param.substr(1, len - 2); len -= 2; } } - /* check for start/end braces */ + // check for start/end braces else if (len >= 2 && param[0] == '{' && param[len - 1] == '}') { - param[len - 1] = 0; - param++; + param = param.substr(1, len - 2); len -= 2; repeat = true; } - /* check for leading spaces */ + // check for leading spaces else if (len >= 1 && param[0] == ' ') { - param++; + param.remove_prefix(1); len--; repeat = true; } - /* check for trailing spaces */ + // check for trailing spaces else if (len >= 1 && param[len - 1] == ' ') { - param[len - 1] = 0; + param.remove_suffix(1); len--; repeat = true; } } while (repeat); - *paramptr = param; + return param; } -/*------------------------------------------------- - internal_execute_command - executes a - command --------------------------------------------------*/ +//------------------------------------------------- +// internal_execute_command - executes a +// command +//------------------------------------------------- -CMDERR debugger_console::internal_execute_command(bool execute, int params, char **param) +CMDERR debugger_console::internal_execute_command(bool execute, std::vector<std::string_view> ¶ms) { // no params is an error - if (params == 0) + if (params.empty()) return CMDERR::none(); // the first parameter has the command and the real first parameter; separate them - char *p, *command; - for (p = param[0]; *p && isspace(u8(*p)); p++) { } - for (command = p; *p && !isspace(u8(*p)); p++) { } - if (*p != 0) - { - *p++ = 0; - for ( ; *p && isspace(u8(*p)); p++) { } - if (*p != 0) - param[0] = p; - else - params = 0; - } + std::string_view command_param = params[0]; + std::string_view::size_type pos = 0; + while (pos < command_param.length() && !isspace(u8(command_param[pos]))) + pos++; + const std::string command(strmakelower(command_param.substr(0, pos))); + while (pos < command_param.length() && isspace(u8(command_param[pos]))) + pos++; + if (pos == command_param.length() && params.size() == 1) + params.clear(); else - { - params = 0; - param[0] = nullptr; - } + params[0].remove_prefix(pos); // search the command list - size_t const len = strlen(command); - auto const found = m_commandlist.lower_bound(command); + auto const found = m_commandlist.lower_bound(command.c_str()); // error if not found - if ((m_commandlist.end() == found) || core_strnicmp(command, found->command.c_str(), len)) + if (m_commandlist.end() == found || std::string_view(command) != std::string_view(found->command).substr(0, command.length())) return CMDERR::unknown_command(0); - if (found->command.length() > len) + if (found->command.length() > command.length()) { auto const next = std::next(found); - if ((m_commandlist.end() != next) && !core_strnicmp(command, next->command.c_str(), len)) + if (m_commandlist.end() != next && std::string_view(command) == std::string_view(next->command).substr(0, command.length())) return CMDERR::ambiguous_command(0); } - // NUL-terminate and trim space around all the parameters - for (int i = 1; i < params; i++) - *param[i]++ = 0; - // now go back and trim quotes and braces and any spaces they reveal - for (int i = 0; i < params; i++) - trim_parameter(¶m[i], found->flags & CMDFLAG_KEEP_QUOTES); + for (std::string_view ¶m : params) + param = trim_parameter(param, found->flags & CMDFLAG_KEEP_QUOTES); // see if we have the right number of parameters - if (params < found->minparams) + if (params.size() < found->minparams) return CMDERR::not_enough_params(0); - if (params > found->maxparams) + if (params.size() > found->maxparams) return CMDERR::too_many_params(0); // execute the handler if (execute) { - std::vector<std::string> params_vec(param, param + params); + std::vector<std::string> params_vec(params.begin(), params.end()); found->handler(params_vec); } return CMDERR::none(); } -/*------------------------------------------------- - internal_parse_command - parses a command - and either executes or just validates it --------------------------------------------------*/ +//------------------------------------------------- +// internal_parse_command - parses a command +// and either executes or just validates it +//------------------------------------------------- -CMDERR debugger_console::internal_parse_command(const std::string &original_command, bool execute) +CMDERR debugger_console::internal_parse_command(std::string_view command, bool execute) { - char command[MAX_COMMAND_LENGTH], parens[MAX_COMMAND_LENGTH]; - char *params[MAX_COMMAND_PARAMS] = { nullptr }; - char *command_start; - char *p, c = 0; + std::string_view::size_type pos = 0; + std::string_view::size_type len = command.length(); - /* make a copy of the command */ - strcpy(command, original_command.c_str()); - - /* loop over all semicolon-separated stuff */ - for (p = command; *p != 0; ) + while (pos < len) { - int paramcount = 0, parendex = 0; + std::string parens; + std::vector<std::string_view> params; bool foundend = false, instring = false, isexpr = false; - /* find a semicolon or the end */ - for (params[paramcount++] = p; !foundend; p++) + // skip leading spaces + while (pos < len && isspace(u8(command[pos]))) + pos++; + std::string_view::size_type startpos = pos; + + // find a semicolon or the end + for (params.push_back(command.substr(pos)); !foundend && pos < len; pos++) { - c = *p; + char c = command[pos]; if (instring) { - if (c == '"' && p[-1] != '\\') + if (c == '"' && command[pos - 1] != '\\') instring = false; } else @@ -364,47 +353,42 @@ CMDERR debugger_console::internal_parse_command(const std::string &original_comm case '"': instring = true; break; case '(': case '[': - case '{': parens[parendex++] = c; break; - case ')': if (parendex == 0 || parens[--parendex] != '(') return CMDERR::unbalanced_parens(p - command); break; - case ']': if (parendex == 0 || parens[--parendex] != '[') return CMDERR::unbalanced_parens(p - command); break; - case '}': if (parendex == 0 || parens[--parendex] != '{') return CMDERR::unbalanced_parens(p - command); break; - case ',': if (parendex == 0) params[paramcount++] = p; break; - case ';': if (parendex == 0) foundend = true; break; - case '-': if (parendex == 0 && paramcount == 1 && p[1] == '-') isexpr = true; *p = c; break; - case '+': if (parendex == 0 && paramcount == 1 && p[1] == '+') isexpr = true; *p = c; break; - case '=': if (parendex == 0 && paramcount == 1) isexpr = true; *p = c; break; - case 0: foundend = true; break; - default: *p = c; break; + case '{': parens.push_back(c); break; + case ')': if (parens.empty() || parens.back() != '(') return CMDERR::unbalanced_parens(pos); parens.pop_back(); break; + case ']': if (parens.empty() || parens.back() != '[') return CMDERR::unbalanced_parens(pos); parens.pop_back(); break; + case '}': if (parens.empty() || parens.back() != '{') return CMDERR::unbalanced_parens(pos); parens.pop_back(); break; + case ',': if (parens.empty()) { params.back().remove_suffix(len - pos); params.push_back(command.substr(pos + 1)); } break; + case ';': if (parens.empty()) { params.back().remove_suffix(len - pos); foundend = true; } break; + case '-': if (parens.empty() && params.size() == 1 && pos > 0 && command[pos - 1] == '-') isexpr = true; break; + case '+': if (parens.empty() && params.size() == 1 && pos > 0 && command[pos - 1] == '+') isexpr = true; break; + case '=': if (parens.empty() && params.size() == 1) isexpr = true; break; + default: break; } } } - /* check for unbalanced parentheses or quotes */ + // check for unbalanced parentheses or quotes if (instring) - return CMDERR::unbalanced_quotes(p - command); - if (parendex != 0) - return CMDERR::unbalanced_parens(p - command); + return CMDERR::unbalanced_quotes(pos); + if (!parens.empty()) + return CMDERR::unbalanced_parens(pos); - /* NULL-terminate if we ended in a semicolon */ - p--; - if (c == ';') *p++ = 0; + // process the command + std::string_view command_or_expr = params[0]; - /* process the command */ - command_start = params[0]; - - /* allow for "do" commands */ - if (tolower(u8(command_start[0])) == 'd' && tolower(u8(command_start[1])) == 'o' && isspace(u8(command_start[2]))) + // allow for "do" commands + if (command_or_expr.length() > 3 && tolower(u8(command_or_expr[0])) == 'd' && tolower(u8(command_or_expr[1])) == 'o' && isspace(u8(command_or_expr[2]))) { isexpr = true; - command_start += 3; + command_or_expr.remove_prefix(3); } - /* if it smells like an assignment expression, treat it as such */ - if (isexpr && paramcount == 1) + // if it smells like an assignment expression, treat it as such + if (isexpr && params.size() == 1) { try { - parsed_expression(visible_symtable(), command_start).execute(); + parsed_expression(visible_symtable(), command_or_expr).execute(); } catch (expression_error &err) { @@ -413,29 +397,29 @@ CMDERR debugger_console::internal_parse_command(const std::string &original_comm } else { - const CMDERR result = internal_execute_command(execute, paramcount, ¶ms[0]); + const CMDERR result = internal_execute_command(execute, params); if (result.error_class() != CMDERR::NONE) - return CMDERR(result.error_class(), command_start - command); + return CMDERR(result.error_class(), startpos); } } return CMDERR::none(); } -/*------------------------------------------------- - execute_command - execute a command string --------------------------------------------------*/ +//------------------------------------------------- +// execute_command - execute a command string +//------------------------------------------------- -CMDERR debugger_console::execute_command(const std::string &command, bool echo) +CMDERR debugger_console::execute_command(std::string_view command, bool echo) { - /* echo if requested */ + // echo if requested if (echo) printf(">%s\n", command); - /* parse and execute */ + // parse and execute const CMDERR result = internal_parse_command(command, true); - /* display errors */ + // display errors if (result.error_class() != CMDERR::NONE) { if (!echo) @@ -444,7 +428,7 @@ CMDERR debugger_console::execute_command(const std::string &command, bool echo) printf("%s\n", cmderr_to_string(result)); } - /* update all views */ + // update all views if (echo) { m_machine.debug_view().update_all(); @@ -454,11 +438,11 @@ CMDERR debugger_console::execute_command(const std::string &command, bool echo) } -/*------------------------------------------------- - validate_command - validate a command string --------------------------------------------------*/ +//------------------------------------------------- +// validate_command - validate a command string +//------------------------------------------------- -CMDERR debugger_console::validate_command(const char *command) +CMDERR debugger_console::validate_command(std::string_view command) { return internal_parse_command(command, false); } @@ -468,7 +452,7 @@ CMDERR debugger_console::validate_command(const char *command) register_command - register a command handler -------------------------------------------------*/ -void debugger_console::register_command(const char *command, u32 flags, int minparams, int maxparams, std::function<void (const std::vector<std::string> &)> &&handler) +void debugger_console::register_command(std::string_view command, u32 flags, int minparams, int maxparams, std::function<void (const std::vector<std::string> &)> &&handler) { if (m_machine.phase() != machine_phase::INIT) throw emu_fatalerror("Can only call debugger_console::register_command() at init time!"); diff --git a/src/emu/debug/debugcon.h b/src/emu/debug/debugcon.h index 5271b0e5ae5..974a43e656a 100644 --- a/src/emu/debug/debugcon.h +++ b/src/emu/debug/debugcon.h @@ -23,8 +23,7 @@ CONSTANTS ***************************************************************************/ -#define MAX_COMMAND_LENGTH 4096 -#define MAX_COMMAND_PARAMS 128 +constexpr int MAX_COMMAND_PARAMS = 128; // flags for command parsing constexpr u32 CMDFLAG_NONE = 0x0000; @@ -79,11 +78,12 @@ class debugger_console { public: debugger_console(running_machine &machine); + ~debugger_console(); // command handling - CMDERR execute_command(const std::string &command, bool echo); - CMDERR validate_command(const char *command); - void register_command(const char *command, u32 flags, int minparams, int maxparams, std::function<void (const std::vector<std::string> &)> &&handler); + CMDERR execute_command(std::string_view command, bool echo); + CMDERR validate_command(std::string_view command); + void register_command(std::string_view command, u32 flags, int minparams, int maxparams, std::function<void (const std::vector<std::string> &)> &&handler); void source_script(const char *file); void process_source_file(); @@ -122,16 +122,16 @@ private: void execute_help_custom(const std::vector<std::string> ¶ms); void execute_condump(const std::vector<std::string>& params); - void trim_parameter(char **paramptr, bool keep_quotes); - CMDERR internal_execute_command(bool execute, int params, char **param); - CMDERR internal_parse_command(const std::string &original_command, bool execute); + [[nodiscard]] static std::string_view trim_parameter(std::string_view param, bool keep_quotes); + CMDERR internal_execute_command(bool execute, std::vector<std::string_view> ¶ms); + CMDERR internal_parse_command(std::string_view command, bool execute); void print_core(std::string_view text); // core text output void print_core_wrap(std::string_view text, int wrapcol); // core text output struct debug_command { - debug_command(const char *_command, u32 _flags, int _minparams, int _maxparams, std::function<void (const std::vector<std::string> &)> &&_handler); + debug_command(std::string_view _command, u32 _flags, int _minparams, int _maxparams, std::function<void (const std::vector<std::string> &)> &&_handler); struct compare { diff --git a/src/emu/debug/debugcpu.cpp b/src/emu/debug/debugcpu.cpp index 403d0b104ee..820d9e72688 100644 --- a/src/emu/debug/debugcpu.cpp +++ b/src/emu/debug/debugcpu.cpp @@ -19,6 +19,7 @@ #include "debugger.h" #include "emuopts.h" +#include "fileio.h" #include "screen.h" #include "uiinput.h" @@ -426,6 +427,7 @@ device_debug::device_debug(device_t &device) , m_instrhook(nullptr) , m_stepaddr(0) , m_stepsleft(0) + , m_delay_steps(0) , m_stopaddr(0) , m_stoptime(attotime::zero) , m_stopirq(0) @@ -458,14 +460,14 @@ device_debug::device_debug(device_t &device) // set up notifiers and clear the passthrough handlers if (m_memory) { int count = m_memory->max_space_count(); - m_phw.resize(count, nullptr); + m_phw.resize(count); for (int i=0; i != count; i++) if (m_memory->has_space(i)) { address_space &space = m_memory->space(i); - m_notifiers.push_back(space.add_change_notifier([this, &space](read_or_write mode) { reinstall(space, mode); })); + m_notifiers.emplace_back(space.add_change_notifier([this, &space] (read_or_write mode) { reinstall(space, mode); })); } else - m_notifiers.push_back(-1); + m_notifiers.emplace_back(); } // set up state-related stuff @@ -562,15 +564,14 @@ void device_debug::reinstall(address_space &space, read_or_write mode) int id = space.spacenum(); if (u32(mode) & u32(read_or_write::WRITE)) { - if (m_phw[id]) - m_phw[id]->remove(); + m_phw[id].remove(); if (m_track_mem) switch (space.data_width()) { - case 8: m_phw[id] = space.install_write_tap(0, space.addrmask(), "track_mem", [this, &space](offs_t address, u8 &data, u8 ) { write_tracking(space, address, data); }, m_phw[id]); break; - case 16: m_phw[id] = space.install_write_tap(0, space.addrmask(), "track_mem", [this, &space](offs_t address, u16 &data, u16) { write_tracking(space, address, data); }, m_phw[id]); break; - case 32: m_phw[id] = space.install_write_tap(0, space.addrmask(), "track_mem", [this, &space](offs_t address, u32 &data, u32) { write_tracking(space, address, data); }, m_phw[id]); break; - case 64: m_phw[id] = space.install_write_tap(0, space.addrmask(), "track_mem", [this, &space](offs_t address, u64 &data, u64) { write_tracking(space, address, data); }, m_phw[id]); break; + case 8: m_phw[id] = space.install_write_tap(0, space.addrmask(), "track_mem", [this, &space] (offs_t address, u8 &data, u8 ) { write_tracking(space, address, data); }, &m_phw[id]); break; + case 16: m_phw[id] = space.install_write_tap(0, space.addrmask(), "track_mem", [this, &space] (offs_t address, u16 &data, u16) { write_tracking(space, address, data); }, &m_phw[id]); break; + case 32: m_phw[id] = space.install_write_tap(0, space.addrmask(), "track_mem", [this, &space] (offs_t address, u32 &data, u32) { write_tracking(space, address, data); }, &m_phw[id]); break; + case 64: m_phw[id] = space.install_write_tap(0, space.addrmask(), "track_mem", [this, &space] (offs_t address, u64 &data, u64) { write_tracking(space, address, data); }, &m_phw[id]); break; } } } @@ -688,11 +689,11 @@ void device_debug::privilege_hook() if ((m_flags & DEBUG_FLAG_STOP_PRIVILEGE) != 0) { bool matched = true; - if (m_privilege_condition && !m_privilege_condition->is_empty()) + if (m_stop_condition && !m_stop_condition->is_empty()) { try { - matched = m_privilege_condition->execute(); + matched = m_stop_condition->execute(); } catch (expression_error &) { @@ -750,19 +751,53 @@ void device_debug::instruction_hook(offs_t curpc) // handle single stepping if (!debugcpu.is_stopped() && (m_flags & DEBUG_FLAG_STEPPING_ANY) != 0) { + bool do_step = true; + if ((m_flags & (DEBUG_FLAG_CALL_IN_PROGRESS | DEBUG_FLAG_TEST_IN_PROGRESS)) != 0) + { + if (curpc == m_stepaddr) + { + if ((~m_flags & (DEBUG_FLAG_TEST_IN_PROGRESS | DEBUG_FLAG_STEPPING_BRANCH_FALSE)) == 0) + { + debugcpu.set_execution_stopped(); + do_step = false; + } + + // reset the breakpoint + m_flags &= ~(DEBUG_FLAG_CALL_IN_PROGRESS | DEBUG_FLAG_TEST_IN_PROGRESS); + m_delay_steps = 0; + } + else if (m_delay_steps != 0) + { + m_delay_steps--; + if (m_delay_steps == 0) + { + // branch taken or subroutine entered (TODO: interrupt acknowledgment or interleaved multithreading can falsely trigger this) + if ((m_flags & DEBUG_FLAG_TEST_IN_PROGRESS) != 0 && (m_flags & (DEBUG_FLAG_STEPPING_OUT | DEBUG_FLAG_STEPPING_BRANCH_TRUE)) != 0) + { + debugcpu.set_execution_stopped(); + do_step = false; + } + if ((m_flags & DEBUG_FLAG_CALL_IN_PROGRESS) != 0) + do_step = false; + m_flags &= ~DEBUG_FLAG_TEST_IN_PROGRESS; + } + } + else + do_step = false; + } + // is this an actual step? - if (m_stepaddr == ~0 || curpc == m_stepaddr) + if (do_step) { - // decrement the count and reset the breakpoint + // decrement the count m_stepsleft--; - m_stepaddr = ~0; // if we hit 0, stop if (m_stepsleft == 0) debugcpu.set_execution_stopped(); // update every 100 steps until we are within 200 of the end - else if ((m_flags & DEBUG_FLAG_STEPPING_OUT) == 0 && (m_stepsleft < 200 || m_stepsleft % 100 == 0)) + else if ((m_flags & (DEBUG_FLAG_STEPPING_OUT | DEBUG_FLAG_STEPPING_BRANCH_TRUE | DEBUG_FLAG_STEPPING_BRANCH_FALSE)) == 0 && (m_stepsleft < 200 || m_stepsleft % 100 == 0)) { machine.debug_view().update_all(); machine.debug_view().flush_osd_updates(); @@ -849,7 +884,7 @@ void device_debug::instruction_hook(offs_t curpc) } // handle step out/over on the instruction we are about to execute - if ((m_flags & (DEBUG_FLAG_STEPPING_OVER | DEBUG_FLAG_STEPPING_OUT)) != 0 && m_stepaddr == ~0) + if ((m_flags & (DEBUG_FLAG_STEPPING_OVER | DEBUG_FLAG_STEPPING_OUT | DEBUG_FLAG_STEPPING_BRANCH)) != 0 && (m_flags & (DEBUG_FLAG_CALL_IN_PROGRESS | DEBUG_FLAG_TEST_IN_PROGRESS)) == 0) prepare_for_step_overout(m_state->pcbase()); // no longer in debugger code @@ -928,7 +963,7 @@ void device_debug::single_step(int numsteps) m_device.machine().rewind_capture(); m_stepsleft = numsteps; - m_stepaddr = ~0; + m_delay_steps = 0; m_flags |= DEBUG_FLAG_STEPPING; m_device.machine().debugger().cpu().set_execution_running(); } @@ -945,7 +980,7 @@ void device_debug::single_step_over(int numsteps) m_device.machine().rewind_capture(); m_stepsleft = numsteps; - m_stepaddr = ~0; + m_delay_steps = 0; m_flags |= DEBUG_FLAG_STEPPING_OVER; m_device.machine().debugger().cpu().set_execution_running(); } @@ -961,8 +996,9 @@ void device_debug::single_step_out() assert(m_exec != nullptr); m_device.machine().rewind_capture(); + m_stop_condition.reset(); m_stepsleft = 100; - m_stepaddr = ~0; + m_delay_steps = 0; m_flags |= DEBUG_FLAG_STEPPING_OUT; m_device.machine().debugger().cpu().set_execution_running(); } @@ -1060,13 +1096,30 @@ void device_debug::go_privilege(const char *condition) { assert(m_exec != nullptr); m_device.machine().rewind_invalidate(); - m_privilege_condition = std::make_unique<parsed_expression>(*m_symtable, condition); + m_stop_condition = std::make_unique<parsed_expression>(*m_symtable, condition); m_flags |= DEBUG_FLAG_STOP_PRIVILEGE; m_device.machine().debugger().cpu().set_execution_running(); } //------------------------------------------------- +// go_branch - execute until branch taken or +// not taken +//------------------------------------------------- + +void device_debug::go_branch(bool sense, const char *condition) +{ + assert(m_exec != nullptr); + m_device.machine().rewind_invalidate(); + m_stop_condition = std::make_unique<parsed_expression>(*m_symtable, condition); + m_stepsleft = 100; + m_delay_steps = 0; + m_flags |= sense ? DEBUG_FLAG_STEPPING_BRANCH_TRUE : DEBUG_FLAG_STEPPING_BRANCH_FALSE; + m_device.machine().debugger().cpu().set_execution_running(); +} + + +//------------------------------------------------- // halt_on_next_instruction_impl - halt in the // debugger on the next instruction, internal // implementation which is necessary solely due @@ -1385,7 +1438,6 @@ void device_debug::set_track_pc(bool value) // track_pc_visited - returns a boolean stating // if this PC has been visited or not. CRC32 is // done in this function on currently active CPU. -// TODO: Take a CPU context as input //------------------------------------------------- bool device_debug::track_pc_visited(offs_t pc) const @@ -1399,7 +1451,6 @@ bool device_debug::track_pc_visited(offs_t pc) const //------------------------------------------------- // set_track_pc_visited - set this pc as visited. -// TODO: Take a CPU context as input //------------------------------------------------- void device_debug::set_track_pc_visited(offs_t pc) @@ -1625,12 +1676,32 @@ void device_debug::prepare_for_step_overout(offs_t pc) // disassemble the current instruction and get the flags u32 dasmresult = buffer.disassemble_info(pc); + if ((dasmresult & util::disasm_interface::SUPPORTED) == 0) + return; + + bool step_out = (m_flags & DEBUG_FLAG_STEPPING_OUT) != 0 && (dasmresult & util::disasm_interface::STEP_OUT) != 0; + bool test_cond = (dasmresult & util::disasm_interface::STEP_COND) != 0 && ((m_flags & (DEBUG_FLAG_STEPPING_BRANCH_TRUE | DEBUG_FLAG_STEPPING_BRANCH_FALSE)) != 0 || step_out); + if (test_cond && m_stop_condition && !m_stop_condition->is_empty()) + { + try + { + test_cond = m_stop_condition->execute(); + } + catch (expression_error &) + { + test_cond = false; + } + } // if flags are supported and it's a call-style opcode, set a temp breakpoint after that instruction - if ((dasmresult & util::disasm_interface::SUPPORTED) != 0 && (dasmresult & util::disasm_interface::STEP_OVER) != 0) + // (TODO: this completely fails for subroutines that consume inline operands or use alternate returns) + if ((dasmresult & util::disasm_interface::STEP_OVER) != 0 || test_cond) { int extraskip = (dasmresult & util::disasm_interface::OVERINSTMASK) >> util::disasm_interface::OVERINSTSHIFT; pc = buffer.next_pc_wrap(pc, dasmresult & util::disasm_interface::LENGTHMASK); + m_delay_steps = extraskip + 1; + if (m_stepsleft < m_delay_steps) + m_stepsleft = m_delay_steps; // if we need to skip additional instructions, advance as requested while (extraskip-- > 0) { @@ -1638,13 +1709,17 @@ void device_debug::prepare_for_step_overout(offs_t pc) pc = buffer.next_pc_wrap(pc, result & util::disasm_interface::LENGTHMASK); } m_stepaddr = pc; + if ((dasmresult & util::disasm_interface::STEP_OVER) != 0) + m_flags |= DEBUG_FLAG_CALL_IN_PROGRESS; + if (test_cond) + m_flags |= DEBUG_FLAG_TEST_IN_PROGRESS; } // if we're stepping out and this isn't a step out instruction, reset the steps until stop to a high number - // (TODO: this doesn't work with conditional return instructions) - if ((m_flags & DEBUG_FLAG_STEPPING_OUT) != 0) + if ((m_flags & (DEBUG_FLAG_STEPPING_OUT | DEBUG_FLAG_STEPPING_BRANCH_TRUE | DEBUG_FLAG_STEPPING_BRANCH_FALSE)) != 0) { - if ((dasmresult & util::disasm_interface::SUPPORTED) != 0 && (dasmresult & util::disasm_interface::STEP_OUT) == 0) + // make sure to also reset the number of steps for conditionals that may be single-instruction loops + if (test_cond || !step_out) m_stepsleft = 100; else { diff --git a/src/emu/debug/debugcpu.h b/src/emu/debug/debugcpu.h index 9e3d23d8d7f..d9fc3f64eae 100644 --- a/src/emu/debug/debugcpu.h +++ b/src/emu/debug/debugcpu.h @@ -82,6 +82,7 @@ public: void go_exception(int exception, const char *condition); void go_milliseconds(u64 milliseconds); void go_privilege(const char *condition); + void go_branch(bool sense, const char *condition); void go_next_device(); template <typename Format, typename... Params> @@ -184,15 +185,16 @@ private: debug_instruction_hook_func m_instrhook; // per-instruction callback hook // stepping information - offs_t m_stepaddr; // step target address for DEBUG_FLAG_STEPPING_OVER + offs_t m_stepaddr; // step target address for DEBUG_FLAG_STEPPING_OVER or DEBUG_FLAG_STEPPING_BRANCH int m_stepsleft; // number of steps left until done + int m_delay_steps; // number of steps until target address check // execution information offs_t m_stopaddr; // stop address for DEBUG_FLAG_STOP_PC attotime m_stoptime; // stop time for DEBUG_FLAG_STOP_TIME int m_stopirq; // stop IRQ number for DEBUG_FLAG_STOP_INTERRUPT int m_stopexception; // stop exception number for DEBUG_FLAG_STOP_EXCEPTION - std::unique_ptr<parsed_expression> m_privilege_condition; // expression to evaluate on privilege change + std::unique_ptr<parsed_expression> m_stop_condition; // expression to evaluate on privilege change std::unique_ptr<parsed_expression> m_exception_condition; // expression to evaluate on exception hit attotime m_endexectime; // ending time of the current execution u64 m_total_cycles; // current total cycles @@ -239,10 +241,10 @@ private: // (0 = not tracing over, // ~0 = not currently tracing over) }; - std::unique_ptr<tracer> m_trace; // tracer state + std::unique_ptr<tracer> m_trace; // tracer state - std::vector<memory_passthrough_handler *> m_phw; // passthrough handler reference for each space, write mode - std::vector<int> m_notifiers; // notifiers for each space + std::vector<memory_passthrough_handler> m_phw; // passthrough handler reference for each space, write mode + std::vector<util::notifier_subscription> m_notifiers; // notifiers for each space // pc tracking class dasm_pc_tag @@ -322,12 +324,17 @@ private: static constexpr u32 DEBUG_FLAG_SUSPENDED = 0x00004000; // CPU currently suspended static constexpr u32 DEBUG_FLAG_LIVE_BP = 0x00010000; // there are live breakpoints for this CPU static constexpr u32 DEBUG_FLAG_STOP_PRIVILEGE = 0x00020000; // run until execution level changes + static constexpr u32 DEBUG_FLAG_STEPPING_BRANCH_TRUE = 0x0040000; // run until true branch + static constexpr u32 DEBUG_FLAG_STEPPING_BRANCH_FALSE = 0x0080000; // run until false branch + static constexpr u32 DEBUG_FLAG_CALL_IN_PROGRESS = 0x01000000; // CPU is in the middle of a subroutine call + static constexpr u32 DEBUG_FLAG_TEST_IN_PROGRESS = 0x02000000; // CPU is performing a conditional test and branch - static constexpr u32 DEBUG_FLAG_STEPPING_ANY = DEBUG_FLAG_STEPPING | DEBUG_FLAG_STEPPING_OVER | DEBUG_FLAG_STEPPING_OUT; + static constexpr u32 DEBUG_FLAG_STEPPING_BRANCH = DEBUG_FLAG_STEPPING_BRANCH_TRUE | DEBUG_FLAG_STEPPING_BRANCH_FALSE; + static constexpr u32 DEBUG_FLAG_STEPPING_ANY = DEBUG_FLAG_STEPPING | DEBUG_FLAG_STEPPING_OVER | DEBUG_FLAG_STEPPING_OUT | DEBUG_FLAG_STEPPING_BRANCH; static constexpr u32 DEBUG_FLAG_TRACING_ANY = DEBUG_FLAG_TRACING | DEBUG_FLAG_TRACING_OVER; static constexpr u32 DEBUG_FLAG_TRANSIENT = DEBUG_FLAG_STEPPING_ANY | DEBUG_FLAG_STOP_PC | DEBUG_FLAG_STOP_INTERRUPT | DEBUG_FLAG_STOP_EXCEPTION | DEBUG_FLAG_STOP_VBLANK | - DEBUG_FLAG_STOP_TIME | DEBUG_FLAG_STOP_PRIVILEGE; + DEBUG_FLAG_STOP_TIME | DEBUG_FLAG_STOP_PRIVILEGE | DEBUG_FLAG_CALL_IN_PROGRESS | DEBUG_FLAG_TEST_IN_PROGRESS; }; //************************************************************************** diff --git a/src/emu/debug/debughlp.cpp b/src/emu/debug/debughlp.cpp index 87bf8f0f18f..a62704c5472 100644 --- a/src/emu/debug/debughlp.cpp +++ b/src/emu/debug/debughlp.cpp @@ -81,6 +81,7 @@ const help_item f_static_help_list[] = " stateload[sl] <filename> -- load a state file for the current driver\n" " snap [<filename>] -- save a screen snapshot.\n" " source <filename> -- reads commands from <filename> and executes them one by one\n" + " time -- prints current machine time to the console\n" " cls -- clears the console text buffer\n" " quit -- exits MAME and the debugger\n" }, @@ -132,6 +133,9 @@ const help_item f_static_help_list[] = " o[ver] [<count>=1] -- single steps over <count> instructions (F10)\n" " out -- single steps until the current subroutine/exception handler is exited (Shift-F11)\n" " g[o] [<address>] -- resumes execution, sets temp breakpoint at <address> (F5)\n" + " gbf [<condition>] -- resumes execution until next false branch\n" + " gbt [<condition>] -- resumes execution until next true branch\n" + " gn[i] [<count>] -- resumes execution, sets temp breakpoint <count> instructions ahead\n" " ge[x] [<exception>[,<condition>]] -- resumes execution, setting temp breakpoint if <exception> is raised\n" " gi[nt] [<irqline>] -- resumes execution, setting temp breakpoint if <irqline> is taken (F7)\n" " gt[ime] <milliseconds> -- resumes execution until the given delay has elapsed\n" @@ -578,6 +582,13 @@ const help_item f_static_help_list[] = " Reads in debugger commands from break_and_trace.cmd and executes them.\n" }, { + "time", + "\n" + " time\n" + "\n" + "The time command prints the current machine time to the console.\n" + }, + { "quit", "\n" " quit\n" @@ -920,6 +931,66 @@ const help_item f_static_help_list[] = " Resume execution, stopping at address 1234 unless something else stops us first.\n" }, { + "gbf", + "\n" + " gbf [<condition>]\n" + "\n" + "The gbf command resumes execution of the current code. Control will not be returned to the " + "debugger until a conditional branch or skip instruction tests false and falls through to the " + "next instruction, or the instruction that follows its delay slot. The optional conditional " + "expression, if provided, will be evaluated at (not after) each conditional branch; execution " + "will not halt regardless of consequent program flow unless its result is true (non-zero).\n" + "\n" + "Examples:\n" + "\n" + "gbf\n" + " Resume execution until the next break/watchpoint or until the next false branch.\n" + "\n" + "gbf {pc != 1234}\n" + " Resume execution until the next false branch, disregarding the instruction at address 1234.\n" + }, + { + "gbt", + "\n" + " gbt [<condition>]\n" + "\n" + "The gbt command resumes execution of the current code. Control will not be returned to the " + "debugger until a conditional branch or skip instruction tests true and program flow transfers " + "to any instruction other than the next one following the delay slot (if any). The optional " + "conditional expression, if provided, will be evaluated at (not after) each conditional " + "branch; execution will not halt regardless of consequent program flow unless its result is " + "true (non-zero).\n" + "\n" + "Examples:\n" + "\n" + "gbt\n" + " Resume execution until the next break/watchpoint or until the next true branch.\n" + "\n" + "gbt {pc != 1234}\n" + " Resume execution until the next true branch, disregarding the instruction at address 1234.\n" + }, + { + "gni", + "\n" + " gn[i] [<count>]\n" + "\n" + "The gni command resumes execution of the current code. Control will not be returned to the " + "debugger until a breakpoint or watchpoint is hit, or until you manually break in using the " + "assigned key. Before executing, the gni command sets a temporary unconditional breakpoint " + "<count> instructions sequentially past the current one, which is automatically removed when " + "hit. If <count> is omitted, its default value is 1. If <count> is specified as zero, the " + "command does nothing. <count> is not permitted to exceed 512 decimal." + "\n" + "Examples:\n" + "\n" + "gni\n" + " Resume execution, stopping at the address of the next instruction unless something else " + "stops us first.\n" + "\n" + "gni 2\n" + " Resume execution, stopping at two instructions past the current one.\n" + }, + { "gex", "\n" " ge[x] [<exception>,[<condition>]]\n" diff --git a/src/emu/debug/debugvw.cpp b/src/emu/debug/debugvw.cpp index 5bcc3d057b8..54fe57ecf5a 100644 --- a/src/emu/debug/debugvw.cpp +++ b/src/emu/debug/debugvw.cpp @@ -503,11 +503,11 @@ bool debug_view_expression::recompute() std::string oldstring(m_parsed.original_string()); try { - m_parsed.parse(m_string.c_str()); + m_parsed.parse(m_string); } catch (expression_error &) { - m_parsed.parse(oldstring.c_str()); + m_parsed.parse(oldstring); } } diff --git a/src/emu/debug/dvmemory.cpp b/src/emu/debug/dvmemory.cpp index 02fcde9cbc3..1f1adcb138d 100644 --- a/src/emu/debug/dvmemory.cpp +++ b/src/emu/debug/dvmemory.cpp @@ -20,6 +20,20 @@ //************************************************************************** +// HELPER FUNCTIONS +//************************************************************************** + +namespace { + +constexpr u8 sanitise_character(u8 ch) +{ + // assume ISO-8859-1 (low 256 Unicode codepoints) - tab, soft hyphen, C0 and C1 cause problems + return ('\t' == ch) ? ' ' : (0xadU == ch) ? '-' : ((' ' > ch) || (('~' < ch) && (0xa0U > ch))) ? '.' : ch; +} + +} // anonymous namespace + +//************************************************************************** // GLOBAL VARIABLES //************************************************************************** @@ -301,7 +315,7 @@ void debug_view_memory::generate_row(debug_view_char *destmin, debug_view_char * if (dest >= destmin && dest < destmax) dest->byte = addrtext[ch]; - // generate the data and the ascii string + // generate the data and the ASCII string std::string chunkascii; if (m_shift_bits != 0) { @@ -323,7 +337,7 @@ void debug_view_memory::generate_row(debug_view_char *destmin, debug_view_char * for (int i = 0; i < m_bytes_per_chunk; i++) { u8 chval = chunkdata >> (8 * (m_bytes_per_chunk - i - 1)); - chunkascii += char((ismapped && isprint(chval)) ? chval : '.'); + chunkascii += char(ismapped ? sanitise_character(chval) : '.'); } } } @@ -380,7 +394,7 @@ void debug_view_memory::generate_row(debug_view_char *destmin, debug_view_char * for (int i = 0; i < m_bytes_per_chunk; i++) { u8 chval = chunkdata >> (8 * (m_bytes_per_chunk - i - 1)); - chunkascii += char((ismapped && isprint(chval)) ? chval : '.'); + chunkascii += char(ismapped ? sanitise_character(chval) : '.'); } } } @@ -526,16 +540,8 @@ void debug_view_memory::view_char(int chval) if (hexchar == nullptr || (m_shift_bits == 3 && chval >= '8')) break; - const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source); - offs_t address = (source.m_space != nullptr) ? source.m_space->byte_to_address(pos.m_address) : pos.m_address; - u64 data; - bool ismapped = read(m_bytes_per_chunk, address, data); - if (!ismapped) - break; - - data &= ~(util::make_bitmask<u64>(m_shift_bits) << pos.m_shift); - data |= u64(hexchar - hexvals) << pos.m_shift; - write(m_bytes_per_chunk, address, data); + if (!write_digit(pos.m_address, pos.m_shift, hexchar - hexvals)) + break; // TODO: alert OSD? } // fall through to the right-arrow press [[fallthrough]]; @@ -984,6 +990,41 @@ void debug_view_memory::write(u8 size, offs_t offs, u64 data) //------------------------------------------------- +// write_digit - write one hex or octal digit +// at the given address and bit position +//------------------------------------------------- + +bool debug_view_memory::write_digit(offs_t offs, u8 pos, u8 digit) +{ + const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source); + offs_t address = (source.m_space != nullptr) ? source.m_space->byte_to_address(offs) : offs; + u64 data; + bool ismapped = read(m_bytes_per_chunk, address, data); + if (!ismapped) + return false; + + // clamp to chunk size + if (m_bytes_per_chunk * 8 < pos + m_shift_bits) + { + assert(m_bytes_per_chunk * 8 > pos); + digit &= util::make_bitmask<u8>(m_bytes_per_chunk * 8 - pos); + } + + u64 write_data = (data & ~(util::make_bitmask<u64>(m_shift_bits) << pos)) | (u64(digit) << pos); + write(m_bytes_per_chunk, address, write_data); + + // verify that data reads back as it was written + if (source.m_space != nullptr) + { + read(m_bytes_per_chunk, address, data); + return data == write_data; + } + else + return true; +} + + +//------------------------------------------------- // set_expression - set the expression string // describing the home address //------------------------------------------------- diff --git a/src/emu/debug/dvmemory.h b/src/emu/debug/dvmemory.h index f2c738a6ff4..6f1ffab4e9e 100644 --- a/src/emu/debug/dvmemory.h +++ b/src/emu/debug/dvmemory.h @@ -127,6 +127,7 @@ private: // memory access bool read(u8 size, offs_t offs, u64 &data); void write(u8 size, offs_t offs, u64 data); + bool write_digit(offs_t offs, u8 pos, u8 digit); bool read(u8 size, offs_t offs, extFloat80_t &data); bool read_chunk(offs_t address, int chunknum, u64 &chunkdata); void generate_row(debug_view_char *destmin, debug_view_char *destmax, debug_view_char *destrow, offs_t address); diff --git a/src/emu/debug/express.cpp b/src/emu/debug/express.cpp index 8363928e5cb..f9074147b7b 100644 --- a/src/emu/debug/express.cpp +++ b/src/emu/debug/express.cpp @@ -387,10 +387,10 @@ void symbol_table::set_memory_modified_func(memory_modified_func modified) // add - add a new u64 pointer symbol //------------------------------------------------- -void symbol_table::add(const char *name, read_write rw, u64 *ptr) +symbol_entry &symbol_table::add(const char *name, read_write rw, u64 *ptr) { m_symlist.erase(name); - m_symlist.emplace(name, std::make_unique<integer_symbol_entry>(*this, name, rw, ptr)); + return *m_symlist.emplace(name, std::make_unique<integer_symbol_entry>(*this, name, rw, ptr)).first->second; } @@ -398,10 +398,10 @@ void symbol_table::add(const char *name, read_write rw, u64 *ptr) // add - add a new value symbol //------------------------------------------------- -void symbol_table::add(const char *name, u64 value) +symbol_entry &symbol_table::add(const char *name, u64 value) { m_symlist.erase(name); - m_symlist.emplace(name, std::make_unique<integer_symbol_entry>(*this, name, value)); + return *m_symlist.emplace(name, std::make_unique<integer_symbol_entry>(*this, name, value)).first->second; } @@ -409,10 +409,10 @@ void symbol_table::add(const char *name, u64 value) // add - add a new register symbol //------------------------------------------------- -void symbol_table::add(const char *name, getter_func getter, setter_func setter, const std::string &format_string) +symbol_entry &symbol_table::add(const char *name, getter_func getter, setter_func setter, const std::string &format_string) { m_symlist.erase(name); - m_symlist.emplace(name, std::make_unique<integer_symbol_entry>(*this, name, getter, setter, format_string)); + return *m_symlist.emplace(name, std::make_unique<integer_symbol_entry>(*this, name, getter, setter, format_string)).first->second; } @@ -420,10 +420,10 @@ void symbol_table::add(const char *name, getter_func getter, setter_func setter, // add - add a new function symbol //------------------------------------------------- -void symbol_table::add(const char *name, int minparams, int maxparams, execute_func execute) +symbol_entry &symbol_table::add(const char *name, int minparams, int maxparams, execute_func execute) { m_symlist.erase(name); - m_symlist.emplace(name, std::make_unique<function_symbol_entry>(*this, name, minparams, maxparams, execute)); + return *m_symlist.emplace(name, std::make_unique<function_symbol_entry>(*this, name, minparams, maxparams, execute)).first->second; } diff --git a/src/emu/debug/express.h b/src/emu/debug/express.h index 23284193f47..74b52710959 100644 --- a/src/emu/debug/express.h +++ b/src/emu/debug/express.h @@ -175,15 +175,16 @@ public: // getters const std::unordered_map<std::string, std::unique_ptr<symbol_entry>> &entries() const { return m_symlist; } symbol_table *parent() const { return m_parent; } + running_machine &machine() { return m_machine; } // setters void set_memory_modified_func(memory_modified_func modified); // symbol access - void add(const char *name, read_write rw, u64 *ptr = nullptr); - void add(const char *name, u64 constvalue); - void add(const char *name, getter_func getter, setter_func setter = nullptr, const std::string &format_string = ""); - void add(const char *name, int minparams, int maxparams, execute_func execute); + symbol_entry &add(const char *name, read_write rw, u64 *ptr = nullptr); + symbol_entry &add(const char *name, u64 constvalue); + symbol_entry &add(const char *name, getter_func getter, setter_func setter = nullptr, const std::string &format_string = ""); + symbol_entry &add(const char *name, int minparams, int maxparams, execute_func execute); symbol_entry *find(const char *name) const { if (name) { auto search = m_symlist.find(name); if (search != m_symlist.end()) return search->second.get(); else return nullptr; } else return nullptr; } symbol_entry *find_deep(const char *name); diff --git a/src/emu/debug/points.cpp b/src/emu/debug/points.cpp index c44bc3f7563..a19f0ab5619 100644 --- a/src/emu/debug/points.cpp +++ b/src/emu/debug/points.cpp @@ -22,18 +22,19 @@ // debug_breakpoint - constructor //------------------------------------------------- -debug_breakpoint::debug_breakpoint(device_debug* debugInterface, - symbol_table &symbols, - int index, - offs_t address, - const char *condition, - const char *action) - : m_debugInterface(debugInterface), - m_index(index), - m_enabled(true), - m_address(address), - m_condition(symbols, (condition != nullptr) ? condition : "1"), - m_action((action != nullptr) ? action : "") +debug_breakpoint::debug_breakpoint( + device_debug *debugInterface, + symbol_table &symbols, + int index, + offs_t address, + const char *condition, + const char *action) : + m_debugInterface(debugInterface), + m_index(index), + m_enabled(true), + m_address(address), + m_condition(symbols, condition ? condition : "1"), + m_action(action ? action : "") { } @@ -59,7 +60,7 @@ bool debug_breakpoint::hit(offs_t pc) { return (m_condition.execute() != 0); } - catch (expression_error &) + catch (expression_error const &) { return false; } @@ -78,27 +79,28 @@ bool debug_breakpoint::hit(offs_t pc) // debug_watchpoint - constructor //------------------------------------------------- -debug_watchpoint::debug_watchpoint(device_debug* debugInterface, - symbol_table &symbols, - int index, - address_space &space, - read_or_write type, - offs_t address, - offs_t length, - const char *condition, - const char *action) - : m_debugInterface(debugInterface), - m_phr(nullptr), - m_phw(nullptr), - m_space(space), - m_index(index), - m_enabled(true), - m_type(type), - m_address(address & space.addrmask()), - m_length(length), - m_condition(symbols, (condition != nullptr) ? condition : "1"), - m_action((action != nullptr) ? action : ""), - m_installing(false) +debug_watchpoint::debug_watchpoint( + device_debug* debugInterface, + symbol_table &symbols, + int index, + address_space &space, + read_or_write type, + offs_t address, + offs_t length, + const char *condition, + const char *action) : + m_debugInterface(debugInterface), + m_phr(nullptr), + m_phw(nullptr), + m_space(space), + m_index(index), + m_enabled(true), + m_type(type), + m_address(address & space.addrmask()), + m_length(length), + m_condition(symbols, condition ? condition : "1"), + m_action(action ? action : ""), + m_installing(false) { std::fill(std::begin(m_start_address), std::end(m_start_address), 0); std::fill(std::begin(m_end_address), std::end(m_end_address), 0); @@ -170,21 +172,21 @@ debug_watchpoint::debug_watchpoint(device_debug* debugInterface, } install(read_or_write::READWRITE); - m_notifier = m_space.add_change_notifier([this](read_or_write mode) { - if (m_enabled) - { - install(mode); - } - }); + m_notifier = m_space.add_change_notifier( + [this] (read_or_write mode) + { + if (m_enabled) + { + install(mode); + } + }); } debug_watchpoint::~debug_watchpoint() { - m_space.remove_change_notifier(m_notifier); - if (m_phr) - m_phr->remove(); - if (m_phw) - m_phw->remove(); + m_notifier.reset(); + m_phr.remove(); + m_phw.remove(); } void debug_watchpoint::setEnabled(bool value) @@ -197,10 +199,8 @@ void debug_watchpoint::setEnabled(bool value) else { m_installing = true; - if(m_phr) - m_phr->remove(); - if(m_phw) - m_phw->remove(); + m_phr.remove(); + m_phw.remove(); m_installing = false; } } @@ -211,24 +211,28 @@ void debug_watchpoint::install(read_or_write mode) if (m_installing) return; m_installing = true; - if ((u32(mode) & u32(read_or_write::READ)) && m_phr) - m_phr->remove(); - if ((u32(mode) & u32(read_or_write::WRITE)) && m_phw) - m_phw->remove(); + if (u32(mode) & u32(read_or_write::READ)) + m_phr.remove(); + if (u32(mode) & u32(read_or_write::WRITE)) + m_phw.remove(); std::string name = util::string_format("wp@%x", m_address); switch (m_space.data_width()) { case 8: if (u32(m_type) & u32(mode) & u32(read_or_write::READ)) - m_phr = m_space.install_read_tap(m_start_address[0], m_end_address[0], name, - [this](offs_t offset, u8 &data, u8 mem_mask) { - triggered(read_or_write::READ, offset, data, mem_mask); - }, m_phr); + m_phr = m_space.install_read_tap( + m_start_address[0], m_end_address[0], name, + [this](offs_t offset, u8 &data, u8 mem_mask) { + triggered(read_or_write::READ, offset, data, mem_mask); + }, + &m_phr); if (u32(m_type) & u32(mode) & u32(read_or_write::WRITE)) - m_phw = m_space.install_write_tap(m_start_address[0], m_end_address[0], name, - [this](offs_t offset, u8 &data, u8 mem_mask) { - triggered(read_or_write::WRITE, offset, data, mem_mask); - }, m_phw); + m_phw = m_space.install_write_tap( + m_start_address[0], m_end_address[0], name, + [this](offs_t offset, u8 &data, u8 mem_mask) { + triggered(read_or_write::WRITE, offset, data, mem_mask); + }, + &m_phw); break; case 16: @@ -237,17 +241,21 @@ void debug_watchpoint::install(read_or_write mode) { u16 mask = m_masks[i]; if (u32(m_type) & u32(mode) & u32(read_or_write::READ)) - m_phr = m_space.install_read_tap(m_start_address[i], m_end_address[i], name, - [this, mask](offs_t offset, u16 &data, u16 mem_mask) { - if (mem_mask & mask) - triggered(read_or_write::READ, offset, data, mem_mask); - }, m_phr); + m_phr = m_space.install_read_tap( + m_start_address[i], m_end_address[i], name, + [this, mask](offs_t offset, u16 &data, u16 mem_mask) { + if (mem_mask & mask) + triggered(read_or_write::READ, offset, data, mem_mask); + }, + &m_phr); if (u32(m_type) & u32(mode) & u32(read_or_write::WRITE)) - m_phw = m_space.install_write_tap(m_start_address[i], m_end_address[i], name, - [this, mask](offs_t offset, u16 &data, u16 mem_mask) { - if (mem_mask & mask) - triggered(read_or_write::WRITE, offset, data, mem_mask); - }, m_phw); + m_phw = m_space.install_write_tap( + m_start_address[i], m_end_address[i], name, + [this, mask](offs_t offset, u16 &data, u16 mem_mask) { + if (mem_mask & mask) + triggered(read_or_write::WRITE, offset, data, mem_mask); + }, + &m_phw); } break; @@ -257,17 +265,21 @@ void debug_watchpoint::install(read_or_write mode) { u32 mask = m_masks[i]; if (u32(m_type) & u32(mode) & u32(read_or_write::READ)) - m_phr = m_space.install_read_tap(m_start_address[i], m_end_address[i], name, - [this, mask](offs_t offset, u32 &data, u32 mem_mask) { - if (mem_mask & mask) - triggered(read_or_write::READ, offset, data, mem_mask); - }, m_phr); + m_phr = m_space.install_read_tap( + m_start_address[i], m_end_address[i], name, + [this, mask](offs_t offset, u32 &data, u32 mem_mask) { + if (mem_mask & mask) + triggered(read_or_write::READ, offset, data, mem_mask); + }, + &m_phr); if (u32(m_type) & u32(mode) & u32(read_or_write::WRITE)) - m_phw = m_space.install_write_tap(m_start_address[i], m_end_address[i], name, - [this, mask](offs_t offset, u32 &data, u32 mem_mask) { - if (mem_mask & mask) - triggered(read_or_write::WRITE, offset, data, mem_mask); - }, m_phw); + m_phw = m_space.install_write_tap( + m_start_address[i], m_end_address[i], name, + [this, mask](offs_t offset, u32 &data, u32 mem_mask) { + if (mem_mask & mask) + triggered(read_or_write::WRITE, offset, data, mem_mask); + }, + &m_phw); } break; @@ -277,17 +289,20 @@ void debug_watchpoint::install(read_or_write mode) { u64 mask = m_masks[i]; if (u32(m_type) & u32(mode) & u32(read_or_write::READ)) - m_phr = m_space.install_read_tap(m_start_address[i], m_end_address[i], name, - [this, mask](offs_t offset, u64 &data, u64 mem_mask) { - if (mem_mask & mask) - triggered(read_or_write::READ, offset, data, mem_mask); - }, m_phr); + m_phr = m_space.install_read_tap( + m_start_address[i], m_end_address[i], name, + [this, mask](offs_t offset, u64 &data, u64 mem_mask) { + if (mem_mask & mask) + triggered(read_or_write::READ, offset, data, mem_mask); + }, + &m_phr); if (u32(m_type) & u32(mode) & u32(read_or_write::WRITE)) m_phw = m_space.install_write_tap(m_start_address[i], m_end_address[i], name, - [this, mask](offs_t offset, u64 &data, u64 mem_mask) { - if (mem_mask & mask) - triggered(read_or_write::WRITE, offset, data, mem_mask); - }, m_phw); + [this, mask](offs_t offset, u64 &data, u64 mem_mask) { + if (mem_mask & mask) + triggered(read_or_write::WRITE, offset, data, mem_mask); + }, + &m_phw); } break; } diff --git a/src/emu/debug/points.h b/src/emu/debug/points.h index 84cdad7720b..62ad953626c 100644 --- a/src/emu/debug/points.h +++ b/src/emu/debug/points.h @@ -102,8 +102,8 @@ private: void triggered(read_or_write type, offs_t address, u64 data, u64 mem_mask); device_debug * m_debugInterface; // the interface we were created from - memory_passthrough_handler *m_phr; // passthrough handler reference, read access - memory_passthrough_handler *m_phw; // passthrough handler reference, write access + memory_passthrough_handler m_phr; // passthrough handler reference, read access + memory_passthrough_handler m_phw; // passthrough handler reference, write access address_space & m_space; // address space int m_index; // user reported index bool m_enabled; // enabled? @@ -112,7 +112,7 @@ private: offs_t m_length; // length of watch area parsed_expression m_condition; // condition std::string m_action; // action - int m_notifier; // address map change notifier id + util::notifier_subscription m_notifier; // address map change notifier ID offs_t m_start_address[3]; // the start addresses of the checks to install offs_t m_end_address[3]; // the end addresses |