/*********************************************************************

    debugcmd.h

    Debugger command interface engine.

    Copyright (c) 1996-2006, Nicola Salmoria and the MAME Team.
    Visit http://mamedev.org for licensing and usage restrictions.

*********************************************************************/

#include "driver.h"
#include "debugcmd.h"
#include "debugcmt.h"
#include "debugcon.h"
#include "debugcpu.h"
#include "express.h"
#include "debughlp.h"
#include "debugvw.h"
#include "render.h"
#include <stdarg.h>
#include <ctype.h>



/***************************************************************************
    CONSTANTS
***************************************************************************/

#define MAX_GLOBALS		1000



/***************************************************************************
    GLOBAL VARIABLES
***************************************************************************/

static struct
{
	void *		base;
	UINT32		size;
} global_array[MAX_GLOBALS];



/***************************************************************************
    FUNCTION PROTOTYPES
***************************************************************************/

static void debug_command_exit(running_machine *machine);

static UINT64 execute_min(UINT32 ref, UINT32 params, UINT64 *param);
static UINT64 execute_max(UINT32 ref, UINT32 params, UINT64 *param);
static UINT64 execute_if(UINT32 ref, UINT32 params, UINT64 *param);

static UINT64 global_get(UINT32 ref);
static void global_set(UINT32 ref, UINT64 value);

static void execute_help(int ref, int params, const char **param);
static void execute_print(int ref, int params, const char **param);
static void execute_printf(int ref, int params, const char **param);
static void execute_logerror(int ref, int params, const char **param);
static void execute_tracelog(int ref, int params, const char **param);
static void execute_quit(int ref, int params, const char **param);
static void execute_do(int ref, int params, const char **param);
static void execute_step(int ref, int params, const char **param);
static void execute_over(int ref, int params, const char **param);
static void execute_out(int ref, int params, const char **param);
static void execute_go(int ref, int params, const char **param);
static void execute_go_vblank(int ref, int params, const char **param);
static void execute_go_interrupt(int ref, int params, const char **param);
static void execute_go_time(int ref, int params, const char *param[]);
static void execute_focus(int ref, int params, const char **param);
static void execute_ignore(int ref, int params, const char **param);
static void execute_observe(int ref, int params, const char **param);
static void execute_next(int ref, int params, const char **param);
static void execute_comment(int ref, int params, const char **param);
static void execute_comment_del(int ref, int params, const char **param);
static void execute_comment_save(int ref, int params, const char **param);
static void execute_bpset(int ref, int params, const char **param);
static void execute_bpclear(int ref, int params, const char **param);
static void execute_bpdisenable(int ref, int params, const char **param);
static void execute_bplist(int ref, int params, const char **param);
static void execute_wpset(int ref, int params, const char **param);
static void execute_wpclear(int ref, int params, const char **param);
static void execute_wpdisenable(int ref, int params, const char **param);
static void execute_wplist(int ref, int params, const char **param);
static void execute_hotspot(int ref, int params, const char **param);
static void execute_save(int ref, int params, const char **param);
static void execute_dump(int ref, int params, const char **param);
static void execute_dasm(int ref, int params, const char **param);
static void execute_find(int ref, int params, const char **param);
static void execute_trace(int ref, int params, const char **param);
static void execute_traceover(int ref, int params, const char **param);
static void execute_traceflush(int ref, int params, const char **param);
static void execute_snap(int ref, int params, const char **param);
static void execute_source(int ref, int params, const char **param);
static void execute_map(int ref, int params, const char **param);
static void execute_memdump(int ref, int params, const char **param);
static void execute_symlist(int ref, int params, const char **param);



/***************************************************************************

    Initialization

***************************************************************************/

/*-------------------------------------------------
    debug_command_init - initializes the command
    system
-------------------------------------------------*/

void debug_command_init(running_machine *machine)
{
	int cpunum, itemnum;
	const char *name;

	/* add a few simple global functions */
	symtable_add_function(global_symtable, "min", 0, 2, 2, execute_min);
	symtable_add_function(global_symtable, "max", 0, 2, 2, execute_max);
	symtable_add_function(global_symtable, "if", 0, 3, 3, execute_if);

	/* add all single-entry save state globals */
	for (itemnum = 0; itemnum < MAX_GLOBALS; itemnum++)
	{
		UINT32 valsize, valcount;
		void *base;

		/* stop when we run out of items */
		name = state_save_get_indexed_item(itemnum, &base, &valsize, &valcount);
		if (name == NULL)
			break;

		/* if this is a single-entry global, add it */
		if (valcount == 1 && strstr(name, "/globals/"))
		{
			char symname[100];
			sprintf(symname, ".%s", strrchr(name, '/') + 1);
			global_array[itemnum].base = base;
			global_array[itemnum].size = valsize;
			symtable_add_register(global_symtable, symname, itemnum, global_get, global_set);
		}
	}

	/* add all the commands */
	debug_console_register_command("help",      CMDFLAG_NONE, 0, 0, 1, execute_help);
	debug_console_register_command("print",     CMDFLAG_NONE, 0, 1, MAX_COMMAND_PARAMS, execute_print);
	debug_console_register_command("printf",    CMDFLAG_NONE, 0, 1, MAX_COMMAND_PARAMS, execute_printf);
	debug_console_register_command("logerror",  CMDFLAG_NONE, 0, 1, MAX_COMMAND_PARAMS, execute_logerror);
	debug_console_register_command("tracelog",  CMDFLAG_NONE, 0, 1, MAX_COMMAND_PARAMS, execute_tracelog);
	debug_console_register_command("quit",      CMDFLAG_NONE, 0, 0, 0, execute_quit);
	debug_console_register_command("do",        CMDFLAG_NONE, 0, 1, 1, execute_do);
	debug_console_register_command("step",      CMDFLAG_NONE, 0, 0, 1, execute_step);
	debug_console_register_command("s",         CMDFLAG_NONE, 0, 0, 1, execute_step);
	debug_console_register_command("over",      CMDFLAG_NONE, 0, 0, 1, execute_over);
	debug_console_register_command("o",         CMDFLAG_NONE, 0, 0, 1, execute_over);
	debug_console_register_command("out" ,      CMDFLAG_NONE, 0, 0, 0, execute_out);
	debug_console_register_command("go",        CMDFLAG_NONE, 0, 0, 1, execute_go);
	debug_console_register_command("g",         CMDFLAG_NONE, 0, 0, 1, execute_go);
	debug_console_register_command("gvblank",   CMDFLAG_NONE, 0, 0, 0, execute_go_vblank);
	debug_console_register_command("gv",        CMDFLAG_NONE, 0, 0, 0, execute_go_vblank);
	debug_console_register_command("gint",      CMDFLAG_NONE, 0, 0, 1, execute_go_interrupt);
	debug_console_register_command("gi",        CMDFLAG_NONE, 0, 0, 1, execute_go_interrupt);
	debug_console_register_command("gtime",     CMDFLAG_NONE, 0, 0, 1, execute_go_time);
	debug_console_register_command("gt",        CMDFLAG_NONE, 0, 0, 1, execute_go_time);
	debug_console_register_command("next",      CMDFLAG_NONE, 0, 0, 0, execute_next);
	debug_console_register_command("n",         CMDFLAG_NONE, 0, 0, 0, execute_next);
	debug_console_register_command("focus",     CMDFLAG_NONE, 0, 1, 1, execute_focus);
	debug_console_register_command("ignore",    CMDFLAG_NONE, 0, 0, MAX_COMMAND_PARAMS, execute_ignore);
	debug_console_register_command("observe",   CMDFLAG_NONE, 0, 0, MAX_COMMAND_PARAMS, execute_observe);

	debug_console_register_command("comadd",	CMDFLAG_NONE, 0, 1, 2, execute_comment);
	debug_console_register_command("//",        CMDFLAG_NONE, 0, 1, 2, execute_comment);
	debug_console_register_command("comdelete",	CMDFLAG_NONE, 0, 1, 1, execute_comment_del);
	debug_console_register_command("comsave", 	CMDFLAG_NONE, 0, 0, 0, execute_comment_save);

	debug_console_register_command("bpset",     CMDFLAG_NONE, 0, 1, 3, execute_bpset);
	debug_console_register_command("bp",        CMDFLAG_NONE, 0, 1, 3, execute_bpset);
	debug_console_register_command("bpclear",   CMDFLAG_NONE, 0, 0, 1, execute_bpclear);
	debug_console_register_command("bpdisable", CMDFLAG_NONE, 0, 0, 1, execute_bpdisenable);
	debug_console_register_command("bpenable",  CMDFLAG_NONE, 1, 0, 1, execute_bpdisenable);
	debug_console_register_command("bplist",    CMDFLAG_NONE, 0, 0, 0, execute_bplist);

	debug_console_register_command("wpset",     CMDFLAG_NONE, ADDRESS_SPACE_PROGRAM, 3, 5, execute_wpset);
	debug_console_register_command("wp",        CMDFLAG_NONE, ADDRESS_SPACE_PROGRAM, 3, 5, execute_wpset);
	debug_console_register_command("wpdset",    CMDFLAG_NONE, ADDRESS_SPACE_DATA, 3, 5, execute_wpset);
	debug_console_register_command("wpd",       CMDFLAG_NONE, ADDRESS_SPACE_DATA, 3, 5, execute_wpset);
	debug_console_register_command("wpiset",    CMDFLAG_NONE, ADDRESS_SPACE_IO, 3, 5, execute_wpset);
	debug_console_register_command("wpi",       CMDFLAG_NONE, ADDRESS_SPACE_IO, 3, 5, execute_wpset);
	debug_console_register_command("wpclear",   CMDFLAG_NONE, 0, 0, 1, execute_wpclear);
	debug_console_register_command("wpdisable", CMDFLAG_NONE, 0, 0, 1, execute_wpdisenable);
	debug_console_register_command("wpenable",  CMDFLAG_NONE, 1, 0, 1, execute_wpdisenable);
	debug_console_register_command("wplist",    CMDFLAG_NONE, 0, 0, 0, execute_wplist);

	debug_console_register_command("hotspot",   CMDFLAG_NONE, 0, 0, 3, execute_hotspot);

	debug_console_register_command("save",      CMDFLAG_NONE, ADDRESS_SPACE_PROGRAM, 3, 4, execute_save);
	debug_console_register_command("saved",     CMDFLAG_NONE, ADDRESS_SPACE_DATA, 3, 4, execute_save);
	debug_console_register_command("savei",     CMDFLAG_NONE, ADDRESS_SPACE_IO, 3, 4, execute_save);

	debug_console_register_command("dump",      CMDFLAG_NONE, ADDRESS_SPACE_PROGRAM, 3, 6, execute_dump);
	debug_console_register_command("dumpd",     CMDFLAG_NONE, ADDRESS_SPACE_DATA, 3, 6, execute_dump);
	debug_console_register_command("dumpi",     CMDFLAG_NONE, ADDRESS_SPACE_IO, 3, 6, execute_dump);

	debug_console_register_command("f",         CMDFLAG_KEEP_QUOTES, ADDRESS_SPACE_PROGRAM, 3, MAX_COMMAND_PARAMS, execute_find);
	debug_console_register_command("find",      CMDFLAG_KEEP_QUOTES, ADDRESS_SPACE_PROGRAM, 3, MAX_COMMAND_PARAMS, execute_find);
	debug_console_register_command("fd",        CMDFLAG_KEEP_QUOTES, ADDRESS_SPACE_DATA, 3, MAX_COMMAND_PARAMS, execute_find);
	debug_console_register_command("findd",     CMDFLAG_KEEP_QUOTES, ADDRESS_SPACE_DATA, 3, MAX_COMMAND_PARAMS, execute_find);
	debug_console_register_command("fi",        CMDFLAG_KEEP_QUOTES, ADDRESS_SPACE_IO, 3, MAX_COMMAND_PARAMS, execute_find);
	debug_console_register_command("findi",     CMDFLAG_KEEP_QUOTES, ADDRESS_SPACE_IO, 3, MAX_COMMAND_PARAMS, execute_find);

	debug_console_register_command("dasm",      CMDFLAG_NONE, 0, 3, 5, execute_dasm);

	debug_console_register_command("trace",     CMDFLAG_NONE, 0, 1, 3, execute_trace);
	debug_console_register_command("traceover", CMDFLAG_NONE, 0, 1, 3, execute_traceover);
	debug_console_register_command("traceflush",CMDFLAG_NONE, 0, 0, 0, execute_traceflush);

	debug_console_register_command("snap",      CMDFLAG_NONE, 0, 0, 1, execute_snap);

	debug_console_register_command("source",    CMDFLAG_NONE, 0, 1, 1, execute_source);

	debug_console_register_command("map",		CMDFLAG_NONE, ADDRESS_SPACE_PROGRAM, 1, 1, execute_map);
	debug_console_register_command("mapd",		CMDFLAG_NONE, ADDRESS_SPACE_DATA, 1, 1, execute_map);
	debug_console_register_command("mapi",		CMDFLAG_NONE, ADDRESS_SPACE_IO, 1, 1, execute_map);
	debug_console_register_command("memdump",	CMDFLAG_NONE, 0, 0, 1, execute_memdump);

	debug_console_register_command("symlist",	CMDFLAG_NONE, 0, 0, 1, execute_symlist);

	/* ask all the CPUs if they would like to register functions or symbols */
	for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++)
	{
		void (*setup_commands)(void);
		setup_commands = cpunum_get_info_fct(cpunum, CPUINFO_PTR_DEBUG_SETUP_COMMANDS);
		if (setup_commands)
			setup_commands();
	}

	add_exit_callback(machine, debug_command_exit);
}


/*-------------------------------------------------
    debug_command_exit - exit-time cleanup
-------------------------------------------------*/

static void debug_command_exit(running_machine *machine)
{
	int cpunum;

	/* turn off all traces */
	for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++)
		debug_cpu_trace(cpunum, NULL, 0, NULL);
}



/***************************************************************************

    Simple Global Functions

***************************************************************************/

/*-------------------------------------------------
    execute_min - return the minimum of two values
-------------------------------------------------*/

static UINT64 execute_min(UINT32 ref, UINT32 params, UINT64 *param)
{
	return (param[0] < param[1]) ? param[0] : param[1];
}


/*-------------------------------------------------
    execute_max - return the maximum of two values
-------------------------------------------------*/

static UINT64 execute_max(UINT32 ref, UINT32 params, UINT64 *param)
{
	return (param[0] > param[1]) ? param[0] : param[1];
}


/*-------------------------------------------------
    execute_if - if (a) return b; else return c;
-------------------------------------------------*/

static UINT64 execute_if(UINT32 ref, UINT32 params, UINT64 *param)
{
	return param[0] ? param[1] : param[2];
}



/***************************************************************************

    Global accessors

***************************************************************************/

/*-------------------------------------------------
    global_get - symbol table getter for globals
-------------------------------------------------*/

static UINT64 global_get(UINT32 ref)
{
	assert(ref < MAX_GLOBALS);
	switch (global_array[ref].size)
	{
		case 1:		return *(UINT8 *)global_array[ref].base;
		case 2:		return *(UINT16 *)global_array[ref].base;
		case 4:		return *(UINT32 *)global_array[ref].base;
		case 8:		return *(UINT64 *)global_array[ref].base;
	}
	return ~0;
}


/*-------------------------------------------------
    global_set - symbol table setter for globals
-------------------------------------------------*/

static void global_set(UINT32 ref, UINT64 value)
{
	assert(ref < MAX_GLOBALS);
	switch (global_array[ref].size)
	{
		case 1:		*(UINT8 *)global_array[ref].base = value;	break;
		case 2:		*(UINT16 *)global_array[ref].base = value;	break;
		case 4:		*(UINT32 *)global_array[ref].base = value;	break;
		case 8:		*(UINT64 *)global_array[ref].base = value;	break;
	}
}



/***************************************************************************

    Parameter Validation Helpers

***************************************************************************/

/*-------------------------------------------------
    validate_parameter_number - validates a
    number parameter
-------------------------------------------------*/

static int validate_parameter_number(const char *param, UINT64 *result)
{
	EXPRERR err = expression_evaluate(param, debug_get_cpu_info(cpu_getactivecpu())->symtable, result);
	if (err == EXPRERR_NONE)
		return 1;
	debug_console_printf("Error in expression: %s\n", param);
	debug_console_printf("                     %*s^", EXPRERR_ERROR_OFFSET(err), "");
	debug_console_printf("%s\n", exprerr_to_string(err));
	return 0;
}


/*-------------------------------------------------
    validate_parameter_expression - validates an
    expression parameter
-------------------------------------------------*/

static int validate_parameter_expression(const char *param, parsed_expression **result)
{
	EXPRERR err = expression_parse(param, debug_get_cpu_info(cpu_getactivecpu())->symtable, result);
	if (err == EXPRERR_NONE)
		return 1;
	debug_console_printf("Error in expression: %s\n", param);
	debug_console_printf("                     %*s^", EXPRERR_ERROR_OFFSET(err), "");
	debug_console_printf("%s\n", exprerr_to_string(err));
	return 0;
}


/*-------------------------------------------------
    validate_parameter_command - validates a
    command parameter
-------------------------------------------------*/

static int validate_parameter_command(const char *param)
{
	CMDERR err = debug_console_validate_command(param);
	if (err == CMDERR_NONE)
		return 1;
	debug_console_printf("Error in command: %s\n", param);
	debug_console_printf("                  %*s^", CMDERR_ERROR_OFFSET(err), "");
	debug_console_printf("%s\n", debug_cmderr_to_string(err));
	return 0;
}



/***************************************************************************

    Command Helpers

***************************************************************************/

/*-------------------------------------------------
    execute_help - execute the help command
-------------------------------------------------*/

static void execute_help(int ref, int params, const char *param[])
{
	if (params == 0)
		debug_console_printf_wrap(80, "%s\n", debug_get_help(""));
	else
		debug_console_printf_wrap(80, "%s\n", debug_get_help(param[0]));
}


/*-------------------------------------------------
    execute_print - execute the print command
-------------------------------------------------*/

static void execute_print(int ref, int params, const char *param[])
{
	UINT64 values[MAX_COMMAND_PARAMS];
	int i;

	/* validate the other parameters */
	for (i = 0; i < params; i++)
		if (!validate_parameter_number(param[i], &values[i]))
			return;

	/* then print each one */
	for (i = 0; i < params; i++)
	{
		if ((values[i] >> 32) != 0)
			debug_console_printf("%X%08X ", (UINT32)(values[i] >> 32), (UINT32)values[i]);
		else
			debug_console_printf("%X ", (UINT32)values[i]);
	}
	debug_console_printf("\n");
}


/*-------------------------------------------------
    mini_printf - safe printf to a buffer
-------------------------------------------------*/

static int mini_printf(char *buffer, const char *format, int params, UINT64 *param)
{
	const char *f = format;
	char *p = buffer;

	/* parse the string looking for % signs */
	for (;;)
	{
		char c = *f++;
		if (!c) break;

		/* escape sequences */
		if (c == '\\')
		{
			c = *f++;
			if (!c) break;
			switch (c)
			{
				case '\\':	*p++ = c;		break;
				case 'n':	*p++ = '\n';	break;
				default:					break;
			}
			continue;
		}

		/* formatting */
		else if (c == '%')
		{
			int width = 0;
			int zerofill = 0;

			/* parse out the width */
			for (;;)
			{
				c = *f++;
				if (!c || c < '0' || c > '9') break;
				if (c == '0' && width == 0)
					zerofill = 1;
				width = width * 10 + (c - '0');
			}
			if (!c) break;

			/* get the format */
			switch (c)
			{
				case '%':
					*p++ = c;
					break;

				case 'X':
				case 'x':
					if (params == 0)
					{
						debug_console_printf("Not enough parameters for format!\n");
						return 0;
					}
					if ((UINT32)(*param >> 32) != 0)
						p += sprintf(p, zerofill ? "%0*X" : "%*X", (width <= 8) ? 1 : width - 8, (UINT32)(*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, (UINT32)*param);
					param++;
					params--;
					break;

				case 'D':
				case 'd':
					if (params == 0)
					{
						debug_console_printf("Not enough parameters for format!\n");
						return 0;
					}
					p += sprintf(p, zerofill ? "%0*d" : "%*d", width, (UINT32)*param);
					param++;
					params--;
					break;
			}
		}

		/* normal stuff */
		else
			*p++ = c;
	}

	/* NULL-terminate and exit */
	*p = 0;
	return 1;
}


/*-------------------------------------------------
    execute_printf - execute the printf command
-------------------------------------------------*/

static void execute_printf(int ref, int params, const char *param[])
{
	UINT64 values[MAX_COMMAND_PARAMS];
	char buffer[1024];
	int i;

	/* validate the other parameters */
	for (i = 1; i < params; i++)
		if (!validate_parameter_number(param[i], &values[i]))
			return;

	/* then do a printf */
	if (mini_printf(buffer, param[0], params - 1, &values[1]))
		debug_console_printf("%s\n", buffer);
}


/*-------------------------------------------------
    execute_logerror - execute the logerror command
-------------------------------------------------*/

static void execute_logerror(int ref, int params, const char *param[])
{
	UINT64 values[MAX_COMMAND_PARAMS];
	char buffer[1024];
	int i;

	/* validate the other parameters */
	for (i = 1; i < params; i++)
		if (!validate_parameter_number(param[i], &values[i]))
			return;

	/* then do a printf */
	if (mini_printf(buffer, param[0], params - 1, &values[1]))
		logerror("%s", buffer);
}


/*-------------------------------------------------
    execute_tracelog - execute the tracelog command
-------------------------------------------------*/

static void execute_tracelog(int ref, int params, const char *param[])
{
	FILE *file = debug_get_cpu_info(cpu_getactivecpu())->trace.file;
	UINT64 values[MAX_COMMAND_PARAMS];
	char buffer[1024];
	int i;

	/* if no tracefile, skip */
	if (!file)
		return;

	/* validate the other parameters */
	for (i = 1; i < params; i++)
		if (!validate_parameter_number(param[i], &values[i]))
			return;

	/* then do a printf */
	if (mini_printf(buffer, param[0], params - 1, &values[1]))
		fprintf(file, "%s", buffer);
}


/*-------------------------------------------------
    execute_quit - execute the quit command
-------------------------------------------------*/

static void execute_quit(int ref, int params, const char *param[])
{
	fatalerror("Exited via the debugger");
}


/*-------------------------------------------------
    execute_do - execute the do command
-------------------------------------------------*/

static void execute_do(int ref, int params, const char *param[])
{
	UINT64 dummy;
	validate_parameter_number(param[0], &dummy);
}


/*-------------------------------------------------
    execute_step - execute the step command
-------------------------------------------------*/

static void execute_step(int ref, int params, const char *param[])
{
	UINT64 steps = 1;

	/* if we have a parameter, use it instead */
	if (params > 0 && !validate_parameter_number(param[0], &steps))
		return;

	debug_cpu_single_step(steps);
}


/*-------------------------------------------------
    execute_over - execute the over command
-------------------------------------------------*/

static void execute_over(int ref, int params, const char *param[])
{
	UINT64 steps = 1;

	/* if we have a parameter, use it instead */
	if (params > 0 && !validate_parameter_number(param[0], &steps))
		return;

	debug_cpu_single_step_over(steps);
}


/*-------------------------------------------------
    execute_out - execute the out command
-------------------------------------------------*/

static void execute_out(int ref, int params, const char *param[])
{
	debug_cpu_single_step_out();
}


/*-------------------------------------------------
    execute_go - execute the go command
-------------------------------------------------*/

static void execute_go(int ref, int params, const char *param[])
{
	UINT64 addr = ~0;

	/* if we have a parameter, use it instead */
	if (params > 0 && !validate_parameter_number(param[0], &addr))
		return;

	debug_cpu_go(addr);
}


/*-------------------------------------------------
    execute_go_vblank - execute the govblank
    command
-------------------------------------------------*/

static void execute_go_vblank(int ref, int params, const char *param[])
{
	debug_cpu_go_vblank();
}


/*-------------------------------------------------
    execute_go_interrupt - execute the goint command
-------------------------------------------------*/

static void execute_go_interrupt(int ref, int params, const char *param[])
{
	UINT64 irqline = -1;

	/* if we have a parameter, use it instead */
	if (params > 0 && !validate_parameter_number(param[0], &irqline))
		return;

	debug_cpu_go_interrupt(irqline);
}


/*-------------------------------------------------
    execute_go_time - execute the gtime command
-------------------------------------------------*/

static void execute_go_time(int ref, int params, const char *param[])
{
	UINT64 milliseconds = -1;

	/* if we have a parameter, use it instead */
	if (params > 0 && !validate_parameter_number(param[0], &milliseconds))
		return;

	debug_cpu_go_milliseconds(milliseconds);
}


/*-------------------------------------------------
    execute_next - execute the next command
-------------------------------------------------*/

static void execute_next(int ref, int params, const char *param[])
{
	debug_cpu_next_cpu();
}


/*-------------------------------------------------
    execute_focus - execute the focus command
-------------------------------------------------*/

static void execute_focus(int ref, int params, const char *param[])
{
	UINT64 cpuwhich;
	int cpunum;

	/* validate params */
	if (!validate_parameter_number(param[0], &cpuwhich))
		return;
	if (cpuwhich >= cpu_gettotalcpu())
	{
		debug_console_printf("Invalid CPU number!\n");
		return;
	}

	/* first clear the ignore flag on the focused CPU */
	debug_cpu_ignore_cpu(cpuwhich, 0);

	/* then loop over CPUs and set the ignore flags on all other CPUs */
	for (cpunum = 0; cpunum < MAX_CPU; cpunum++)
	{
		const debug_cpu_info *info = debug_get_cpu_info(cpunum);
		if (info && info->valid && cpunum != cpuwhich)
			debug_cpu_ignore_cpu(cpunum, 1);
	}
	debug_console_printf("Now focused on CPU %d\n", (int)cpuwhich);
}


/*-------------------------------------------------
    execute_ignore - execute the ignore command
-------------------------------------------------*/

static void execute_ignore(int ref, int params, const char *param[])
{
	UINT64 cpuwhich[MAX_COMMAND_PARAMS];
	int cpunum, paramnum;
	char buffer[100];
	int buflen = 0;

	/* if there are no parameters, dump the ignore list */
	if (params == 0)
	{
		/* loop over all CPUs */
		for (cpunum = 0; cpunum < MAX_CPU; cpunum++)
		{
			const debug_cpu_info *info = debug_get_cpu_info(cpunum);

			/* build up a comma-separated list */
			if (info && info->valid && info->ignoring)
			{
				if (buflen == 0) buflen += sprintf(&buffer[buflen], "Currently ignoring CPU %d", cpunum);
				else buflen += sprintf(&buffer[buflen], ",%d", cpunum);
			}
		}

		/* special message for none */
		if (buflen == 0)
			sprintf(&buffer[buflen], "Not currently ignoring any CPUs");
		debug_console_printf("%s\n", buffer);
	}

	/* otherwise clear the ignore flag on all requested CPUs */
	else
	{
		/* validate parameters */
		for (paramnum = 0; paramnum < params; paramnum++)
		{
			if (!validate_parameter_number(param[paramnum], &cpuwhich[paramnum]))
				return;
			if (cpuwhich[paramnum] >= cpu_gettotalcpu())
			{
				debug_console_printf("Invalid CPU number! (%d)\n", (int)cpuwhich[paramnum]);
				return;
			}
		}

		/* set the ignore flags */
		for (paramnum = 0; paramnum < params; paramnum++)
		{
			/* make sure this isn't the last live CPU */
			for (cpunum = 0; cpunum < MAX_CPU; cpunum++)
			{
				const debug_cpu_info *info = debug_get_cpu_info(cpunum);
				if (cpunum != cpuwhich[paramnum] && info && info->valid && !info->ignoring)
					break;
			}
			if (cpunum == MAX_CPU)
			{
				debug_console_printf("Can't ignore all CPUs!\n");
				return;
			}

			debug_cpu_ignore_cpu(cpuwhich[paramnum], 1);
			debug_console_printf("Now ignoring CPU %d\n", (int)cpuwhich[paramnum]);
		}
	}
}


/*-------------------------------------------------
    execute_observe - execute the observe command
-------------------------------------------------*/

static void execute_observe(int ref, int params, const char *param[])
{
	UINT64 cpuwhich[MAX_COMMAND_PARAMS];
	int cpunum, paramnum;
	char buffer[100];
	int buflen = 0;

	/* if there are no parameters, dump the ignore list */
	if (params == 0)
	{
		/* loop over all CPUs */
		for (cpunum = 0; cpunum < MAX_CPU; cpunum++)
		{
			const debug_cpu_info *info = debug_get_cpu_info(cpunum);

			/* build up a comma-separated list */
			if (info && info->valid && !info->ignoring)
			{
				if (buflen == 0) buflen += sprintf(&buffer[buflen], "Currently observing CPU %d", cpunum);
				else buflen += sprintf(&buffer[buflen], ",%d", cpunum);
			}
		}

		/* special message for none */
		if (buflen == 0)
			buflen += sprintf(&buffer[buflen], "Not currently observing any CPUs");
		debug_console_printf("%s\n", buffer);
	}

	/* otherwise set the ignore flag on all requested CPUs */
	else
	{
		/* validate parameters */
		for (paramnum = 0; paramnum < params; paramnum++)
		{
			if (!validate_parameter_number(param[paramnum], &cpuwhich[paramnum]))
				return;
			if (cpuwhich[paramnum] >= cpu_gettotalcpu())
			{
				debug_console_printf("Invalid CPU number! (%d)\n", (int)cpuwhich[paramnum]);
				return;
			}
		}

		/* clear the ignore flags */
		for (paramnum = 0; paramnum < params; paramnum++)
		{
			debug_cpu_ignore_cpu(cpuwhich[paramnum], 0);
			debug_console_printf("Now observing CPU %d\n", (int)cpuwhich[paramnum]);
		}
	}
}


/*-------------------------------------------------
    execute_comment - add a comment to a line
-------------------------------------------------*/

static void execute_comment(int ref, int params, const char *param[])
{
	UINT64 address;

	/* param 1 is the address for the comment */
	if (!validate_parameter_number(param[0], &address))
		return;

	/* make sure param 2 exists */
	if (strlen(param[1]) == 0)
	{
		debug_console_printf("Error : comment text empty\n");
		return;
	}

	/* Now try adding the comment */
	debug_comment_add(cpu_getactivecpu(), address, param[1], 0x00ff0000, debug_comment_get_opcode_crc32(address));
	debug_view_update_type(DVT_DISASSEMBLY);
}


/*------------------------------------------------------
    execute_comment_del - remove a comment from an addr
--------------------------------------------------------*/

static void execute_comment_del(int ref, int params, const char *param[])
{
	UINT64 address;

	/* param 1 can either be a command or the address for the comment */
	if (!validate_parameter_number(param[0], &address))
		return;

	/* If it's a number, it must be an address */
	/* The bankoff and cbn will be pulled from what's currently active */
	debug_comment_remove(cpu_getactivecpu(), address, debug_comment_get_opcode_crc32(address));
	debug_view_update_type(DVT_DISASSEMBLY);
}


/*-------------------------------------------------
    execute_comment - add a comment to a line
-------------------------------------------------*/

static void execute_comment_save(int ref, int params, const char *param[])
{
	if (debug_comment_save())
		debug_console_printf("Comments successfully saved\n");
}


/*-------------------------------------------------
    execute_bpset - execute the breakpoint set
    command
-------------------------------------------------*/

static void execute_bpset(int ref, int params, const char *param[])
{
	parsed_expression *condition = NULL;
	const char *action = NULL;
	UINT64 address;
	int bpnum;

	/* make sure that there is an active CPU */
	if (cpu_getactivecpu() < 0)
	{
		debug_console_printf("No active CPU!\n");
		return;
	}

	/* param 1 is the address */
	if (!validate_parameter_number(param[0], &address))
		return;

	/* param 2 is the condition */
	if (params > 1 && !validate_parameter_expression(param[1], &condition))
		return;

	/* param 3 is the action */
	if (params > 2 && !validate_parameter_command(action = param[2]))
		return;

	/* set the breakpoint */
	bpnum = debug_breakpoint_set(cpu_getactivecpu(), address, condition, action);
	debug_console_printf("Breakpoint %X set\n", bpnum);
}


/*-------------------------------------------------
    execute_bpclear - execute the breakpoint
    clear command
-------------------------------------------------*/

static void execute_bpclear(int ref, int params, const char *param[])
{
	UINT64 bpindex;

	/* if 0 parameters, clear all */
	if (params == 0)
	{
		int cpunum;

		for (cpunum = 0; cpunum < MAX_CPU; cpunum++)
		{
			const debug_cpu_info *cpuinfo = debug_get_cpu_info(cpunum);
			if (cpuinfo->valid)
			{
				debug_cpu_breakpoint *bp;
				while ((bp = cpuinfo->first_bp) != NULL)
					debug_breakpoint_clear(bp->index);
			}
		}
		debug_console_printf("Cleared all breakpoints\n");
	}

	/* otherwise, clear the specific one */
	else if (!validate_parameter_number(param[0], &bpindex))
		return;
	else
	{
		int found = debug_breakpoint_clear(bpindex);
		if (found)
			debug_console_printf("Breakpoint %X cleared\n", (UINT32)bpindex);
		else
			debug_console_printf("Invalid breakpoint number %X\n", (UINT32)bpindex);
	}
}


/*-------------------------------------------------
    execute_bpdisenable - execute the breakpoint
    disable/enable commands
-------------------------------------------------*/

static void execute_bpdisenable(int ref, int params, const char *param[])
{
	UINT64 bpindex;

	/* if 0 parameters, clear all */
	if (params == 0)
	{
		int cpunum;

		for (cpunum = 0; cpunum < MAX_CPU; cpunum++)
		{
			const debug_cpu_info *cpuinfo = debug_get_cpu_info(cpunum);
			if (cpuinfo->valid)
			{
				debug_cpu_breakpoint *bp;
				for (bp = cpuinfo->first_bp; bp; bp = bp->next)
					debug_breakpoint_enable(bp->index, ref);
			}
		}
		if (ref == 0)
			debug_console_printf("Disabled all breakpoints\n");
		else
			debug_console_printf("Enabled all breakpoints\n");
	}

	/* otherwise, clear the specific one */
	else if (!validate_parameter_number(param[0], &bpindex))
		return;
	else
	{
		int found = debug_breakpoint_enable(bpindex, ref);
		if (found)
			debug_console_printf("Breakpoint %X %s\n", (UINT32)bpindex, ref ? "enabled" : "disabled");
		else
			debug_console_printf("Invalid breakpoint number %X\n", (UINT32)bpindex);
	}
}


/*-------------------------------------------------
    execute_bplist - execute the breakpoint list
    command
-------------------------------------------------*/

static void execute_bplist(int ref, int params, const char *param[])
{
	int cpunum, printed = 0;
	char buffer[256];

	/* loop over all CPUs */
	for (cpunum = 0; cpunum < MAX_CPU; cpunum++)
	{
		const debug_cpu_info *cpuinfo = debug_get_cpu_info(cpunum);

		if (cpuinfo->valid && cpuinfo->first_bp)
		{
			debug_cpu_breakpoint *bp;

			debug_console_printf("CPU %d breakpoints:\n", cpunum);

			/* loop over the breakpoints */
			for (bp = cpuinfo->first_bp; bp; bp = bp->next)
			{
				int buflen;
				buflen = sprintf(buffer, "%c%4X @ %08X", bp->enabled ? ' ' : 'D', bp->index, bp->address);
				if (bp->condition)
					buflen += sprintf(&buffer[buflen], " if %s", expression_original_string(bp->condition));
				if (bp->action)
					buflen += sprintf(&buffer[buflen], " do %s", bp->action);
				debug_console_printf("%s\n", buffer);
				printed++;
			}
		}
	}

	if (!printed)
		debug_console_printf("No breakpoints currently installed\n");
}


/*-------------------------------------------------
    execute_wpset - execute the watchpoint set
    command
-------------------------------------------------*/

static void execute_wpset(int ref, int params, const char *param[])
{
	parsed_expression *condition = NULL;
	const char *action = NULL;
	UINT64 address, length;
	int type = 0;
	int wpnum;

	/* param 1 is the address */
	if (!validate_parameter_number(param[0], &address))
		return;

	/* param 2 is the length */
	if (!validate_parameter_number(param[1], &length))
		return;

	/* param 3 is the type */
	if (!strcmp(param[2], "r"))
		type = WATCHPOINT_READ;
	else if (!strcmp(param[2], "w"))
		type = WATCHPOINT_WRITE;
	else if (!strcmp(param[2], "rw") || !strcmp(param[2], "wr"))
		type = WATCHPOINT_READWRITE;
	else
	{
		debug_console_printf("Invalid watchpoint type: expected r, w, or rw\n");
		return;
	}

	/* param 4 is the condition */
	if (params > 3 && !validate_parameter_expression(param[3], &condition))
		return;

	/* param 5 is the action */
	if (params > 4 && !validate_parameter_command(action = param[4]))
		return;

	/* set the watchpoint */
	wpnum = debug_watchpoint_set(cpu_getactivecpu(), ref, type, address, length, condition, action);
	debug_console_printf("Watchpoint %X set\n", wpnum);
}


/*-------------------------------------------------
    execute_wpclear - execute the watchpoint
    clear command
-------------------------------------------------*/

static void execute_wpclear(int ref, int params, const char *param[])
{
	UINT64 wpindex;

	/* if 0 parameters, clear all */
	if (params == 0)
	{
		int cpunum;

		for (cpunum = 0; cpunum < MAX_CPU; cpunum++)
		{
			const debug_cpu_info *cpuinfo = debug_get_cpu_info(cpunum);
			if (cpuinfo->valid)
			{
				int spacenum;

				for (spacenum = 0; spacenum < ADDRESS_SPACES; spacenum++)
				{
					debug_cpu_watchpoint *wp;
					while ((wp = cpuinfo->space[spacenum].first_wp) != NULL)
						debug_watchpoint_clear(wp->index);
				}
			}
		}
		debug_console_printf("Cleared all watchpoints\n");
	}

	/* otherwise, clear the specific one */
	else if (!validate_parameter_number(param[0], &wpindex))
		return;
	else
	{
		int found = debug_watchpoint_clear(wpindex);
		if (found)
			debug_console_printf("Watchpoint %X cleared\n", (UINT32)wpindex);
		else
			debug_console_printf("Invalid watchpoint number %X\n", (UINT32)wpindex);
	}
}


/*-------------------------------------------------
    execute_wpdisenable - execute the watchpoint
    disable/enable commands
-------------------------------------------------*/

static void execute_wpdisenable(int ref, int params, const char *param[])
{
	UINT64 wpindex;

	/* if 0 parameters, clear all */
	if (params == 0)
	{
		int cpunum;

		for (cpunum = 0; cpunum < MAX_CPU; cpunum++)
		{
			const debug_cpu_info *cpuinfo = debug_get_cpu_info(cpunum);
			if (cpuinfo->valid)
			{
				int spacenum;

				for (spacenum = 0; spacenum < ADDRESS_SPACES; spacenum++)
				{
					debug_cpu_watchpoint *wp;
					for (wp = cpuinfo->space[spacenum].first_wp; wp; wp = wp->next)
						debug_watchpoint_enable(wp->index, ref);
				}
			}
		}
		if (ref == 0)
			debug_console_printf("Disabled all watchpoints\n");
		else
			debug_console_printf("Enabled all watchpoints\n");
	}

	/* otherwise, clear the specific one */
	else if (!validate_parameter_number(param[0], &wpindex))
		return;
	else
	{
		int found = debug_watchpoint_enable(wpindex, ref);
		if (found)
			debug_console_printf("Watchpoint %X %s\n", (UINT32)wpindex, ref ? "enabled" : "disabled");
		else
			debug_console_printf("Invalid watchpoint number %X\n", (UINT32)wpindex);
	}
}


/*-------------------------------------------------
    execute_wplist - execute the watchpoint list
    command
-------------------------------------------------*/

static void execute_wplist(int ref, int params, const char *param[])
{
	static const char *spacenames[ADDRESS_SPACES] = { "program", "data", "I/O" };
	int cpunum, printed = 0;
	char buffer[256];

	/* loop over all CPUs */
	for (cpunum = 0; cpunum < MAX_CPU; cpunum++)
	{
		const debug_cpu_info *cpuinfo = debug_get_cpu_info(cpunum);

		if (cpuinfo->valid)
		{
			int spacenum;

			for (spacenum = 0; spacenum < ADDRESS_SPACES; spacenum++)
			{
				if (cpuinfo->space[spacenum].first_wp)
				{
					static const char *types[] = { "unkn ", "read ", "write", "r/w  " };
					debug_cpu_watchpoint *wp;

					debug_console_printf("CPU %d %s space watchpoints:\n", cpunum, spacenames[spacenum]);

					/* loop over the watchpoints */
					for (wp = cpuinfo->space[spacenum].first_wp; wp; wp = wp->next)
					{
						int buflen;
						buflen = sprintf(buffer, "%c%4X @ %08X-%08X %s", wp->enabled ? ' ' : 'D', wp->index, wp->address, wp->address + wp->length - 1, types[wp->type & 3]);
						if (wp->condition)
							buflen += sprintf(&buffer[buflen], " if %s", expression_original_string(wp->condition));
						if (wp->action)
							buflen += sprintf(&buffer[buflen], " do %s", wp->action);
						debug_console_printf("%s\n", buffer);
						printed++;
					}
				}
			}
		}
	}

	if (!printed)
		debug_console_printf("No watchpoints currently installed\n");
}


/*-------------------------------------------------
    execute_hotspot - execute the hotspot
    command
-------------------------------------------------*/

static void execute_hotspot(int ref, int params, const char *param[])
{
	UINT64 threshhold;
	UINT64 cpunum;
	UINT64 count;

	/* if no params, and there are live hotspots, clear them */
	if (params == 0)
	{
		int cleared = FALSE;

		/* loop over CPUs and find live spots */
		for (cpunum = 0; cpunum < MAX_CPU; cpunum++)
		{
			const debug_cpu_info *cpuinfo = debug_get_cpu_info(cpunum);

			if (cpuinfo->valid && cpuinfo->hotspots)
			{
				debug_hotspot_track(cpunum, 0, 0);
				debug_console_printf("Cleared hotspot tracking on CPU %d\n", (int)cpunum);
				cleared = TRUE;
			}
		}

		/* if we cleared, we're done */
		if (cleared)
			return;
	}

	/* extract parameters */
	cpunum = cpu_getactivecpu();
	count = 64;
	threshhold = 250;
	if (params > 0 && !validate_parameter_number(param[0], &cpunum))
		return;
	if (params > 1 && !validate_parameter_number(param[1], &count))
		return;
	if (params > 2 && !validate_parameter_number(param[2], &threshhold))
		return;

	/* attempt to install */
	if (debug_hotspot_track(cpunum, count, threshhold))
		debug_console_printf("Now tracking hotspots on CPU %d using %d slots with a threshhold of %d\n", (int)cpunum, (int)count, (int)threshhold);
	else
		debug_console_printf("Error setting up the hotspot tracking\n");
}


/*-------------------------------------------------
    execute_save - execute the save command
-------------------------------------------------*/

static void execute_save(int ref, int params, const char *param[])
{
	UINT64 offset, endoffset, length, cpunum = cpu_getactivecpu();
	const debug_cpu_info *info;
	int spacenum = ref;
	FILE *f;
	UINT64 i;

	/* validate parameters */
	if (!validate_parameter_number(param[1], &offset))
		return;
	if (!validate_parameter_number(param[2], &length))
		return;
	if (params > 3 && !validate_parameter_number(param[3], &cpunum))
		return;

	/* determine the addresses to write */
	info = debug_get_cpu_info(cpunum);
	offset = ADDR2BYTE_MASKED(offset, info, spacenum);
	endoffset = ADDR2BYTE_MASKED(offset + length - 1, info, spacenum);

	/* open the file */
	f = fopen(param[0], "wb");
	if (!f)
	{
		debug_console_printf("Error opening file '%s'\n", param[0]);
		return;
	}

	/* now write the data out */
	cpuintrf_push_context(cpunum);
	for (i = offset; i <= endoffset; i++)
	{
		UINT8 byte = debug_read_byte(spacenum, i);
		fwrite(&byte, 1, 1, f);
	}
	cpuintrf_pop_context();

	/* close the file */
	fclose(f);
	debug_console_printf("Data saved successfully\n");
}


/*-------------------------------------------------
    execute_dump - execute the dump command
-------------------------------------------------*/

static void execute_dump(int ref, int params, const char *param[])
{
	UINT64 offset, endoffset, length, width = 0, ascii = 1, cpunum = cpu_getactivecpu();
	const debug_cpu_info *info;
	int spacenum = ref;
	FILE *f = NULL;
	UINT64 i, j;

	/* validate parameters */
	if (!validate_parameter_number(param[1], &offset))
		return;
	if (!validate_parameter_number(param[2], &length))
		return;
	if (params > 3 && !validate_parameter_number(param[3], &width))
		return;
	if (params > 4 && !validate_parameter_number(param[4], &ascii))
		return;
	if (params > 5 && !validate_parameter_number(param[5], &cpunum))
		return;

	/* further validation */
	if (cpunum >= cpu_gettotalcpu())
	{
		debug_console_printf("Invalid CPU number!\n");
		return;
	}
	info = debug_get_cpu_info(cpunum);
	if (width == 0)
		width = info->space[spacenum].databytes;
	if (width < ADDR2BYTE(1, info, spacenum))
		width = ADDR2BYTE(1, info, spacenum);
	if (width != 1 && width != 2 && width != 4 && width != 8)
	{
		debug_console_printf("Invalid width! (must be 1,2,4 or 8)\n");
		return;
	}
	offset = ADDR2BYTE_MASKED(offset, info, spacenum);
	endoffset = ADDR2BYTE_MASKED(offset + length - 1, info, spacenum);

	/* open the file */
	f = fopen(param[0], "w");
	if (!f)
	{
		debug_console_printf("Error opening file '%s'\n", param[0]);
		return;
	}

	/* now write the data out */
	cpuintrf_push_context(cpunum);
	for (i = offset; i <= endoffset; i += 16)
	{
		char output[200];
		int outdex = 0;

		/* print the address */
		outdex += sprintf(&output[outdex], "%0*X: ", info->space[spacenum].logchars, (UINT32)BYTE2ADDR(i, info, spacenum));

		/* print the bytes */
		switch (width)
		{
			case 1:
				for (j = 0; j < 16; j++)
				{
					if (i + j <= endoffset)
					{
						offs_t curaddr = i + j;
						if (!info->translate || (*info->translate)(spacenum, &curaddr))
						{
							UINT8 byte = debug_read_byte(ref, i + j);
							outdex += sprintf(&output[outdex], " %02X", byte);
						}
						else
							outdex += sprintf(&output[outdex], " **");
					}
					else
						outdex += sprintf(&output[outdex], "   ");
				}
				break;

			case 2:
				for (j = 0; j < 16; j += 2)
				{
					if (i + j <= endoffset)
					{
						offs_t curaddr = i + j;
						if (!info->translate || (*info->translate)(spacenum, &curaddr))
						{
							UINT16 word = debug_read_word(ref, i + j);
							outdex += sprintf(&output[outdex], " %04X", word);
						}
						else
							outdex += sprintf(&output[outdex], " ****");
					}
					else
						outdex += sprintf(&output[outdex], "     ");
				}
				break;

			case 4:
				for (j = 0; j < 16; j += 4)
				{
					if (i + j <= endoffset)
					{
						offs_t curaddr = i + j;
						if (!info->translate || (*info->translate)(spacenum, &curaddr))
						{
							UINT32 dword = debug_read_dword(ref, i + j);
							outdex += sprintf(&output[outdex], " %08X", dword);
						}
						else
							outdex += sprintf(&output[outdex], " ********");
					}
					else
						outdex += sprintf(&output[outdex], "         ");
				}
				break;

			case 8:
				for (j = 0; j < 16; j += 8)
				{
					if (i + j <= endoffset)
					{
						offs_t curaddr = i + j;
						if (!info->translate || (*info->translate)(spacenum, &curaddr))
						{
							UINT64 qword = debug_read_qword(ref, i + j);
							outdex += sprintf(&output[outdex], " %08X%08X", (UINT32)(qword >> 32), (UINT32)qword);
						}
						else
							outdex += sprintf(&output[outdex], " ****************");
					}
					else
						outdex += sprintf(&output[outdex], "                 ");
				}
				break;
		}

		/* print the ASCII */
		if (ascii)
		{
			outdex += sprintf(&output[outdex], "  ");
			for (j = 0; j < 16 && (i + j) <= endoffset; j++)
			{
				offs_t curaddr = i + j;
				if (!info->translate || (*info->translate)(spacenum, &curaddr))
				{
					UINT8 byte = debug_read_byte(ref, i + j);
					outdex += sprintf(&output[outdex], "%c", (byte >= 32 && byte < 128) ? byte : '.');
				}
				else
					outdex += sprintf(&output[outdex], " ");
			}
		}

		/* output the result */
		fprintf(f, "%s\n", output);
	}
	cpuintrf_pop_context();

	/* close the file */
	fclose(f);
	debug_console_printf("Data dumped successfully\n");
}


/*-------------------------------------------------
    execute_find - execute the find command
-------------------------------------------------*/

static void execute_find(int ref, int params, const char *param[])
{
	UINT64 offset, endoffset, length, cpunum = cpu_getactivecpu();
	const debug_cpu_info *info;
	UINT64 data_to_find[256];
	UINT8 data_size[256];
	int cur_data_size;
	int data_count = 0;
	int spacenum = ref;
	int found = 0;
	UINT64 i, j;

	/* validate parameters */
	if (!validate_parameter_number(param[0], &offset))
		return;
	if (!validate_parameter_number(param[1], &length))
		return;

	/* further validation */
	if (cpunum >= cpu_gettotalcpu())
	{
		debug_console_printf("Invalid CPU number!\n");
		return;
	}
	info = debug_get_cpu_info(cpunum);
	offset = ADDR2BYTE_MASKED(offset, info, spacenum);
	endoffset = ADDR2BYTE_MASKED(offset + length - 1, info, spacenum);
	cur_data_size = ADDR2BYTE(1, info, spacenum);
	if (cur_data_size == 0)
		cur_data_size = 1;

	/* parse the data parameters */
	for (i = 2; i < params; i++)
	{
		const char *pdata = param[i];

		/* check for a string */
		if (pdata[0] == '"' && pdata[strlen(pdata) - 1] == '"')
		{
			for (j = 1; j < strlen(pdata) - 1; j++)
			{
				data_to_find[data_count] = pdata[j];
				data_size[data_count++] = 1;
			}
		}

		/* otherwise, validate as a number */
		else
		{
			/* check for a 'b','w','d',or 'q' prefix */
			data_size[data_count] = cur_data_size;
			if (tolower(pdata[0]) == 'b' && pdata[1] == '.') { data_size[data_count] = cur_data_size = 1; pdata += 2; }
			if (tolower(pdata[0]) == 'w' && pdata[1] == '.') { data_size[data_count] = cur_data_size = 2; pdata += 2; }
			if (tolower(pdata[0]) == 'd' && pdata[1] == '.') { data_size[data_count] = cur_data_size = 4; pdata += 2; }
			if (tolower(pdata[0]) == 'q' && pdata[1] == '.') { data_size[data_count] = cur_data_size = 8; pdata += 2; }

			/* look for a wildcard */
			if (!strcmp(pdata, "?"))
				data_size[data_count++] |= 0x10;

			/* otherwise, validate as a number */
			else if (!validate_parameter_number(pdata, &data_to_find[data_count++]))
				return;
		}
	}

	/* now search */
	cpuintrf_push_context(cpunum);
	for (i = offset; i <= endoffset; i += data_size[0])
	{
		int suboffset = 0;
		int match = 1;

		/* find the entire string */
		for (j = 0; j < data_count && match; j++)
		{
			switch (data_size[j])
			{
				case 1:	match = ((UINT8)debug_read_byte(spacenum, i + suboffset) == (UINT8)data_to_find[j]);	break;
				case 2:	match = ((UINT16)debug_read_word(spacenum, i + suboffset) == (UINT16)data_to_find[j]);	break;
				case 4:	match = ((UINT32)debug_read_dword(spacenum, i + suboffset) == (UINT32)data_to_find[j]);	break;
				case 8:	match = ((UINT64)debug_read_qword(spacenum, i + suboffset) == (UINT64)data_to_find[j]);	break;
				default:	/* all other cases are wildcards */		break;
			}
			suboffset += data_size[j] & 0x0f;
		}

		/* did we find it? */
		if (match)
		{
			found++;
			debug_console_printf("Found at %*X\n", info->space[spacenum].logchars, (UINT32)BYTE2ADDR(i, info, spacenum));
		}
	}
	cpuintrf_pop_context();

	/* print something if not found */
	if (found == 0)
		debug_console_printf("Not found\n");
}


/*-------------------------------------------------
    execute_dasm - execute the dasm command
-------------------------------------------------*/

static void execute_dasm(int ref, int params, const char *param[])
{
	UINT64 offset, length, bytes = 1, cpunum = cpu_getactivecpu();
	const debug_cpu_info *info;
	int minbytes, maxbytes, byteswidth;
	int use_new_dasm;
	FILE *f = NULL;
	int i, j;

	/* validate parameters */
	if (!validate_parameter_number(param[1], &offset))
		return;
	if (!validate_parameter_number(param[2], &length))
		return;
	if (params > 3 && !validate_parameter_number(param[3], &bytes))
		return;
	if (params > 4 && !validate_parameter_number(param[4], &cpunum))
		return;

	/* further validation */
	if (cpunum >= cpu_gettotalcpu())
	{
		debug_console_printf("Invalid CPU number!\n");
		return;
	}
	info = debug_get_cpu_info(cpunum);

	/* determine the width of the bytes */
	minbytes = cpunum_min_instruction_bytes(cpunum);
	maxbytes = cpunum_max_instruction_bytes(cpunum);
	byteswidth = 0;
	if (bytes)
	{
		byteswidth = (maxbytes + (minbytes - 1)) / minbytes;
		byteswidth *= (2 * minbytes) + 1;
	}

	/* open the file */
	f = fopen(param[0], "w");
	if (!f)
	{
		debug_console_printf("Error opening file '%s'\n", param[0]);
		return;
	}

	/* now write the data out */
	cpuintrf_push_context(cpunum);
	use_new_dasm = (activecpu_get_info_fct(CPUINFO_PTR_DISASSEMBLE_NEW) != NULL);
	for (i = 0; i < length; )
	{
		int pcbyte = ADDR2BYTE_MASKED(offset + i, info, ADDRESS_SPACE_PROGRAM);
		char output[200+DEBUG_COMMENT_MAX_LINE_LENGTH], disasm[200];
		const char *comment;
		UINT64 dummyreadop;
		offs_t tempaddr;
		int outdex = 0;
		int numbytes = 0;

		/* print the address */
		outdex += sprintf(&output[outdex], "%0*X: ", info->space[ADDRESS_SPACE_PROGRAM].logchars, (UINT32)BYTE2ADDR(pcbyte, info, ADDRESS_SPACE_PROGRAM));

		/* make sure we can translate the address */
		tempaddr = pcbyte;
		if (!info->translate || (*info->translate)(ADDRESS_SPACE_PROGRAM, &tempaddr))
		{
			/* if we can use new disassembly, do it */
			if (use_new_dasm)
			{
				UINT8 opbuf[64], argbuf[64];

				/* fetch the bytes up to the maximum */
				for (numbytes = 0; numbytes < maxbytes; numbytes++)
				{
					opbuf[numbytes] = debug_read_opcode(pcbyte + numbytes, 1, FALSE);
					argbuf[numbytes] = debug_read_opcode(pcbyte + numbytes, 1, TRUE);
				}

				/* disassemble the result */
				i += numbytes = activecpu_dasm_new(disasm, offset + i, opbuf, argbuf, maxbytes) & DASMFLAG_LENGTHMASK;
			}

			/* otherwise, we need to use the old, risky way */
			else
			{
				/* get the disassembly up front, but only if mapped */
				if (memory_get_op_ptr(cpunum, pcbyte, 0) != NULL || (info->readop && (*info->readop)(pcbyte, 1, &dummyreadop)))
				{
					memory_set_opbase(pcbyte);
					i += numbytes = activecpu_dasm(disasm, offset + i) & DASMFLAG_LENGTHMASK;
				}
				else
				{
					sprintf(disasm, "<unmapped>");
					i += numbytes = 1;
				}
			}
		}

		/* print the bytes */
		if (bytes)
		{
			int startdex = outdex;
			numbytes = ADDR2BYTE(numbytes, info, ADDRESS_SPACE_PROGRAM);
			switch (minbytes)
			{
				case 1:
					for (j = 0; j < numbytes; j++)
						outdex += sprintf(&output[outdex], "%02X ", (UINT32)debug_read_opcode(pcbyte + j, 1, FALSE));
					break;

				case 2:
					for (j = 0; j < numbytes; j += 2)
						outdex += sprintf(&output[outdex], "%04X ", (UINT32)debug_read_opcode(pcbyte + j, 2, FALSE));
					break;

				case 4:
					for (j = 0; j < numbytes; j += 4)
						outdex += sprintf(&output[outdex], "%08X ", (UINT32)debug_read_opcode(pcbyte + j, 4, FALSE));
					break;

				case 8:
					for (j = 0; j < numbytes; j += 8)
					{
						UINT64 val = debug_read_opcode(pcbyte + j, 8, FALSE);
						outdex += sprintf(&output[outdex], "%08X%08X ", (UINT32)(val >> 32), (UINT32)val);
					}
					break;
			}
			if (outdex - startdex < byteswidth)
				outdex += sprintf(&output[outdex], "%*s", byteswidth - (outdex - startdex), "");
			outdex += sprintf(&output[outdex], "  ");
		}

		/* add the disassembly */
		sprintf(&output[outdex], "%s", disasm);

		/* attempt to add the comment */
		comment = debug_comment_get_text(cpunum, tempaddr, debug_comment_get_opcode_crc32(tempaddr));
		if (comment != NULL)
		{
			/* somewhat arbitrary guess as to how long most disassembly lines will be [column 60] */
			if (strlen(output) < 60)
			{
				/* pad the comment space out to 60 characters and null-terminate */
				for (outdex = strlen(output); outdex < 60; outdex++)
					output[outdex] = ' ' ;
				output[outdex] = 0 ;

				sprintf(&output[strlen(output)], "// %s", comment) ;
			}
			else
				sprintf(&output[strlen(output)], "\t// %s", comment) ;
		}

		/* output the result */
		fprintf(f, "%s\n", output);
	}
	cpuintrf_pop_context();

	/* close the file */
	fclose(f);
	debug_console_printf("Data dumped successfully\n");
}


/*-------------------------------------------------
    execute_trace_internal - functionality for
    trace over and trace info
-------------------------------------------------*/

static void execute_trace_internal(int ref, int params, const char *param[], int trace_over)
{
	const char *action = NULL, *filename = param[0];
	FILE *f = NULL;
	const char *mode;
	UINT64 cpunum;

	cpunum = cpu_getactivecpu();

	/* validate parameters */
	if (params > 1 && !validate_parameter_number(param[1], &cpunum))
		return;
	if (params > 2 && !validate_parameter_command(action = param[2]))
		return;

	/* further validation */
	if (!mame_stricmp(filename, "off"))
		filename = NULL;
	if (cpunum >= cpu_gettotalcpu())
	{
		debug_console_printf("Invalid CPU number!\n");
		return;
	}

	/* open the file */
	if (filename)
	{
		mode = "w";

		/* opening for append? */
		if ((filename[0] == '>') && (filename[1] == '>'))
		{
			mode = "a";
			filename += 2;
		}

		f = fopen(filename, mode);
		if (!f)
		{
			debug_console_printf("Error opening file '%s'\n", param[0]);
			return;
		}
	}

	/* do it */
	debug_cpu_trace(cpunum, f, trace_over, action);
	if (f)
		debug_console_printf("Tracing CPU %d to file %s\n", (int)cpunum, filename);
	else
		debug_console_printf("Stopped tracing on CPU %d\n", (int)cpunum);
}


/*-------------------------------------------------
    execute_trace - execute the trace command
-------------------------------------------------*/

static void execute_trace(int ref, int params, const char *param[])
{
	execute_trace_internal(ref, params, param, 0);
}


/*-------------------------------------------------
    execute_traceover - execute the trace over command
-------------------------------------------------*/

static void execute_traceover(int ref, int params, const char *param[])
{
	execute_trace_internal(ref, params, param, 1);
}


/*-------------------------------------------------
    execute_traceflush - execute the trace flush command
-------------------------------------------------*/

static void execute_traceflush(int ref, int params, const char *param[])
{
	debug_flush_traces();
}


/*-------------------------------------------------
    execute_snap - execute the trace over command
-------------------------------------------------*/

static void execute_snap(int ref, int params, const char *param[])
{
	/* if no params, use the default behavior */
	if (params == 0)
	{
		video_save_active_screen_snapshots();
		debug_console_printf("Saved snapshot\n");
	}

	/* otherwise, we have to open the file ourselves */
	else
	{
		mame_file *fp;
		const char *filename = param[0];
		int scrnum = (params > 1) ? atoi(param[1]) : 0;
		UINT32 mask = render_get_live_screens_mask();

		if ((scrnum < 0) || (scrnum >= MAX_SCREENS)	|| !(mask & (1 << scrnum)))
		{
			debug_console_printf("Invalid screen number '%d'\n", scrnum);
			return;
		}

		fp = mame_fopen(Machine->gamedrv->name, filename, FILETYPE_SCREENSHOT, 1);
		if (!fp)
		{
			debug_console_printf("Error creating file '%s'\n", filename);
			return;
		}

		video_screen_save_snapshot(fp, scrnum);
		mame_fclose(fp);
		debug_console_printf("Saved screen #%d snapshot as '%s'\n", scrnum, filename);
	}
}


/*-------------------------------------------------
    execute_source - execute the source command
-------------------------------------------------*/

static void execute_source(int ref, int params, const char *param[])
{
	debug_source_script(param[0]);
}


/*-------------------------------------------------
    execute_map - execute the map command
-------------------------------------------------*/

static void execute_map(int ref, int params, const char *param[])
{
	UINT64 address, cpunum = cpu_getactivecpu();
	const debug_cpu_info *info;
	int spacenum = ref;
	offs_t taddress;

	/* validate parameters */
	if (!validate_parameter_number(param[0], &address))
		return;
	info = debug_get_cpu_info(cpunum);

	/* do the translation first */
	taddress = ADDR2BYTE_MASKED(address, info, spacenum);
	if (info->translate)
	{
		if ((*info->translate)(spacenum, &taddress))
			debug_console_printf("%08X logical -> %08X physical\n", (UINT32)address, BYTE2ADDR(taddress, info, spacenum));
		else
		{
			debug_console_printf("%08X logical -> unmapped\n", (UINT32)address);
			return;
		}
	}
	else
		debug_console_printf("%08X physical\n", BYTE2ADDR(taddress, info, spacenum));

	/* now do the mapping */
	debug_console_printf("  -> read: %s\n", memory_get_handler_string(0, cpunum, spacenum, taddress));
	debug_console_printf("  -> write: %s\n", memory_get_handler_string(1, cpunum, spacenum, taddress));
}


/*-------------------------------------------------
    execute_memdump - execute the memdump command
-------------------------------------------------*/

static void execute_memdump(int ref, int params, const char **param)
{
	FILE *file;
	const char *filename;

	filename = (params == 0) ? "memdump.log" : param[0];

	debug_console_printf("Dumping memory to %s\n", filename);

	file = fopen(filename, "w");
	if (file)
	{
		memory_dump(file);
		fclose(file);
	}
}


/*-------------------------------------------------
    execute_symlist - execute the symlist command
-------------------------------------------------*/

static int CLIB_DECL symbol_sort_compare(const void *item1, const void *item2)
{
	const char *str1 = *(const char **)item1;
	const char *str2 = *(const char **)item2;
	return strcmp(str1, str2);
}

static void execute_symlist(int ref, int params, const char **param)
{
	const char *namelist[1000];
	symbol_table *symtable;
	UINT64 cpunum = 100000;
	int symnum, count = 0;

	/* validate parameters */
	if (params > 0 && !validate_parameter_number(param[0], &cpunum))
		return;
	if (cpunum != 100000 && cpunum >= cpu_gettotalcpu())
	{
		debug_console_printf("Invalid CPU number!\n");
		return;
	}
	symtable = (cpunum == 100000) ? global_symtable : debug_get_cpu_info(cpunum)->symtable;

	if (symtable == global_symtable)
		debug_console_printf("Global symbols:\n");
	else
		debug_console_printf("CPU #%d symbols:\n", (UINT32)cpunum);

	/* gather names for all symbols */
	for (symnum = 0; symnum < 100000; symnum++)
	{
		const symbol_entry *entry;
		const char *name = symtable_find_indexed(symtable, symnum, &entry);

		/* if we didn't get anything, we're done */
		if (name == NULL)
			break;

		/* only display "register" type symbols */
		if (entry->type == SMT_REGISTER)
		{
			namelist[count++] = name;
			if (count >= ARRAY_LENGTH(namelist))
				break;
		}
	}

	/* sort the symbols */
	if (count > 1)
		qsort((void *)namelist, count, sizeof(namelist[0]), symbol_sort_compare);

	/* iterate over symbols and print out relevant ones */
	for (symnum = 0; symnum < count; symnum++)
	{
		const symbol_entry *entry = symtable_find(symtable, namelist[symnum]);
		UINT64 value = (*entry->info.reg.getter)(entry->ref);
		assert(entry != NULL);

		/* only display "register" type symbols */
		debug_console_printf("%s = ", namelist[symnum]);
		if ((value >> 32) != 0)
			debug_console_printf("%X%08X", (UINT32)(value >> 32), (UINT32)value);
		else
			debug_console_printf("%X", (UINT32)value);
		if (entry->info.reg.setter == NULL)
			debug_console_printf("  (read-only)");
		debug_console_printf("\n");
	}
}

