mame/src/emu/debug/debugcon.c

569 lines
15 KiB
C

/*********************************************************************
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->gamedrv->name, machine->gamedrv->description);
/* request callback upon exiting */
machine->add_notifier(MACHINE_NOTIFY_EXIT, debug_console_exit);
}
/*-------------------------------------------------
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;
}