summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/debug/debugcon.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/debug/debugcon.cpp')
-rw-r--r--src/emu/debug/debugcon.cpp1043
1 files changed, 800 insertions, 243 deletions
diff --git a/src/emu/debug/debugcon.cpp b/src/emu/debug/debugcon.cpp
index 8faf4f0de02..ec5bc154751 100644
--- a/src/emu/debug/debugcon.cpp
+++ b/src/emu/debug/debugcon.cpp
@@ -1,8 +1,8 @@
// license:BSD-3-Clause
-// copyright-holders:Aaron Giles
+// copyright-holders:Aaron Giles, Vas Crabb
/*********************************************************************
- debugcon.c
+ debugcon.cpp
Debugger console engine.
@@ -10,12 +10,21 @@
#include "emu.h"
#include "debugcon.h"
+
#include "debugcpu.h"
#include "debugvw.h"
#include "textbuf.h"
+
#include "debugger.h"
-#include <ctype.h>
+#include "fileio.h"
+#include "main.h"
+
+#include "corestr.h"
+
+#include <cctype>
#include <fstream>
+#include <iterator>
+
/***************************************************************************
CONSTANTS
@@ -35,9 +44,10 @@
debugger_console::debugger_console(running_machine &machine)
: m_machine(machine)
+ , m_visiblecpu(nullptr)
, m_console_textbuf(nullptr)
, m_errorlog_textbuf(nullptr)
- , m_commandlist(nullptr)
+ , m_logfile(nullptr)
{
/* allocate text buffers */
m_console_textbuf = text_buffer_alloc(CONSOLE_BUF_SIZE, CONSOLE_MAX_LINES);
@@ -48,6 +58,9 @@ debugger_console::debugger_console(running_machine &machine)
if (!m_errorlog_textbuf)
return;
+ /* due to initialization order, @machine is holding our debug.log handle */
+ m_logfile = machine.steal_debuglogfile();
+
/* print the opening lines */
printf("%s debugger version %s\n", emulator_info::get_appname(), emulator_info::get_build_version());
printf("Currently targeting %s (%s)\n", m_machine.system().name, m_machine.system().type.fullname());
@@ -60,31 +73,41 @@ debugger_console::debugger_console(running_machine &machine)
m_machine.add_logerror_callback(std::bind(&debugger_console::errorlog_write_line, this, _1));
/* register our own custom-command help */
- register_command("helpcustom", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_console::execute_help_custom, this, _1, _2));
+ register_command("helpcustom", CMDFLAG_NONE, 0, 0, std::bind(&debugger_console::execute_help_custom, this, _1));
+ register_command("condump", CMDFLAG_NONE, 1, 1, std::bind(&debugger_console::execute_condump, this, _1));
+
+ /* first CPU is visible by default */
+ for (device_t &device : device_enumerator(m_machine.root_device()))
+ {
+ auto *cpu = dynamic_cast<cpu_device *>(&device);
+ if (cpu)
+ {
+ m_visiblecpu = cpu;
+ break;
+ }
+ }
}
+debugger_console::~debugger_console()
+{
+}
-/*-------------------------------------------------
- exit - frees the console system
--------------------------------------------------*/
+
+//-------------------------------------------------
+// exit - frees the console system
+//-------------------------------------------------
void debugger_console::exit()
{
- /* free allocated memory */
- if (m_console_textbuf)
- {
- text_buffer_free(m_console_textbuf);
- }
- m_console_textbuf = nullptr;
+ // free allocated memory
+ m_console_textbuf.reset();
+ m_errorlog_textbuf.reset();
- if (m_errorlog_textbuf)
- {
- text_buffer_free(m_errorlog_textbuf);
- }
- m_errorlog_textbuf = nullptr;
+ // free the command list
+ m_commandlist.clear();
- /* free the command list */
- m_commandlist = nullptr;
+ // close the logfile, if any
+ m_logfile.reset();
}
@@ -95,192 +118,230 @@ void debugger_console::exit()
***************************************************************************/
-/*------------------------------------------------------------
- execute_help_custom - execute the helpcustom command
-------------------------------------------------------------*/
+inline bool debugger_console::debug_command::compare::operator()(const debug_command &a, const debug_command &b) const
+{
+ return a.command < b.command;
+}
+
+inline bool debugger_console::debug_command::compare::operator()(const char *a, const debug_command &b) const
+{
+ return strcmp(a, b.command.c_str()) < 0;
+}
-void debugger_console::execute_help_custom(int ref, const std::vector<std::string> &params)
+inline bool debugger_console::debug_command::compare::operator()(const debug_command &a, const char *b) const
{
- debug_command *cmd = m_commandlist;
- char buf[64];
- while (cmd)
+ return strcmp(a.command.c_str(), b) < 0;
+}
+
+
+debugger_console::debug_command::debug_command(std::string_view _command, u32 _flags, int _minparams, int _maxparams, std::function<void (const std::vector<std::string_view> &)> &&_handler)
+ : command(_command), params(nullptr), help(nullptr), handler(std::move(_handler)), flags(_flags), minparams(_minparams), maxparams(_maxparams)
+{
+}
+
+
+//------------------------------------------------------------
+// execute_help_custom - execute the helpcustom command
+//------------------------------------------------------------
+
+void debugger_console::execute_help_custom(const std::vector<std::string_view> &params)
+{
+ for (const debug_command &cmd : m_commandlist)
{
- if (cmd->flags & CMDFLAG_CUSTOM_HELP)
+ if (cmd.flags & CMDFLAG_CUSTOM_HELP)
{
- snprintf(buf, 63, "%s help", cmd->command);
- 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);
}
- cmd = cmd->next;
}
}
+/*------------------------------------------------------------
+ execute_condump - execute the condump command
+------------------------------------------------------------*/
-/*-------------------------------------------------
- trim_parameter - executes a
- command
--------------------------------------------------*/
+void debugger_console::execute_condump(const std::vector<std::string_view>& params)
+{
+ std::string filename(params[0]);
+ const char* mode;
+
+ /* replace macros */
+ strreplace(filename, "{game}", m_machine.basename());
+
+ mode = "w";
+ /* opening for append? */
+ if (filename.length() >= 2 && filename[0] == '>' && filename[1] == '>')
+ {
+ mode = "a";
+ filename = filename.substr(2);
+ }
+
+ FILE* f = fopen(filename.c_str(), mode);
+ if (!f)
+ {
+ printf("Error opening file '%s'\n", filename);
+ return;
+ }
+
+ for (std::string_view line_info : text_buffer_lines(*m_console_textbuf))
+ {
+ fwrite(line_info.data(), sizeof(char), line_info.length(), f);
+ fputc('\n', f);
+ }
-void debugger_console::trim_parameter(char **paramptr, bool keep_quotes)
+ fclose(f);
+ printf("Wrote console contents to '%s'\n", filename);
+}
+
+//-------------------------------------------------
+// visible_symtable - return the locally-visible
+// symbol table
+//-------------------------------------------------
+
+symbol_table &debugger_console::visible_symtable() const
+{
+ return m_visiblecpu->debug()->symtable();
+}
+
+
+
+//-------------------------------------------------
+// trim_parameter - trim spaces and quotes around
+// a command parameter
+//-------------------------------------------------
+
+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> &params)
{
- debug_command *cmd, *found = nullptr;
- int i, foundcount = 0;
- char *p, *command;
- size_t len;
+ // no params is an error
+ if (params.empty())
+ return CMDERR::none();
+
+ // the first parameter has the command and the real first parameter; separate them
+ 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].remove_prefix(pos);
- /* no params is an error */
- if (params == 0)
- return CMDERR_NONE;
+ // search the command list
+ auto const found = m_commandlist.lower_bound(command.c_str());
- /* the first parameter has the command and the real first parameter; separate them */
- for (p = param[0]; *p && isspace(u8(*p)); p++) { }
- for (command = p; *p && !isspace(u8(*p)); p++) { }
- if (*p != 0)
+ // error if not found
+ 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() > command.length())
{
- *p++ = 0;
- for ( ; *p && isspace(u8(*p)); p++) { }
- if (*p != 0)
- param[0] = p;
- else
- params = 0;
+ auto const next = std::next(found);
+ if (m_commandlist.end() != next && std::string_view(command) == std::string_view(next->command).substr(0, command.length()))
+ return CMDERR::ambiguous_command(0);
}
- else
- {
- params = 0;
- param[0] = nullptr;
- }
-
- /* search the command list */
- len = strlen(command);
- for (cmd = m_commandlist; cmd != nullptr; cmd = cmd->next)
- if (!strncmp(command, cmd->command, len))
- {
- foundcount++;
- found = cmd;
- if (strlen(cmd->command) == len)
- {
- foundcount = 1;
- break;
- }
- }
- /* error if not found */
- if (!found)
- return MAKE_CMDERR_UNKNOWN_COMMAND(0);
- if (foundcount > 1)
- return MAKE_CMDERR_AMBIGUOUS_COMMAND(0);
+ // now go back and trim quotes and braces and any spaces they reveal
+ for (std::string_view &param : params)
+ param = trim_parameter(param, found->flags & CMDFLAG_KEEP_QUOTES);
- /* NULL-terminate and trim space around all the parameters */
- for (i = 1; i < params; i++)
- *param[i]++ = 0;
+ // see if we have the right number of parameters
+ if (params.size() < found->minparams)
+ return CMDERR::not_enough_params(0);
+ if (params.size() > found->maxparams)
+ return CMDERR::too_many_params(0);
- /* now go back and trim quotes and braces and any spaces they reveal*/
- for (i = 0; i < params; i++)
- trim_parameter(&param[i], found->flags & CMDFLAG_KEEP_QUOTES);
-
- /* see if we have the right number of parameters */
- if (params < found->minparams)
- return MAKE_CMDERR_NOT_ENOUGH_PARAMS(0);
- if (params > found->maxparams)
- return MAKE_CMDERR_TOO_MANY_PARAMS(0);
-
- /* execute the handler */
+ // execute the handler
if (execute)
- {
- std::vector<std::string> params_vec(param, param + params);
- found->handler(found->ref, params_vec);
- }
- return CMDERR_NONE;
+ found->handler(params);
+ 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 };
- CMDERR result;
- char *command_start;
- char *p, c = 0;
-
- /* make a copy of the command */
- strcpy(command, original_command.c_str());
+ std::string_view::size_type pos = 0;
+ std::string_view::size_type len = command.length();
- /* 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
@@ -290,90 +351,84 @@ 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 MAKE_CMDERR_UNBALANCED_PARENS(p - command); break;
- case ']': if (parendex == 0 || parens[--parendex] != '[') return MAKE_CMDERR_UNBALANCED_PARENS(p - command); break;
- case '}': if (parendex == 0 || parens[--parendex] != '{') return MAKE_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 = tolower(u8(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 MAKE_CMDERR_UNBALANCED_QUOTES(p - command);
- if (parendex != 0)
- return MAKE_CMDERR_UNBALANCED_PARENS(p - command);
-
- /* NULL-terminate if we ended in a semicolon */
- p--;
- if (c == ';') *p++ = 0;
+ return CMDERR::unbalanced_quotes(pos);
+ if (!parens.empty())
+ return CMDERR::unbalanced_parens(pos);
- /* process the command */
- command_start = params[0];
+ // process the command
+ std::string_view command_or_expr = 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
{
- u64 expresult;
- parsed_expression expression(m_machine.debugger().cpu().get_visible_symtable(), command_start, &expresult);
+ parsed_expression expr(visible_symtable(), command_or_expr);
+ if (execute)
+ expr.execute();
}
catch (expression_error &err)
{
- return MAKE_CMDERR_EXPRESSION_ERROR(err);
+ return CMDERR::expression_error(err);
}
}
else
{
- result = internal_execute_command(execute, paramcount, &params[0]);
- if (result != CMDERR_NONE)
- return MAKE_CMDERR(CMDERR_ERROR_CLASS(result), command_start - command);
+ const CMDERR result = internal_execute_command(execute, params);
+ if (result.error_class() != CMDERR::NONE)
+ return CMDERR(result.error_class(), startpos);
}
}
- return CMDERR_NONE;
+ 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)
{
- CMDERR result;
-
- /* echo if requested */
+ // echo if requested
if (echo)
- printf(">%s\n", command.c_str());
+ printf(">%s\n", command);
- /* parse and execute */
- result = internal_parse_command(command, true);
+ // parse and execute
+ const CMDERR result = internal_parse_command(command, true);
- /* display errors */
- if (result != CMDERR_NONE)
+ // display errors
+ if (result.error_class() != CMDERR::NONE)
{
if (!echo)
- printf(">%s\n", command.c_str());
- printf(" %*s^\n", CMDERR_ERROR_OFFSET(result), "");
- printf("%s\n", cmderr_to_string(result).c_str());
+ printf(">%s\n", command);
+ printf(" %*s^\n", result.error_offset(), "");
+ printf("%s\n", cmderr_to_string(result));
}
- /* update all views */
+ // update all views
if (echo)
{
m_machine.debug_view().update_all();
@@ -383,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);
}
@@ -397,26 +452,16 @@ 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 ref, int minparams, int maxparams, std::function<void(int, 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_view> &)> &&handler)
{
if (m_machine.phase() != machine_phase::INIT)
throw emu_fatalerror("Can only call debugger_console::register_command() at init time!");
if (!(m_machine.debug_flags & DEBUG_FLAG_ENABLED))
throw emu_fatalerror("Cannot call debugger_console::register_command() when debugger is not running");
- debug_command *cmd = auto_alloc_clear(m_machine, <debug_command>());
-
- /* fill in the command */
- strcpy(cmd->command, command);
- cmd->flags = flags;
- cmd->ref = ref;
- cmd->minparams = minparams;
- cmd->maxparams = maxparams;
- cmd->handler = handler;
-
- /* link it */
- cmd->next = m_commandlist;
- m_commandlist = cmd;
+ auto const ins = m_commandlist.emplace(command, flags, minparams, maxparams, std::move(handler));
+ if (!ins.second)
+ osd_printf_error("error: Duplicate debugger command %s registered\n", command);
}
@@ -469,7 +514,7 @@ void debugger_console::process_source_file()
buf.resize(pos);
// strip whitespace
- strtrimrightspace(buf);
+ buf = strtrimrightspace(buf);
// execute the command
if (!buf.empty())
@@ -499,22 +544,510 @@ void debugger_console::process_source_file()
std::string debugger_console::cmderr_to_string(CMDERR error)
{
- int offset = CMDERR_ERROR_OFFSET(error);
- switch (CMDERR_ERROR_CLASS(error))
+ const int offset = error.error_offset();
+ switch (error.error_class())
{
- case CMDERR_UNKNOWN_COMMAND: return "unknown command";
- case CMDERR_AMBIGUOUS_COMMAND: return "ambiguous command";
- case CMDERR_UNBALANCED_PARENS: return "unbalanced parentheses";
- case CMDERR_UNBALANCED_QUOTES: return "unbalanced quotes";
- case CMDERR_NOT_ENOUGH_PARAMS: return "not enough parameters for command";
- case CMDERR_TOO_MANY_PARAMS: return "too many parameters for command";
- case CMDERR_EXPRESSION_ERROR: return string_format("error in assignment expression: %s",
+ case CMDERR::UNKNOWN_COMMAND: return "unknown command";
+ case CMDERR::AMBIGUOUS_COMMAND: return "ambiguous command";
+ case CMDERR::UNBALANCED_PARENS: return "unbalanced parentheses";
+ case CMDERR::UNBALANCED_QUOTES: return "unbalanced quotes";
+ case CMDERR::NOT_ENOUGH_PARAMS: return "not enough parameters for command";
+ case CMDERR::TOO_MANY_PARAMS: return "too many parameters for command";
+ case CMDERR::EXPRESSION_ERROR: return string_format("error in assignment expression: %s",
expression_error(static_cast<expression_error::error_code>(offset)).code_string());
default: return "unknown error";
}
}
+//**************************************************************************
+// PARAMETER VALIDATION HELPERS
+//**************************************************************************
+
+namespace {
+
+template <typename T>
+inline std::string_view::size_type find_delimiter(std::string_view str, T &&is_delim)
+{
+ unsigned parens = 0;
+ for (std::string_view::size_type i = 0; str.length() > i; ++i)
+ {
+ if (str[i] == '(')
+ {
+ ++parens;
+ }
+ else if (parens)
+ {
+ if (str[i] == ')')
+ --parens;
+ }
+ else if (is_delim(str[i]))
+ {
+ return i;
+ }
+ }
+ return std::string_view::npos;
+}
+
+} // anonymous namespace
+
+
+/// \brief Validate parameter as a Boolean value
+///
+/// Validates a parameter as a Boolean value. Fixed strings and
+/// expressions evaluating to numeric values are recognised. The result
+/// is unchanged for an empty string.
+/// \param [in] param The parameter string.
+/// \param [in,out] result The default value on entry, and the value of
+/// the parameter interpreted as a Boolean on success. Unchanged if
+/// the parameter is an empty string.
+/// \return true if the parameter is a valid Boolean value or an empty
+/// string, or false otherwise.
+bool debugger_console::validate_boolean_parameter(std::string_view param, bool &result)
+{
+ // nullptr parameter does nothing and returns no error
+ if (param.empty())
+ return true;
+
+ // evaluate the expression; success if no error
+ using namespace std::literals;
+ bool const is_true = util::streqlower(param, "true"sv);
+ bool const is_false = util::streqlower(param, "false"sv);
+
+ if (is_true || is_false)
+ {
+ result = is_true;
+ return true;
+ }
+
+ // try to evaluate as a number
+ u64 val;
+ if (!validate_number_parameter(param, val))
+ return false;
+
+ result = val != 0;
+ return true;
+}
+
+
+/// \brief Validate parameter as a numeric value
+///
+/// Parses the parameter as an expression and evaluates it as a number.
+/// \param [in] param The parameter string.
+/// \param [out] result The numeric value of the expression on success.
+/// Unchanged on failure.
+/// \return true if the parameter is a valid expression that evaluates
+/// to a numeric value, or false otherwise.
+bool debugger_console::validate_number_parameter(std::string_view param, u64 &result)
+{
+ // evaluate the expression; success if no error
+ try
+ {
+ result = parsed_expression(visible_symtable(), param).execute();
+ return true;
+ }
+ catch (expression_error const &error)
+ {
+ // print an error pointing to the character that caused it
+ printf("Error in expression: %s\n", param);
+ printf(" %*s^", error.offset(), "");
+ printf("%s\n", error.code_string());
+ return false;
+ }
+}
+
+
+/// \brief Validate parameter as a device
+///
+/// Validates a parameter as a device identifier and retrieves the
+/// device on success. A string corresponding to the tag of a device
+/// refers to that device; an empty string refers to the current CPU
+/// with debugger focus; any other string is parsed as an expression
+/// and treated as an index of a device implementing
+/// #device_execute_interface and #device_state_interface, and exposing
+/// a generic PC base value.
+/// \param [in] param The parameter string.
+/// \param [out] result A pointer to the device on success, or unchanged
+/// on failure.
+/// \return true if the parameter refers to a device in the current
+/// system, or false otherwise.
+bool debugger_console::validate_device_parameter(std::string_view param, device_t *&result)
+{
+ // if no parameter, use the visible CPU
+ if (param.empty())
+ {
+ device_t *const current = m_visiblecpu;
+ if (current)
+ {
+ result = current;
+ return true;
+ }
+ else
+ {
+ printf("No valid CPU is currently selected\n");
+ return false;
+ }
+ }
+
+ // next look for a tag match
+ std::string_view relative = param;
+ device_t &base = get_device_search_base(relative);
+ device_t *device = base.subdevice(strmakelower(relative));
+ if (device)
+ {
+ result = device;
+ return true;
+ }
+
+ // then evaluate as an expression; on an error assume it was a tag
+ u64 cpunum;
+ try
+ {
+ cpunum = parsed_expression(visible_symtable(), param).execute();
+ }
+ catch (expression_error &)
+ {
+ printf("Unable to find device '%s'\n", param);
+ return false;
+ }
+
+ // attempt to find by numerical index
+ device = get_cpu_by_index(cpunum);
+ if (device)
+ {
+ result = device;
+ return true;
+ }
+ else
+ {
+ // if out of range, complain
+ printf("Invalid CPU index %u\n", cpunum);
+ return false;
+ }
+}
+
+
+/// \brief Validate a parameter as a CPU
+///
+/// Validates a parameter as a CPU identifier. Uses the same rules as
+/// #validate_device_parameter to identify devices, but additionally
+/// checks that the device is a "CPU" for the debugger's purposes.
+/// \param [in] The parameter string.
+/// \param [out] result The device on success, or unchanged on failure.
+/// \return true if the parameter refers to a CPU-like device in the
+/// current system, or false otherwise.
+bool debugger_console::validate_cpu_parameter(std::string_view param, device_t *&result)
+{
+ // first do the standard device thing
+ device_t *device;
+ if (!validate_device_parameter(param, device))
+ return false;
+
+ // check that it's a "CPU" for the debugger's purposes
+ device_execute_interface const *execute;
+ if (device->interface(execute))
+ {
+ result = device;
+ return true;
+ }
+
+ printf("Device %s is not a CPU\n", device->name());
+ return false;
+}
+
+
+/// \brief Validate a parameter as an address space identifier
+///
+/// Validates a parameter as an address space identifier. Uses the same
+/// rules as #validate_device_parameter to identify devices. If the
+/// default address space number is negative, the first address space
+/// exposed by the device will be used as the default.
+/// \param [in] The parameter string.
+/// \param [in] spacenum The default address space index. If negative,
+/// the first address space exposed by the device (i.e. the address
+/// space with the lowest index) will be used as the default.
+/// \param [out] result The addresfs space on success, or unchanged on
+/// failure.
+/// \return true if the parameter refers to an address space in the
+/// current system, or false otherwise.
+bool debugger_console::validate_device_space_parameter(std::string_view param, int spacenum, address_space *&result)
+{
+ device_t *device;
+ std::string spacename;
+ if (param.empty())
+ {
+ // if no parameter, use the visible CPU
+ device = m_visiblecpu;
+ if (!device)
+ {
+ printf("No valid CPU is currently selected\n");
+ return false;
+ }
+ }
+ else
+ {
+ // look for a tag match on the whole parameter value
+ std::string_view relative = param;
+ device_t &base = get_device_search_base(relative);
+ device = base.subdevice(strmakelower(relative));
+
+ // if that failed, treat the last component as an address space
+ if (!device)
+ {
+ auto const delimiter = relative.find_last_of(":^");
+ bool const found = std::string_view::npos != delimiter;
+ if (!found || (':' == relative[delimiter]))
+ {
+ spacename = strmakelower(relative.substr(found ? (delimiter + 1) : 0));
+ relative = relative.substr(0, !found ? 0 : !delimiter ? 1 : delimiter);
+ if (!relative.empty())
+ device = base.subdevice(strmakelower(relative));
+ else if (m_visiblecpu)
+ device = m_visiblecpu;
+ else
+ device = &m_machine.root_device();
+ }
+ }
+ }
+
+ // if still no device found, evaluate as an expression
+ if (!device)
+ {
+ u64 cpunum;
+ try
+ {
+ cpunum = parsed_expression(visible_symtable(), param).execute();
+ }
+ catch (expression_error const &)
+ {
+ // parsing failed - assume it was a tag
+ printf("Unable to find device '%s'\n", param);
+ return false;
+ }
+
+ // attempt to find by numerical index
+ device = get_cpu_by_index(cpunum);
+ if (!device)
+ {
+ // if out of range, complain
+ printf("Invalid CPU index %u\n", cpunum);
+ return false;
+ }
+ }
+
+ // ensure the device implements the memory interface
+ device_memory_interface *memory;
+ if (!device->interface(memory))
+ {
+ printf("No memory interface found for device %s\n", device->name());
+ return false;
+ }
+
+ // fall back to supplied default space if appropriate
+ if (spacename.empty() && (0 <= spacenum))
+ {
+ if (memory->has_space(spacenum))
+ {
+ result = &memory->space(spacenum);
+ return true;
+ }
+ else
+ {
+ printf("No matching memory space found for device '%s'\n", device->tag());
+ return false;
+ }
+ }
+
+ // otherwise find the specified space or fall back to the first populated space
+ for (int i = 0; memory->max_space_count() > i; ++i)
+ {
+ if (memory->has_space(i) && (spacename.empty() || (memory->space(i).name() == spacename)))
+ {
+ result = &memory->space(i);
+ return true;
+ }
+ }
+
+ // report appropriate error message
+ if (spacename.empty())
+ printf("No memory spaces found for device '%s'\n", device->tag());
+ else
+ printf("Memory space '%s' not found found for device '%s'\n", spacename, device->tag());
+ return false;
+}
+
+
+/// \brief Validate a parameter as a target address
+///
+/// Validates a parameter as an numeric expression to use as an address
+/// optionally followed by a colon and a device identifier. If the
+/// device identifier is not presnt, the current CPU with debugger focus
+/// is assumed. See #validate_device_parameter for information on how
+/// device parametersare interpreted.
+/// \param [in] The parameter string.
+/// \param [in] spacenum The default address space index. If negative,
+/// the first address space exposed by the device (i.e. the address
+/// space with the lowest index) will be used as the default.
+/// \param [out] space The address space on success, or unchanged on
+/// failure.
+/// \param [out] addr The address on success, or unchanged on failure.
+/// \return true if the address is a valid expression evaluating to a
+/// number and the address space is found, or false otherwise.
+bool debugger_console::validate_target_address_parameter(std::string_view param, int spacenum, address_space *&space, u64 &addr)
+{
+ // check for the device delimiter
+ std::string_view::size_type const devdelim = find_delimiter(param, [] (char ch) { return ':' == ch; });
+ std::string_view device;
+ if (devdelim != std::string::npos)
+ device = param.substr(devdelim + 1);
+
+ // parse the address first
+ u64 addrval;
+ if (!validate_number_parameter(param.substr(0, devdelim), addrval))
+ return false;
+
+ // find the address space
+ if (!validate_device_space_parameter(device, spacenum, space))
+ return false;
+
+ // set the address now that we have the space
+ addr = addrval;
+ return true;
+}
+
+
+/// \brief Validate a parameter as a memory region
+///
+/// Validates a parameter as a memory region tag and retrieves the
+/// specified memory region.
+/// \param [in] The parameter string.
+/// \param [out] result The memory region on success, or unchanged on
+/// failure.
+/// \return true if the parameter refers to a memory region in the
+/// current system, or false otherwise.
+bool debugger_console::validate_memory_region_parameter(std::string_view param, memory_region *&result)
+{
+ auto const &regions = m_machine.memory().regions();
+ std::string_view relative = param;
+ device_t &base = get_device_search_base(relative);
+ auto const iter = regions.find(base.subtag(strmakelower(relative)));
+ if (regions.end() != iter)
+ {
+ result = iter->second.get();
+ return true;
+ }
+ else
+ {
+ printf("No matching memory region found for '%s'\n", param);
+ return false;
+ }
+}
+
+
+/// \brief Get search base for device or address space parameter
+///
+/// Handles prefix prefixes used to indicate that a device tag should be
+/// interpreted relative to the selected CPU. Removes the recognised
+/// prefixes from the parameter value.
+/// \param [in,out] param The parameter string. Recognised prefixes
+/// affecting the search base are removed, leaving a tag relative to
+/// the base device.
+/// \return A reference to the base device that the tag should be
+/// interpreted relative to.
+device_t &debugger_console::get_device_search_base(std::string_view &param) const
+{
+ if (!param.empty())
+ {
+ // handle ".:" or ".^" prefix for tag relative to current CPU if any
+ if (('.' == param[0]) && ((param.size() == 1) || (':' == param[1]) || ('^' == param[1])))
+ {
+ param.remove_prefix(((param.size() > 1) && (':' == param[1])) ? 2 : 1);
+ device_t *const current = m_visiblecpu;
+ return current ? *current : m_machine.root_device();
+ }
+
+ // a sibling path makes most sense relative to current CPU
+ if ('^' == param[0])
+ {
+ device_t *const current = m_visiblecpu;
+ return current ? *current : m_machine.root_device();
+ }
+ }
+
+ // default to root device
+ return m_machine.root_device();
+}
+
+
+/// \brief Get CPU by index
+///
+/// Looks up a CPU by the number the debugger assigns it based on its
+/// position in the device tree relative to other CPUs.
+/// \param [in] cpunum Zero-based index of the CPU to find.
+/// \return A pointer to the CPU if found, or \c nullptr if no CPU has
+/// the specified index.
+device_t *debugger_console::get_cpu_by_index(u64 cpunum) const
+{
+ unsigned index = 0;
+ for (device_execute_interface &exec : execute_interface_enumerator(m_machine.root_device()))
+ {
+ // real CPUs should have pcbase
+ device_state_interface const *state;
+ if (exec.device().interface(state) && state->state_find_entry(STATE_GENPCBASE))
+ {
+ if (index++ == cpunum)
+ {
+ return &exec.device();
+ }
+ }
+ }
+ return nullptr;
+}
+
+
+//-------------------------------------------------
+// validate_expression_parameter - validates
+// an expression parameter
+//-----------------------------------------------*/
+
+bool debugger_console::validate_expression_parameter(std::string_view param, parsed_expression &result)
+{
+ try
+ {
+ // parse the expression; success if no error
+ result.parse(param);
+ return true;
+ }
+ catch (expression_error const &err)
+ {
+ // output an error
+ printf("Error in expression: %s\n", param);
+ printf(" %*s^", err.offset(), "");
+ printf("%s\n", err.code_string());
+ return false;
+ }
+}
+
+
+//-------------------------------------------------
+// validate_command_parameter - validates a
+// command parameter
+//-------------------------------------------------
+
+bool debugger_console::validate_command_parameter(std::string_view param)
+{
+ // validate the comment; success if no error
+ CMDERR err = validate_command(param);
+ if (err.error_class() == CMDERR::NONE)
+ return true;
+
+ // output an error
+ printf("Error in command: %s\n", param);
+ printf(" %*s^", err.error_offset(), "");
+ printf("%s\n", cmderr_to_string(err));
+ return false;
+}
+
/***************************************************************************
@@ -522,62 +1055,86 @@ std::string debugger_console::cmderr_to_string(CMDERR error)
***************************************************************************/
-/*-------------------------------------------------
- vprintf - vprintfs the given arguments using
- the format to the debug console
--------------------------------------------------*/
-void debugger_console::vprintf(util::format_argument_pack<std::ostream> const &args)
+//-------------------------------------------------
+// print_core - write preformatted text
+// to the debug console
+//-------------------------------------------------
+
+void debugger_console::print_core(std::string_view text)
+{
+ text_buffer_print(*m_console_textbuf, text);
+ if (m_logfile)
+ m_logfile->write(text.data(), text.length());
+}
+
+//-------------------------------------------------
+// print_core_wrap - write preformatted text
+// to the debug console, with wrapping
+//-------------------------------------------------
+
+void debugger_console::print_core_wrap(std::string_view text, int wrapcol)
{
- text_buffer_print(m_console_textbuf, util::string_format(args).c_str());
+ // FIXME: look into honoring wrapcol for the logfile
+ text_buffer_print_wrap(*m_console_textbuf, text, wrapcol);
+ if (m_logfile)
+ m_logfile->write(text.data(), text.length());
+}
- /* force an update of any console views */
+//-------------------------------------------------
+// vprintf - vprintfs the given arguments using
+// the format to the debug console
+//-------------------------------------------------
+
+void debugger_console::vprintf(util::format_argument_pack<char> const &args)
+{
+ print_core(util::string_format(args));
+
+ // force an update of any console views
m_machine.debug_view().update_all(DVT_CONSOLE);
}
-void debugger_console::vprintf(util::format_argument_pack<std::ostream> &&args)
+void debugger_console::vprintf(util::format_argument_pack<char> &&args)
{
- text_buffer_print(m_console_textbuf, util::string_format(std::move(args)).c_str());
+ print_core(util::string_format(std::move(args)));
- /* force an update of any console views */
+ // force an update of any console views
m_machine.debug_view().update_all(DVT_CONSOLE);
}
-/*-------------------------------------------------
- vprintf_wrap - vprintfs the given arguments
- using the format to the debug console
--------------------------------------------------*/
+//-------------------------------------------------
+// vprintf_wrap - vprintfs the given arguments
+// using the format to the debug console
+//-------------------------------------------------
-void debugger_console::vprintf_wrap(int wrapcol, util::format_argument_pack<std::ostream> const &args)
+void debugger_console::vprintf_wrap(int wrapcol, util::format_argument_pack<char> const &args)
{
- text_buffer_print_wrap(m_console_textbuf, util::string_format(args).c_str(), wrapcol);
+ print_core_wrap(util::string_format(args), wrapcol);
- /* force an update of any console views */
+ // force an update of any console views
m_machine.debug_view().update_all(DVT_CONSOLE);
}
-void debugger_console::vprintf_wrap(int wrapcol, util::format_argument_pack<std::ostream> &&args)
+void debugger_console::vprintf_wrap(int wrapcol, util::format_argument_pack<char> &&args)
{
- text_buffer_print_wrap(m_console_textbuf, util::string_format(std::move(args)).c_str(), wrapcol);
+ print_core_wrap(util::string_format(std::move(args)), wrapcol);
- /* force an update of any console views */
+ // force an update of any console views
m_machine.debug_view().update_all(DVT_CONSOLE);
}
-/*-------------------------------------------------
- errorlog_write_line - writes a line to the
- errorlog ring buffer
--------------------------------------------------*/
+//-------------------------------------------------
+// errorlog_write_line - writes a line to the
+// errorlog ring buffer
+//-------------------------------------------------
void debugger_console::errorlog_write_line(const char *line)
{
if (m_errorlog_textbuf)
- {
- text_buffer_print(m_errorlog_textbuf, line);
- }
+ text_buffer_print(*m_errorlog_textbuf, line);
- /* force an update of any log views */
+ // force an update of any log views
m_machine.debug_view().update_all(DVT_LOG);
}