summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/debug/debugcon.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/debug/debugcon.c')
-rw-r--r--trunk/src/emu/debug/debugcon.c568
1 files changed, 568 insertions, 0 deletions
diff --git a/trunk/src/emu/debug/debugcon.c b/trunk/src/emu/debug/debugcon.c
new file mode 100644
index 00000000000..6cb821ad810
--- /dev/null
+++ b/trunk/src/emu/debug/debugcon.c
@@ -0,0 +1,568 @@
+/*********************************************************************
+
+ debugcon.c
+
+ Debugger console engine.
+
+ Copyright Nicola Salmoria and the MAME Team.
+ Visit http://mamedev.org for licensing and usage restrictions.
+
+*********************************************************************/
+
+#include "emu.h"
+#include "debugcon.h"
+#include "debugcpu.h"
+#include "debughlp.h"
+#include "debugvw.h"
+#include "textbuf.h"
+#include "debugger.h"
+#include <ctype.h>
+
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+#define CONSOLE_BUF_SIZE (1024 * 1024)
+#define CONSOLE_MAX_LINES (CONSOLE_BUF_SIZE / 20)
+
+#define ERRORLOG_BUF_SIZE (1024 * 1024)
+#define ERRORLOG_MAX_LINES (ERRORLOG_BUF_SIZE / 20)
+
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+typedef struct _debug_command debug_command;
+struct _debug_command
+{
+ debug_command * next;
+ char command[32];
+ const char * params;
+ const char * help;
+ void (*handler)(running_machine &machine, int ref, int params, const char **param);
+ void (*handler_ex)(int ref);
+ UINT32 flags;
+ int ref;
+ int minparams;
+ int maxparams;
+};
+
+
+
+/***************************************************************************
+ LOCAL VARIABLES
+***************************************************************************/
+
+static text_buffer *console_textbuf;
+static text_buffer *errorlog_textbuf;
+
+static debug_command *commandlist;
+
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+static void debug_console_exit(running_machine &machine);
+
+
+
+/***************************************************************************
+
+ Initialization and tear down
+
+***************************************************************************/
+
+/*-------------------------------------------------
+ debug_console_init - initializes the console
+ system
+-------------------------------------------------*/
+
+void debug_console_init(running_machine &machine)
+{
+ /* allocate text buffers */
+ console_textbuf = text_buffer_alloc(CONSOLE_BUF_SIZE, CONSOLE_MAX_LINES);
+ if (!console_textbuf)
+ return;
+
+ errorlog_textbuf = text_buffer_alloc(ERRORLOG_BUF_SIZE, ERRORLOG_MAX_LINES);
+ if (!errorlog_textbuf)
+ return;
+
+ /* print the opening lines */
+ debug_console_printf(machine, "MAME new debugger version %s\n", build_version);
+ debug_console_printf(machine, "Currently targeting %s (%s)\n", machine.system().name, machine.system().description);
+
+ /* request callback upon exiting */
+ machine.add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(FUNC(debug_console_exit), &machine));
+}
+
+
+/*-------------------------------------------------
+ debug_console_exit - frees the console
+ system
+-------------------------------------------------*/
+
+static void debug_console_exit(running_machine &machine)
+{
+ /* free allocated memory */
+ if (console_textbuf)
+ text_buffer_free(console_textbuf);
+ console_textbuf = NULL;
+
+ if (errorlog_textbuf)
+ text_buffer_free(errorlog_textbuf);
+ errorlog_textbuf = NULL;
+
+ /* free the command list */
+ commandlist = NULL;
+}
+
+
+
+/***************************************************************************
+
+ Command Handling
+
+***************************************************************************/
+
+/*-------------------------------------------------
+ trim_parameter - executes a
+ command
+-------------------------------------------------*/
+
+static void trim_parameter(char **paramptr, int keep_quotes)
+{
+ char *param = *paramptr;
+ size_t len = strlen(param);
+ int repeat;
+
+ /* loop until all adornments are gone */
+ do
+ {
+ repeat = 0;
+
+ /* check for begin/end quotes */
+ if (len >= 2 && param[0] == '"' && param[len - 1] == '"')
+ {
+ if (!keep_quotes)
+ {
+ param[len - 1] = 0;
+ param++;
+ len -= 2;
+ }
+ }
+
+ /* check for start/end braces */
+ else if (len >= 2 && param[0] == '{' && param[len - 1] == '}')
+ {
+ param[len - 1] = 0;
+ param++;
+ len -= 2;
+ repeat = 1;
+ }
+
+ /* check for leading spaces */
+ else if (len >= 1 && param[0] == ' ')
+ {
+ param++;
+ len--;
+ repeat = 1;
+ }
+
+ /* check for trailing spaces */
+ else if (len >= 1 && param[len - 1] == ' ')
+ {
+ param[len - 1] = 0;
+ len--;
+ repeat = 1;
+ }
+ } while (repeat);
+
+ *paramptr = param;
+}
+
+
+/*-------------------------------------------------
+ internal_execute_command - executes a
+ command
+-------------------------------------------------*/
+
+static CMDERR internal_execute_command(running_machine &machine, int execute, int params, char **param)
+{
+ debug_command *cmd, *found = NULL;
+ int i, foundcount = 0;
+ char *p, *command;
+ size_t len;
+
+ /* no params is an error */
+ if (params == 0)
+ return CMDERR_NONE;
+
+ /* the first parameter has the command and the real first parameter; separate them */
+ for (p = param[0]; *p && isspace((UINT8)*p); p++) { }
+ for (command = p; *p && !isspace((UINT8)*p); p++) { }
+ if (*p != 0)
+ {
+ *p++ = 0;
+ for ( ; *p && isspace((UINT8)*p); p++) { }
+ if (*p != 0)
+ param[0] = p;
+ else
+ params = 0;
+ }
+ else
+ {
+ params = 0;
+ param[0] = NULL;
+ }
+
+ /* search the command list */
+ len = strlen(command);
+ for (cmd = commandlist; cmd != NULL; 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);
+
+ /* NULL-terminate and trim space around all the parameters */
+ for (i = 1; i < params; i++)
+ *param[i]++ = 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 */
+ if (execute)
+ (*found->handler)(machine, found->ref, params, (const char **)param);
+ return CMDERR_NONE;
+}
+
+
+/*-------------------------------------------------
+ internal_parse_command - parses a command
+ and either executes or just validates it
+-------------------------------------------------*/
+
+static CMDERR internal_parse_command(running_machine &machine, const char *original_command, int execute)
+{
+ char command[MAX_COMMAND_LENGTH], parens[MAX_COMMAND_LENGTH];
+ char *params[MAX_COMMAND_PARAMS] = { 0 };
+ CMDERR result = CMDERR_NONE;
+ char *command_start;
+ char *p, c = 0;
+
+ /* make a copy of the command */
+ strcpy(command, original_command);
+
+ /* loop over all semicolon-separated stuff */
+ for (p = command; *p != 0; )
+ {
+ int paramcount = 0, foundend = FALSE, instring = FALSE, isexpr = FALSE, parendex = 0;
+
+ /* find a semicolon or the end */
+ for (params[paramcount++] = p; !foundend; p++)
+ {
+ c = *p;
+ if (instring)
+ {
+ if (c == '"' && p[-1] != '\\')
+ instring = FALSE;
+ }
+ else
+ {
+ switch (c)
+ {
+ 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((UINT8)c); break;
+ }
+ }
+ }
+
+ /* 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;
+
+ /* process the command */
+ command_start = params[0];
+
+ /* allow for "do" commands */
+ if (tolower((UINT8)command_start[0] == 'd') && tolower((UINT8)command_start[1] == 'o') && isspace((UINT8)command_start[2]))
+ {
+ isexpr = TRUE;
+ command_start += 3;
+ }
+
+ /* if it smells like an assignment expression, treat it as such */
+ if (isexpr && paramcount == 1)
+ {
+ try
+ {
+ UINT64 expresult;
+ parsed_expression expression(debug_cpu_get_visible_symtable(machine), command_start, &expresult);
+ }
+ catch (expression_error &err)
+ {
+ return MAKE_CMDERR_EXPRESSION_ERROR(err);
+ }
+ }
+ else
+ {
+ result = internal_execute_command(machine, execute, paramcount, &params[0]);
+ if (result != CMDERR_NONE)
+ return MAKE_CMDERR(CMDERR_ERROR_CLASS(result), command_start - command);
+ }
+ }
+ return CMDERR_NONE;
+}
+
+
+/*-------------------------------------------------
+ debug_console_execute_command - execute a
+ command string
+-------------------------------------------------*/
+
+CMDERR debug_console_execute_command(running_machine &machine, const char *command, int echo)
+{
+ CMDERR result;
+
+ /* echo if requested */
+ if (echo)
+ debug_console_printf(machine, ">%s\n", command);
+
+ /* parse and execute */
+ result = internal_parse_command(machine, command, TRUE);
+
+ /* display errors */
+ if (result != CMDERR_NONE)
+ {
+ if (!echo)
+ debug_console_printf(machine, ">%s\n", command);
+ debug_console_printf(machine, " %*s^\n", CMDERR_ERROR_OFFSET(result), "");
+ debug_console_printf(machine, "%s\n", debug_cmderr_to_string(result));
+ }
+
+ /* update all views */
+ if (echo)
+ {
+ machine.debug_view().update_all();
+ debugger_refresh_display(machine);
+ }
+ return result;
+}
+
+
+/*-------------------------------------------------
+ debug_console_validate_command - validate a
+ command string
+-------------------------------------------------*/
+
+CMDERR debug_console_validate_command(running_machine &machine, const char *command)
+{
+ return internal_parse_command(machine, command, FALSE);
+}
+
+
+/*-------------------------------------------------
+ debug_console_register_command - register a
+ command handler
+-------------------------------------------------*/
+
+void debug_console_register_command(running_machine &machine, const char *command, UINT32 flags, int ref, int minparams, int maxparams, void (*handler)(running_machine &machine, int ref, int params, const char **param))
+{
+ debug_command *cmd;
+
+ assert_always(machine.phase() == MACHINE_PHASE_INIT, "Can only call debug_console_register_command() at init time!");
+ assert_always((machine.debug_flags & DEBUG_FLAG_ENABLED) != 0, "Cannot call debug_console_register_command() when debugger is not running");
+
+ cmd = auto_alloc_clear(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 = commandlist;
+ commandlist = cmd;
+}
+
+
+
+/***************************************************************************
+
+ Error Handling
+
+***************************************************************************/
+
+/*-------------------------------------------------
+ debug_cmderr_to_string - return a friendly
+ string for a given command error
+-------------------------------------------------*/
+
+const char *debug_cmderr_to_string(CMDERR error)
+{
+ switch (CMDERR_ERROR_CLASS(error))
+ {
+ 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 "error in assignment expression";
+ default: return "unknown error";
+ }
+}
+
+
+
+/***************************************************************************
+
+ Console Management
+
+***************************************************************************/
+
+/*-------------------------------------------------
+ debug_console_printf - printfs the given
+ arguments using the format to the debug
+ console
+-------------------------------------------------*/
+
+void CLIB_DECL debug_console_printf(running_machine &machine, const char *format, ...)
+{
+ astring buffer;
+ va_list arg;
+
+ va_start(arg, format);
+ buffer.vprintf(format, arg);
+ va_end(arg);
+
+ text_buffer_print(console_textbuf, buffer);
+
+ /* force an update of any console views */
+ machine.debug_view().update_all(DVT_CONSOLE);
+}
+
+
+/*-------------------------------------------------
+ debug_console_vprintf - printfs the given
+ arguments using the format to the debug
+ console
+-------------------------------------------------*/
+
+void CLIB_DECL debug_console_vprintf(running_machine &machine, const char *format, va_list args)
+{
+ astring buffer;
+
+ buffer.vprintf(format, args);
+ text_buffer_print(console_textbuf, buffer);
+
+ /* force an update of any console views */
+ machine.debug_view().update_all(DVT_CONSOLE);
+}
+
+
+/*-------------------------------------------------
+ debug_console_printf_wrap - printfs the given
+ arguments using the format to the debug
+ console
+-------------------------------------------------*/
+
+void CLIB_DECL debug_console_printf_wrap(running_machine &machine, int wrapcol, const char *format, ...)
+{
+ astring buffer;
+ va_list arg;
+
+ va_start(arg, format);
+ buffer.vprintf(format, arg);
+ va_end(arg);
+
+ text_buffer_print_wrap(console_textbuf, buffer, wrapcol);
+
+ /* force an update of any console views */
+ machine.debug_view().update_all(DVT_CONSOLE);
+}
+
+
+/*-------------------------------------------------
+ debug_console_get_textbuf - return a pointer
+ to the console text buffer
+-------------------------------------------------*/
+
+text_buffer *debug_console_get_textbuf(void)
+{
+ return console_textbuf;
+}
+
+
+/*-------------------------------------------------
+ debug_errorlog_write_line - writes a line to
+ the errorlog ring buffer
+-------------------------------------------------*/
+
+void debug_errorlog_write_line(running_machine &machine, const char *line)
+{
+ if (errorlog_textbuf)
+ text_buffer_print(errorlog_textbuf, line);
+
+ /* force an update of any log views */
+ machine.debug_view().update_all(DVT_LOG);
+}
+
+
+/*-------------------------------------------------
+ debug_errorlog_get_textbuf - return a pointer
+ to the errorlog text buffer
+-------------------------------------------------*/
+
+text_buffer *debug_errorlog_get_textbuf(void)
+{
+ return errorlog_textbuf;
+}
+