/****************************************************************************
 *	The new MAME debugger V0.53
 *	Written by:   Juergen Buchmueller (so far - help welcome! :)
 *
 *	Based on code found in the preivous version of the MAME debugger
 *	written by:   Martin Scragg, John Butler, Mirko Buffoni
 *				  Chris Moore, Aaron Giles, Ernesto Corvi
 *
 *  Online help is available by pressing F1 (context sensitive!)
 *
 *	TODO:
 *	- Verify correct endianess handling, ie. look at the word and dword
 *	  modes of the memory and disassembly windows (selected with M)
 *	- Squash thousands of bugs :-/
 *	- Test & improve name_rom()
 *	- Add more names for generic handlers to name_rdmem() and name_wrmem()
 *
 *  LATER:
 *	- Add stack view using cpu_get_reg(REG_SP_CONTENTS+offset)
 *	- Add more display modes for the memory windows (binary? octal? decimal?)
 *	- Make the windows resizeable somehow (using win_set_w and win_set_h)
 *	- Anything you like - just contact me to avoid doubled effort.
 *
 ****************************************************************************/

#include <stdio.h>

#ifdef MAME_DEBUG
#include "mamedbg.h"
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
#include "driver.h"
#include "window.h"
#include "vidhrdw/generic.h"
#include "osd_dbg.h"


#if (HAS_Z80)
#include "cpu/z80/z80.h"
#endif
#if (HAS_8080 || HAS_8085A)
#include "cpu/i8085/i8085.h"
#endif
#if (HAS_M6502 || HAS_M65C02 || HAS_M6510)
#include "cpu/m6502/m6502.h"
#endif
#if (HAS_H6280)
#include "cpu/h6280/h6280.h"
#endif
#if (HAS_I86)
#include "cpu/i86/i86intrf.h"
#endif
#if (HAS_I8035 || HAS_I8039 || HAS_I8048 || HAS_N7751)
#include "cpu/i8039/i8039.h"
#endif
#if (HAS_M6800 || HAS_M6801 || HAS_M6802 || HAS_M6803 || HAS_M6808 || HAS_HD63701)
#include "cpu/m6800/m6800.h"
#endif
#if (HAS_M6805 || HAS_M68705 || HAS_HD63705)
#include "cpu/m6805/m6805.h"
#endif
#if (HAS_HD6309 || HAS_M6809)
#include "cpu/m6809/m6809.h"
#endif
#if (HAS_M68000 || defined HAS_M68010 || HAS_M68020)
#include "cpu/m68000/m68000.h"
#endif
#if (HAS_T11)
#include "cpu/t11/t11.h"
#endif
#if (HAS_S2650)
#include "cpu/s2650/s2650.h"
#endif
#if (HAS_TMS34010)
#include "cpu/tms34010/tms34010.h"
#endif
#if (HAS_TMS9900)
#include "cpu/tms9900/tms9900.h"
#endif
#if (HAS_Z8000)
#include "cpu/z8000/z8000.h"
#endif
#if (HAS_TMS320C10)
#include "cpu/tms32010/tms32010.h"
#endif
#if (HAS_CCPU)
#include "cpu/ccpu/ccpu.h"
#endif
#if (HAS_PDP1)
#include "cpu/pdp1/pdp1.h"
#endif

#ifndef INVALID
#define INVALID -1
#endif

/****************************************************************************
 * Externals (define in the header files)
 ****************************************************************************/
/* Long(er) function names, short macro names... */
#define ABITS	cpu_address_bits()
#define AMASK   cpu_address_mask()
#define ASHIFT	cpu_address_shift()
#define ALIGN   cpu_align_unit()
#define INSTL   cpu_max_inst_len()
#define ENDIAN	cpu_endianess()

#define RDMEM(a)	(*cpuintf[cputype].memory_read)(a)
#define WRMEM(a,v)	(*cpuintf[cputype].memory_write)(a,v)

/****************************************************************************
 * Globals
 ****************************************************************************/
int debug_key_pressed = 0;
int debug_key_delay = 0;

/****************************************************************************
 * Limits
 ****************************************************************************/
#define MAX_DATA	512 		/* Maximum memory size in bytes of a dump window */
#define MAX_REGS	64			/* Maximum number of registers for a CPU */
#define MAX_MEM 	2			/* You can't redefine this... too easy */

#define MAX_LOOPS	64			/* Maximum loop addresses recognized by trace */

#define MAX_HIST	16			/* Maximum history depth */

#define EDIT_CMDS   0
#define EDIT_REGS	1			/* Just the order of the windows */
#define EDIT_DASM	2
#define EDIT_MEM1	3
#define EDIT_MEM2	4

#define DBG_WINDOWS 5

/* Some convenience macros to address the cpu'th window */
#define WIN_CMDS(cpu)   (cpu*DBG_WINDOWS+EDIT_CMDS)
#define WIN_REGS(cpu)	(cpu*DBG_WINDOWS+EDIT_REGS)
#define WIN_DASM(cpu)	(cpu*DBG_WINDOWS+EDIT_DASM)
#define WIN_MEM(cpu,n)	(cpu*DBG_WINDOWS+EDIT_MEM1+n)
#define WIN_MEM1(cpu)	(cpu*DBG_WINDOWS+EDIT_MEM1)
#define WIN_MEM2(cpu)	(cpu*DBG_WINDOWS+EDIT_MEM2)
#define WIN_HELP		(MAX_WINDOWS-1)
#define WIN_MSGBOX		(MAX_WINDOWS-2)

#define MODE_HEX_UINT8	0
#define MODE_HEX_UINT16 1
#define MODE_HEX_UINT32 2

#define UINT16_XOR_LE(o) (((o)&~1)|(((o)&1)^1))
#define UINT32_XOR_LE(o) (((o)&~3)|(((o)&3)^3))
#define HOST_XOR_LE  0
#define UINT16_XOR_BE(o) (o)
#define UINT32_XOR_BE(o) (o)
#define HOST_XOR_BE  0

/****************************************************************************
 * Statics
 ****************************************************************************/
static int first_time = 1;

static int activecpu = INVALID;
static int previous_activecpu = INVALID;
static int totalcpu = 0;
static int cputype = 0;

static int dbg_fast = 0;
static int dbg_step = 0;
static int dbg_trace = 0;
static int dbg_trace_delay = 0x1000;
static int dbg_update = 0;
static int dbg_update_cur = 0;
static int dbg_active = 0;

/* 0 = dont, 1 = do allow squeezed display w/alternating dim, bright colors */
static int dbg_mem_squeezed = 0;
/* 0 = display disassembly only, 1 = display opcodes too */
static int dbg_dasm_opcodes = 0;
/* 0 = default, 1 = lower or 2 = upper case */
static int dbg_dasm_case = 0;
/* 0 = absolute, 1 = relative format for relative jumps/branches */
static int dbg_dasm_relative_jumps = 0;

static const char *dbg_info_once = NULL;

/****************************************************************************
 * Color settings
 ****************************************************************************/
#define COLOR_NAMES \
	"BLACK\0" \
	"BLUE\0" \
	"GREEN\0" \
	"CYAN\0" \
	"RED\0" \
	"MAGENTA\0" \
	"BROWN\0" \
	"LIGHTGRAY\0" \
	"DARKGRAY\0" \
	"LIGHTBLUE\0" \
	"LIGHTGREEN\0" \
	"LIGHTCYAN\0" \
	"LIGHTRED\0" \
	"LIGHTMAGENTA\0" \
	"YELLOW\0" \
	"WHITE\0"

#define ELEMENT_NAMES \
	"TITLE\0" \
	"FRAME\0" \
	"REGS\0" \
	"DASM\0" \
    "MEM1\0" \
    "MEM2\0" \
	"CMDS\0" \
	"BRK_EXEC\0" \
	"BRK_DATA\0" \
	"BRK_REGS\0" \
    "ERROR\0" \
    "HELP\0" \
	"PROMPT\0" \
    "CHANGES\0" \
	"PC\0" \
	"CURSOR\0"

enum ELEMENT {
	E_TITLE,
	E_FRAME,
	E_REGS,
	E_DASM,
    E_MEM1,
    E_MEM2,
	E_CMDS,
	E_BRK_EXEC,
	E_BRK_DATA,
	E_BRK_REGS,
    E_ERROR,
    E_HELP,
    E_PROMPT,
    E_CHANGES,
	E_PC,
	E_CURSOR,
	E_COUNT
};

static UINT8 cur_col[E_COUNT] = {
	COLOR_TITLE,
	COLOR_FRAME,
	COLOR_REGS,
	COLOR_DASM,
    COLOR_MEM1,
    COLOR_MEM2,
	COLOR_CMDS,
	COLOR_BRK_EXEC,
	COLOR_BRK_DATA,
	COLOR_BRK_REGS,
    COLOR_ERROR,
    COLOR_HELP,
	COLOR_PROMPT,
    COLOR_CHANGES,
	COLOR_PC,
	COLOR_CURSOR,
};

static UINT8 def_col[E_COUNT] = {
	COLOR_TITLE,
	COLOR_FRAME,
	COLOR_REGS,
	COLOR_DASM,
    COLOR_MEM1,
    COLOR_MEM2,
	COLOR_CMDS,
	COLOR_BRK_EXEC,
	COLOR_BRK_DATA,
	COLOR_BRK_REGS,
    COLOR_ERROR,
    COLOR_HELP,
	COLOR_PROMPT,
    COLOR_CHANGES,
	COLOR_PC,
    COLOR_CURSOR,
};

/****************************************************************************
 * Code to ASCII translation table; may be redefined (later)
 ****************************************************************************/
static char trans_table[256] = {
	'.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.',
    '.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.',
	' ','!','"','#','$','%','&', 39,'(',')','*','+',',','-','.','/',
	'0','1','2','3','4','5','6','7','8','9',':',';','<','=','>','?',
	'@','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O',
	'P','Q','R','S','T','U','V','W','X','Y','Z','[', 92,']','^','_',
	'`','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',
	'p','q','r','s','t','u','v','w','x','y','z','{','|','}','~','.',
	'.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.',
    '.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.',
	'.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.',
    '.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.',
	'.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.',
    '.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.',
	'.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.',
    '.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.',
};

/****************************************************************************
 * Function prototypes
 ****************************************************************************/
static unsigned get_register_id( char **parg, int *size );
static const char *get_register_name( int id );
static unsigned get_register_or_value( char **parg, int *size );
static void trace_init( const char *filename, UINT8 *regs );
static void trace_done( void );
static void trace_select( void );
static void trace_output( void );

static int hit_brk_exec( void );
static int hit_brk_data( void );
static int hit_brk_regs( void );

static const char *name_rom( const char *type, int region, unsigned *base, unsigned start );
static const char *name_rdmem( unsigned base );
static const char *name_wrmem( unsigned base );
static const char *name_memory( unsigned base );

static int win_create(int n, UINT8 prio, int x, int y, int w, int h,
	UINT8 co_text, UINT8 co_frame, UINT8 chr, UINT32 attributes);
static int DECL_SPEC win_msgbox( UINT8 color, const char *title, const char *fmt, ... );
static void dbg_open_windows( void );
static void dbg_close_windows( void );

static unsigned dasm_line( unsigned pc, int times );

static void dump_regs( void );
static unsigned dump_dasm( unsigned pc );
static void dump_mem_hex( int which, unsigned len_addr, unsigned len_data );
static void dump_mem( int which, int set_title );

static int edit_cmds_info( void );
static int edit_cmds_parse( char *cmdline );
static void edit_cmds_append( const char *src );

static void edit_regs( void );
static void edit_dasm( void );
static void edit_mem( int which );
static void edit_cmds(void);

static void cmd_help( void );
static void cmd_default( int code );

static void cmd_display_memory( void );
static void cmd_edit_memory( void );
static void cmd_set_memory_mode( void );
static void cmd_fast( void );
static void cmd_go_break( void );
static void cmd_jump( void );
static void cmd_replace_register( void );
static void cmd_brk_exec_set( void );
static void cmd_brk_exec_clear( void );
static void cmd_brk_regs_set( void );
static void cmd_brk_regs_clear( void );
static void cmd_brk_data_set( void );
static void cmd_brk_data_clear( void );
static void cmd_here( void );
static void cmd_dasm_to_file( void );
static void cmd_dump_to_file( void );
static void cmd_trace_to_file( void );
static void cmd_save_to_file( void );
static void cmd_set_ignore( void );
static void cmd_set_observe( void );
static void cmd_set_dasm_case( void );
static void cmd_set_dasm_opcodes( void );
static void cmd_set_dasm_relative_jumps( void );
static void cmd_set_mem_squeezed( void );
static void cmd_set_screen_size( void );
static void cmd_set_element_color( void );
static void cmd_brk_exec_toggle( void );
static void cmd_brk_data_toggle( void );

static void cmd_switch_window( void );
static void cmd_dasm_up( void );
static void cmd_dasm_down( void );
static void cmd_dasm_page_up( void );
static void cmd_dasm_page_down( void );
static void cmd_dasm_home( void );
static void cmd_dasm_end( void );
static void cmd_dasm_hist_follow( void );
static void cmd_dasm_hist_back( void );
static void cmd_run_to_cursor( void );
static void cmd_view_screen( void );
static void cmd_focus_next_cpu( void );
static void cmd_step( void );
static void cmd_animate( void );
static void cmd_step_over( void );
static void cmd_go( void );
static void cmd_search_memory( void );

/****************************************************************************
 * Generic structure for saving points in the 'follow history'
 ****************************************************************************/
typedef struct {
    UINT32  dasm_top;               /* previous top of window PC */
    UINT32  dasm_cur;               /* previous cursor PC */
    UINT32  mem1_base;              /* previous memory 1 base address */
    UINT32  mem1_offset;            /* previous memory 1 offset */
    UINT32  mem1_nibble;            /* previous memory 1 nibble */
}   s_hist;

/****************************************************************************
 * Symbol table entry
 ****************************************************************************/
typedef struct {
	UINT32	value;					/* value of the symbol */
	INT32	access; 				/* access mode (EA_... enum) */
	INT32	size;					/* size of the element */
	UINT32	times;					/* repeat how many times */
	char	name[47+1]; 			/* name of the symbol */
}	s_symbol;

/****************************************************************************
 * Generic structure for editing values
 * x,y are the coordinates inside a window
 * w is the width in hex digits (aka nibbles)
 * n is the distance of the hex part from the start of the output (register)
 ****************************************************************************/
typedef struct {
	UINT8	x,y,w,n;
}	s_edit;

/****************************************************************************
 * Register display and editing structure
 ****************************************************************************/
typedef struct {
    UINT32  backup[MAX_REGS];       /* backup register values */
    UINT32  newval[MAX_REGS];       /* new register values */
	s_edit	edit[MAX_REGS]; 		/* list of x,y,w triplets for the register values */
	char	name[MAX_REGS][15+1];	/* ...fifteen characters enough!? */
	UINT8	id[MAX_REGS];			/* the ID of the register (cpu_get_reg/cpu_set_reg) */
	UINT32	max_width;				/* maximum width of any dumped register */
    INT32   idx;                    /* index of current register */
    INT32   count;                  /* number of registers */
    INT32   nibble;                 /* edit nibble */
    INT32   changed;
}	s_regs;

/****************************************************************************
 * Memory display and editing structure
 ****************************************************************************/
typedef struct {
    UINT8   backup[MAX_DATA];   /* backup data */
    UINT8   newval[MAX_DATA];   /* newly read data */
	s_edit	edit[MAX_DATA]; 	/* list of x,y,w triplets for the memory elements */
    UINT32  base;               /* current base address */
	UINT32	address;			/* current cursor address */
    INT32   offset;             /* edit offset */
    INT32   nibble;             /* edit nibble */
	INT32	bytes;				/* number of bytes per edit line */
	INT32	width;				/* width in nibbles of the edit line */
	INT32	size;				/* number of bytes in the edit window */
	UINT8	mode;				/* 0 bytes, 1 words, 2 dword */
	UINT8	ascii;				/* display ASCII values */
    UINT8   changed;
}	s_mem;

/****************************************************************************
 * Disassembly structure
 ****************************************************************************/
typedef struct {
	UINT32	pc_cpu; 			/* The CPUs PC */
	UINT32	pc_top; 			/* Top of the disassembly window PC */
	UINT32	pc_cur; 			/* Cursor PC */
	UINT32	pc_end; 			/* End of the disassembly window PC */
	UINT32	dst_ea_value;		/* effective destination address or value */
	INT32	dst_ea_access;		/* destination access mode */
	INT32	dst_ea_size;		/* destination access size */
	UINT32	src_ea_value;		/* effective source address or value */
	INT32	src_ea_access;		/* source access mode */
	INT32	src_ea_size;		/* source access size */
}	s_dasm;

/****************************************************************************
 * Tracing structure
 ****************************************************************************/
typedef struct {
    UINT32  last_pc[MAX_LOOPS];
	UINT8	regs[MAX_REGS];
    FILE    *file;
    INT32   iters;
    INT32   loops;
}	s_trace;

/****************************************************************************
 * Debugger structure. There is one instance per CPU
 ****************************************************************************/
typedef struct {
	UINT32	ignore; 			/* ignore this CPU while debugging? */
	UINT32	next_pc;
	UINT32	prev_sp;

	/* Break- and Watchpoints */
	UINT32	brk_exec;			/* execution breakpoint (program counter) */
	UINT32	brk_exec_times; 	/* how many times to ignore the breakpoint */
	UINT32	brk_exec_reset; 	/* reset value for times once it counted down */
	UINT32	brk_data;			/* data watchpoint (memory address) */
	UINT32	brk_data_oldval;	/* old data watchpoint value */
	UINT32	brk_data_newval;	/* expected new value (INVALID: always break) */
	UINT32	brk_regs;			/* register watchpoint (register ID) */
	UINT32	brk_regs_oldval;	/* old register watchpoint value */
	UINT32	brk_regs_newval;	/* expected new value (INVALID: always break) */
	UINT32	brk_regs_mask;		/* mask register value before comparing */
	UINT32	brk_temp;			/* temporary execution breakpoint */

	s_regs	regs;
	s_dasm	dasm;
	s_mem	mem[MAX_MEM];
	s_trace trace;
	INT32	hist_cnt;
	s_hist	hist[MAX_HIST];

    char    cmdline[80+1];
	UINT8	window; 	/* edit what: cmds, regs, dasm, mem1, mem2 */

}	s_dbg;

static	s_dbg	dbg[MAX_CPU];

/* Covenience macros... keep the code readable */
#define DBG 	dbg[activecpu]
#define DBGREGS dbg[activecpu].regs
#define DBGDASM dbg[activecpu].dasm
#define DBGMEM	dbg[activecpu].mem
#define TRACE	dbg[tracecpu].trace

#define CMD 	dbg[activecpu].cmdline

/****************************************************************************
 * Tracing
 ****************************************************************************/
static int tracecpu = 0;
static int trace_on = 0;

/****************************************************************************
 * Commands structure
 ****************************************************************************/
typedef struct {
	int valid;				/* command is valid for which windows (bit mask) */
	const char *name;		/* command name (NULL none) */
	const char *alias;		/* command name alias (NULL none) */
	int key;				/* key code (0 none) */
	const char *args;		/* description of expected arguments */
	const char *info;		/* description of the function */
	void (*function)(void); /* function handling the key/command */
}	s_command;

#define ALL 	((1<<EDIT_CMDS)|(1<<EDIT_REGS)|(1<<EDIT_DASM)|(1<<EDIT_MEM1)|(1<<EDIT_MEM2))

static s_command commands[] = {
{	(1<<EDIT_CMDS),
	"A",            0,          0,
	"[<update>]",
	"Animate (trace) and update display every <update> opcodes only",
	cmd_animate },
{   (1<<EDIT_CMDS),
    "D",            0,          0,
    "<1|2> <address>",
    "Display memory <1|2> starting at <address>",
    cmd_display_memory },
{   (1<<EDIT_CMDS),
    "E",            0,          0,
    "<1|2> [<address>]",
    "Edit memory window <1|2> [at <address>]",
    cmd_edit_memory },
{	(1<<EDIT_CMDS),
    "M",            0,          0,
	"<1|2> [BYTE|WORD|DWORD]",
	"Change memory window mode to default [to BYTE|WORD|DWORD (or 0|1|2)]",
	cmd_set_memory_mode },
{   (1<<EDIT_CMDS),
    "F",            0,          0,
    "",
    "Fast",
    cmd_fast },
{   (1<<EDIT_CMDS),
    "G",            0,          0,
    "[<address>]",
    "Go [and break at <address>]",
    cmd_go_break },
{   (1<<EDIT_CMDS),
    "J",            0,          0,
    "<address>",
    "Jump to <address> in disassembly window",
    cmd_jump },
{   (1<<EDIT_CMDS),
    "R",            0,          0,
    "<register> <value>",
	"Replace <register> with <value> (<value> may also be a <register>)",
    cmd_replace_register },
{   (1<<EDIT_CMDS),
	"BP",           "BPX",      0,
	"<address> [<times>]",
	"Break on execution of <address> [after ignoring it <times>]",
	cmd_brk_exec_set },
{   (1<<EDIT_CMDS),
	"BC",           0,          0,
	"",
	"Clear execution breakpoint",
	cmd_brk_exec_clear },
{	(1<<EDIT_CMDS),
    "RP",           0,          0,
    "<register> [<value> [<mask>]]",
    "Break if <register> changes [to <value> [compare after applying <mask>]]",
    cmd_brk_regs_set },
{   (1<<EDIT_CMDS),
    "RC",           0,          0,
    "",
    "Clear register watchpoint",
    cmd_brk_regs_clear },
{   (1<<EDIT_CMDS),
    "WP",           "BPW",      0,
    "<address> [<value>]",
    "Break if data at <address> changes [to <value>]",
    cmd_brk_data_set },
{   (1<<EDIT_CMDS),
    "WC",           0,          0,
    "",
	"Clear data watchpoint",
    cmd_brk_data_clear },
{	(1<<EDIT_CMDS),
    "HERE",         0,          0,
    "",
    "Run to cursor",
    cmd_here },
{   (1<<EDIT_CMDS),
	"DASM",         0,          0,
	"<filename> <start> <end> [<boolean>]",
	"Disassemble to <filename> from address <start> to <end>\n" \
	"Opcode dump on by default [OFF|NO|0 without]",
	cmd_dasm_to_file },
{	(1<<EDIT_CMDS),
	"DUMP",         0,          0,
	"<filename> <start> <end> [<data size> [<ASCII mode>]]",
	"Dump to <filename> from address <start> to <end>\n" \
	"[data size BYTE|WORD|DWORD (also 0|1|2)]\n" \
	"[ASCII mode OFF|TRANSLATE|FULL (also 0|1|2)]\n",
	cmd_dump_to_file },
{	(1<<EDIT_CMDS),
    "TRACE",        0,          0,
    "{<filename> [<reg1> [<reg2>...]]}|OFF",
    "Trace to <filename> [dumping <reg1> [<reg2>...]] | OFF to stop tracing.",
    cmd_trace_to_file },
{	(1<<EDIT_CMDS),
	"SAVE",         0,          0,
	"<filename> <start> <end> [OPCODES|DATA]",
	"Save binary to <filename> from address <start> to <end>\n" \
	"[either OPCODES (from OP_ROM, default) or DATA (from OP_RAM), also 0|1].",
	cmd_save_to_file },
{   (1<<EDIT_CMDS),
	"IGNORE",       0,          0,
	"<cpunum>",
	"Ignore CPU #<cpunum> while debugging or tracing",
	cmd_set_ignore },
{	(1<<EDIT_CMDS),
	"OBSERVE",      0,          0,
	"<cpunum>",
	"Observe CPU #<cpunum> while debugging or tracing",
	cmd_set_observe },
{	(1<<EDIT_CMDS),
    "CASE",         0,          0,
    "DEFAULT|LOWER|UPPER (also 0|1|2)",
    "Set disassembly case style.",
    cmd_set_dasm_case },
{   (1<<EDIT_CMDS),
    "OPCODES",      0,          0,
    "<boolean>",
    "Display opcodes in disassembly window",
    cmd_set_dasm_opcodes },
{   (1<<EDIT_CMDS),
    "RELATIVE",     0,          0,
    "<boolean>",
    "Display relative jump addresses in disassembly window",
    cmd_set_dasm_relative_jumps },
{	(1<<EDIT_CMDS),
	"SQUEEZE",      0,        0,
	"<boolean>",
	"Allow squeezed memory display",
	cmd_set_mem_squeezed },
{	(1<<EDIT_CMDS),
	"COLOR",       0,          0,
	"<element> <foreground> [<background>]",
	"Set <element> color to <foreground> on BLACK [or <background>].\nFor a list of <elements> and <colors> see mamedbg.cfg",
	cmd_set_element_color },
{   (1<<EDIT_CMDS),
	"SCREEN",       0,          0,
	"[<column> <lines>]",
	"Set screen size to default [or to <columns> x <lines>]",
	cmd_set_screen_size },
{   (1<<EDIT_CMDS)|(1<<EDIT_DASM),
	0,				0,			KEYCODE_UP,
	"",
	"Move cursor up in disassembly window",
	cmd_dasm_up },
{	(1<<EDIT_CMDS)|(1<<EDIT_DASM),
	0,				0,			KEYCODE_DOWN,
	"",
	"Move cursor down in disassembly window",
	cmd_dasm_down },
{	(1<<EDIT_CMDS)|(1<<EDIT_DASM),
	0,				0,			KEYCODE_PGUP,
	"",
	"Move cursor up one page in disassembly window",
	cmd_dasm_page_up },
{	(1<<EDIT_CMDS)|(1<<EDIT_DASM),
	0,				0,			KEYCODE_PGDN,
	"",
	"Move cursor down one page in disassembly window",
	cmd_dasm_page_down },
{	(1<<EDIT_CMDS)|(1<<EDIT_DASM),
	0,				0,			KEYCODE_HOME,
	"",
	"Move cursor to first page in disassembly window",
	cmd_dasm_home },
{	(1<<EDIT_CMDS)|(1<<EDIT_DASM),
	0,				0,			KEYCODE_END,
	"",
	"Move cursor to last page in disassembly window",
	cmd_dasm_end },
{   (1<<EDIT_CMDS)|(1<<EDIT_DASM),
	0,				0,			KEYCODE_LEFT,
	"",
	"Back to the previous point in 'follow history'",
	cmd_dasm_hist_back },
{	(1<<EDIT_CMDS)|(1<<EDIT_DASM),
	0,				0,			KEYCODE_RIGHT,
	"",
	"Follow the current instruction's code or data reference",
	cmd_dasm_hist_follow },
{   ALL,
	0,				0,			KEYCODE_TAB,
	"",
	"Switch between windows (backwards SHIFT+TAB)",
	cmd_switch_window },
{	(1<<EDIT_DASM),
    0,              0,          KEYCODE_D,
    "",
    "Change disassembly case style to default",
    NULL },
{	(1<<EDIT_DASM),
	0,				0,			KEYCODE_L,
    "",
	"Change disassembly case style to lower case",
    NULL },
{	(1<<EDIT_DASM),
	0,				0,			KEYCODE_U,
    "",
	"Change disassembly case style to upper case",
    NULL },
{	(1<<EDIT_DASM),
	0,				0,			KEYCODE_M,
    "",
	"Toggle disassembly opcode display mode",
    NULL },
{	(1<<EDIT_MEM1)|(1<<EDIT_MEM2),
	0,				0,			KEYCODE_H,
    "",
	"Toggle between hex, ASCII and full character set mode",
    NULL },
{   (1<<EDIT_MEM1)|(1<<EDIT_MEM2),
	0,				0,			KEYCODE_M,
    "",
	"Switch memory display mode between bytes, words and dwords",
    NULL },
{	(1<<EDIT_MEM1)|(1<<EDIT_MEM2),
	0,				0,			KEYCODE_S,
    "",
	"Search memory for a sequence of bytes",
	cmd_search_memory },
{   ALL,
	0,				0,			KEYCODE_F1,
	"",
    "Help - maybe you realized this ;)",
	cmd_help },
{	(1<<EDIT_CMDS)|(1<<EDIT_DASM),
	0,				0,			KEYCODE_F2,
	"",
	"Toggle breakpoint at current cursor position",
	cmd_brk_exec_toggle },
{	(1<<EDIT_CMDS)|(1<<EDIT_DASM),
	0,				0,			KEYCODE_F4,
	"",
	"Run to cursor",
	cmd_run_to_cursor },
{	(1<<EDIT_MEM1)|(1<<EDIT_MEM2),
	0,				0,			KEYCODE_F4,
	"",
	"Set data watchpoint to current memory location",
	cmd_brk_data_toggle },
{   ALL,
	0,				0,			KEYCODE_F5,
	"",
	"View emulation screen",
	cmd_view_screen },
{	ALL,
	0,				0,			KEYCODE_F6,
	"",
	"Set the focus to the next (not ignored) CPU",
	cmd_focus_next_cpu },
{	ALL,
	0,				0,			KEYCODE_F8,
	"",
	"Step one instruction",
	cmd_step },
{	ALL,
	0,				0,			KEYCODE_F9,
	"",
	"Animate (trace) at speed set by last \"A\" command",
	cmd_animate },
{	ALL,
	0,				0,			KEYCODE_F10,
	"",
	"Step over instruction at cursor (ie. execute call, jsr or bsr)",
	cmd_step_over },
{	ALL,
	0,				0,			KEYCODE_F12,
	"",
	"Go!",
	cmd_go },
{	ALL,
	0,				0,			KEYCODE_ESC,
	"",
	"Go!",
	cmd_go },
/* This is the end of the list! */
{ 0,    },
};

INLINE unsigned order( unsigned offset, unsigned size )
{
	switch( size )
	{
	case 1:
		return offset;
		break;
	case 2:
		switch( ENDIAN )
		{
		case CPU_IS_LE: return UINT16_XOR_LE(offset);
		case CPU_IS_BE: return UINT16_XOR_BE(offset);
        }
		break;
	case 4:
		switch( ENDIAN )
		{
		case CPU_IS_LE: return UINT32_XOR_LE(offset);
		case CPU_IS_BE: return UINT32_XOR_BE(offset);
        }
		break;
	}
	return offset;
}

/* adjust an offset by shifting it left cpu_address_shift() times */
INLINE unsigned lshift( unsigned offset )
{
	switch( ASHIFT )
	{
		case -1: return offset / 2;
		case  3: return offset * 8;
	}
	return offset;
}

/* adjust an offset by shifting it right cpu_address_shift() times */
INLINE unsigned rshift( unsigned offset )
{
	switch( ASHIFT )
	{
		case -1: return offset * 2;
		case  3: return offset / 8;
	}
	return offset;
}


/**************************************************************************
 * dtou
 * Decimal to unsigned.
 * The pointer to the char* is placed after all consecutive digits
 * and trailing space. The pointer to int size (if given) contains the
 * number of digits found.
 **************************************************************************/
INLINE unsigned dtou( char **parg, int *size)
{
	unsigned val = 0, digit;

    if (size) *size = 0;
	while( isdigit( *(*parg) ) )
	{
		digit = *(*parg) - '0';
		val = (val * 10) + digit;
		if( size ) (*size)++;
		(*parg) += 1;
	}
	while( isspace(*(*parg)) ) *parg += 1;
	return val;
}

/**************************************************************************
 * xtou
 * Hex to unsigned.
 * The pointer to the char* is placed after all consecutive hex digits
 * and trailing space. The pointer to int size (if given) contains the
 * number of digits found.
 **************************************************************************/
INLINE unsigned xtou( char **parg, int *size)
{
	unsigned val = 0, digit;

    if (size) *size = 0;
	while( isxdigit( *(*parg) ) )
	{
		digit = toupper(*(*parg)) - '0';
		if( digit > 9 ) digit -= 7;
		val = (val << 4) | digit;
		if( size ) (*size)++;
		(*parg) += 1;
	}
	while( isspace(*(*parg)) ) *parg += 1;
	return val;
}

const char *set_ea_info( int what, unsigned value, int size, int access )
{
	static char buffer[8][63+1];
	static int which = 0;
	const char *sign = "";
	unsigned width, result;

    which = ++which % 8;

	if( access == EA_REL_PC )
		/* PC relative calls set_ea_info with value = PC and size = offset */
		result = value + size;
	else
		result = value;

    /* set source EA? */
	if( what == EA_SRC )
    {
		DBGDASM.src_ea_access = access;
		DBGDASM.src_ea_value = result;
		DBGDASM.src_ea_size = size;
    }
    else
	if( what == EA_DST )
    {
		DBGDASM.dst_ea_access = access;
		DBGDASM.dst_ea_value = result;
		DBGDASM.dst_ea_size = size;
    }
	else
	{
		return "set_ea_info: invalid <what>!";
	}

    switch( access )
	{
	case EA_VALUE:	/* Immediate value */
		switch( size )
		{
		case EA_INT8:
			width = 2;
			result &= 0xff;
			if( result & 0x80 )
			{
				sign = "-";
				result = (unsigned)-result;
			}
			break;
		case EA_INT16:
			width = 4;
			result &= 0xffff;
			if( result & 0x8000 )
			{
				sign = "-";
				result = (unsigned)-result;
			}
			break;
		case EA_INT32:
			width = 8;
			if( result & 0x80000000 )
			{
				sign = "-";
				result = (unsigned)-result;
			}
			break;
		case EA_UINT8:
			width = 2;
			result &= 0xff;
			break;
		case EA_UINT16:
			width = 4;
			result &= 0xffff;
			break;
		case EA_UINT32:
			width = 8;
			break;
		default:
			return "set_ea_info: invalid <size>!";
		}
		break;

	case EA_ZPG_RD:
	case EA_ZPG_WR:
	case EA_ZPG_RDWR:
		result &= 0xff;
		width = 2;
		break;

	case EA_ABS_PC: /* Absolute program counter change */
		result &= AMASK;
		if( size == EA_INT8 || size == EA_UINT8 )
			width = 2;
		else
		if( size == EA_INT16 || size == EA_UINT16 )
			width = 4;
		else
		if( size == EA_INT32 || size == EA_UINT32 )
			width = 8;
        else
			width = (ABITS + 3) / 4;
		break;

	case EA_REL_PC: /* Relative program counter change */
		if( dbg_dasm_relative_jumps )
		{
			if( size == 0 )
				return "$";
			if( size < 0 )
			{
				sign = "-";
				result = (unsigned) -size;
            }
			else
			{
				sign = "+";
				result = (unsigned) size;
			}
			sprintf( buffer[which], "$%s%u", sign, result );
			return buffer[which];
        }
		/* fall through */
    default:
		result &= AMASK;
		width = (ABITS + 3) / 4;
	}
	sprintf( buffer[which], "%s$%0*X", sign, width, result );
    return buffer[which];
}

/**************************************************************************
 * lower
 * Convert string into all lower case.
 **************************************************************************/
INLINE char *lower( const char *src)
{
	static char buffer[127+1];
	char *dst = buffer;
	while( *src )
		*dst++ = tolower(*src++);
	*dst = '\0';
	return buffer;
}

/**************************************************************************
 * upper
 * Convert string into all upper case.
 **************************************************************************/
INLINE char *upper( const char *src)
{
	static char buffer[127+1];
	char *dst = buffer;
	while( *src )
		*dst++ = toupper(*src++);
	*dst = '\0';
	return buffer;
}

/**************************************************************************
 * kilobyte
 * Format a byte count or size to a kilo or mega bytes string
 **************************************************************************/
INLINE char *kilobyte( unsigned bytes )
{
	static char buffer[2][31+1];
	static int which = 0;
	char *dst = buffer[which];
	which ^= 1;
	if( bytes < 1024 )
		sprintf( dst, "%u", bytes );
	else
	if( bytes < 1024 * 1024 )
		sprintf( dst, "%u.%02uK", bytes / 1024, 100 * bytes / 1024 );
	else
		sprintf( dst, "%u.%02uM", bytes / 1024 / 1024, 100 * ((bytes / 1024) % 1024) / 1024 );
	return dst;
}

/**************************************************************************
 * my_stricmp
 * Compare strings case insensitive
 **************************************************************************/
INLINE int my_stricmp( const char *dst, const char *src)
{
	while( *src && *dst )
	{
		if( tolower(*src) != tolower(*dst) ) return *dst - *src;
		src++;
		dst++;
	}
	return 0;
}

/**************************************************************************
 * get_boolean
 * Get a boolean argument (on/off, yes/no, y/n, 1/0)
 **************************************************************************/
static unsigned get_boolean( char **parg, int *size )
{
	char *p = *parg;
	unsigned result = 0;
	int length = 0;

	if( toupper(p[0]) == 'O' )
	{
		if( toupper(p[1]) == 'N' && !isalnum(p[2]) )
		{
			result = 1;
			length = 2;
			*parg += length;
		}
		else
		if( toupper(p[1]) == 'F' && toupper(p[2]) == 'F' && !isalnum(p[3]) )
		{
			result = 0;
			length = 3;
			*parg += length;
        }
    }
    else
	if( toupper(p[0]) == 'N' )
	{
		if( toupper(p[1]) == 'O' && !isalnum(p[2]) )
		{
			result = 0;
			length = 2;
			*parg += length;
		}
		else
		if( !isalnum(p[1]) )
		{
			result = 0;
			length = 1;
            *parg += length;
        }
    }
    else
	if( toupper(p[0]) == 'Y' )
	{
		if( toupper(p[1]) == 'E' && toupper(p[2]) == 'S' && !isalnum(p[3]) )
        {
			result = 1;
			length = 3;
			*parg += length;
		}
		else
		if( !isalnum(p[1]) )
		{
			result = 0;
			length = 1;
            *parg += length;
        }
    }

	if( length )
	{
		while( isspace(*(*parg)) ) *parg += 1;
	}
	else
	{
		/* found nothing yet: assume numeric */
		result = xtou( parg, &length );
	}

    if( size ) *size += length;

    return result;
}

/**************************************************************************
 * get_option_or_value
 * Get a option argument (from opt_list) or a number in the
 * range of 0 .. number of options - 1
 **************************************************************************/
static unsigned get_option_or_value( char **parg, int *size, const char *opt_list )
{
	char *p = *parg;
	const char *opt;
	unsigned result = 0, opt_count = 0;
	int length = 0;

	/* length of the next argument */
	while( isalnum(*p) ) p++;
	length = (int) (p - *parg);
	while( isspace(*p) ) p++;

	/* sacn options */
    for( opt = opt_list; *opt ; opt += strlen(opt) + 1 )
	{
		if( strncmp(*parg, opt, length) == 0 )
		{
			*parg = p;
			if( size ) *size = length;
			return opt_count;
		}
		opt_count++;
	}

	result = xtou( parg, &length );
	if( size ) *size += length;

    return result;
}

static const char *get_file_name( char **parg, int *size )
{
	static char filename[127+1];
	char *s, *d;
	int l;

	for( l = 0, s = *parg, d = filename; *s && (isalnum(*s) || ispunct(*s)); l++ )
		*d++ = *s++;

    *d = '\0';
	while( isspace(*s) ) s++;
    *parg = s;

	if( size ) *size = l;

	return filename;
}

const char *get_ea_info( unsigned pc )
{
	static char buffer[63+1];
	static char *access[EA_COUNT] =
	{
		"",     /* no EA mode */
		"#",    /* immediate */
		"=",    /* absolute PC */
		"$",    /* relative PC */
		"<",    /* zero page memory read */
		">",    /* zero page memory write */
		"*",    /* zero page memory modify */
        "<",    /* memory read */
		">",    /* memory write */
		"*",    /* memory modify */
		"P<",   /* port read */
		"P>"    /* port write */
	};

    unsigned wdst, wsrc;

	switch( DBGDASM.dst_ea_size )
	{
		case EA_INT8:	wdst = 2; break;
		case EA_INT16:	wdst = 4; break;
		case EA_INT32:	wdst = 8; break;
		case EA_UINT8:	wdst = 2; break;
		case EA_UINT16: wdst = 4; break;
		case EA_UINT32: wdst = 8; break;
        default:
			wdst = (ABITS + 3) / 4;
    }

	switch( DBGDASM.src_ea_size )
	{
		case EA_INT8:	wsrc = 2; break;
		case EA_INT16:	wsrc = 4; break;
		case EA_INT32:	wsrc = 8; break;
		case EA_UINT8:	wsrc = 2; break;
		case EA_UINT16: wsrc = 4; break;
		case EA_UINT32: wsrc = 8; break;
        default:
			wsrc = (ABITS + 3) / 4;
    }

	if( DBGDASM.dst_ea_value != INVALID && DBGDASM.src_ea_value != INVALID )
		sprintf( buffer, "%s\t%s%0*X %s%0*X",
			name_rdmem(rshift(pc)),
			access[DBGDASM.src_ea_access], wsrc, DBGDASM.src_ea_value,
			access[DBGDASM.dst_ea_access], wdst, DBGDASM.dst_ea_value );
	else
	if( DBGDASM.dst_ea_value != INVALID )
		sprintf( buffer, "%s\t%s%0*X",
			name_rdmem(rshift(pc)),
			access[DBGDASM.dst_ea_access], wdst, DBGDASM.dst_ea_value );
	else
	if( DBGDASM.src_ea_value != INVALID )
		sprintf( buffer, "%s\t%s%0*X",
			name_rdmem(rshift(pc)),
			access[DBGDASM.src_ea_access], wsrc, DBGDASM.src_ea_value );
	else
		sprintf( buffer, "%s", name_rdmem(rshift(pc)) );

    return buffer;
}

/**************************************************************************
 * get_register_id
 * Return the ID for a register if the string at *parg matches one
 * of the register names for the active cpu.
 **************************************************************************/
static unsigned get_register_id( char **parg, int *size )
{
	int i, l;
	for( i = 0; i < DBGREGS.count; i++ )
	{
		l = strlen( DBGREGS.name[i] );
		if( l > 0 && !strncmp( *parg, DBGREGS.name[i], l ) )
		{
			if( !isalnum( (*parg)[l] ) )
			{
				if( size ) *size = l;
				*parg += l;
				while( isspace(*(*parg)) ) *parg += 1;
				return DBGREGS.id[i];
			}
		}
    }
	if( size ) *size = 0;
	return 0;
}

/**************************************************************************
 * get_register_name
 * Get the name of a register with ID
 **************************************************************************/
static const char *get_register_name( int id )
{
	int i;
	for( i = 0; i < DBGREGS.count; i++ )
	{
		if( DBGREGS.id[i] == id )
			return DBGREGS.name[i];
    }
	return "??";
}

/**************************************************************************
 * get_register_or_value
 * Return the value for a register if the string at *parg matches one
 * of the register names for the active cpu. Otherwise get a hex
 * value from *parg. In both cases set the pointer int size to the
 * length of the name or digits found (if size is not NULL)
 **************************************************************************/
static unsigned get_register_or_value( char **parg, int *size )
{
	int regnum, l;

	regnum = get_register_id( parg, &l );
	if( regnum > 0 )
	{
		if( size ) *size = l;
		return cpu_get_reg( regnum );
	}
	/* default to hex value */
	return xtou( parg, size );
}

/**************************************************************************
 * trace_init
 * Creates trace output files for all CPUs
 * Resets the loop and iteration counters and the last PC array
 **************************************************************************/
static void trace_init( const char *filename, UINT8 *regs )
{
	char name[100];

    if( trace_on )
		return;

    for( tracecpu = 0; tracecpu < totalcpu; tracecpu++ )
	{
		sprintf( name, "%s.%d", filename, tracecpu );
		TRACE.file = fopen(name,"w");
		if( tracecpu == activecpu )
			memcpy( TRACE.regs, regs, MAX_REGS );
		else
			TRACE.regs[0] = 0;
		TRACE.iters = 0;
		TRACE.loops = 0;
		memset(TRACE.last_pc, 0xff, sizeof(TRACE.last_pc));
	}
	tracecpu = activecpu;
	trace_on = 1;
}

/**************************************************************************
 * trace_done
 * Closes the trace output files
 **************************************************************************/
void trace_done(void)
{
	if( !trace_on )
		return;

    for( tracecpu = 0; tracecpu < totalcpu; tracecpu++ )
	{
		if( TRACE.file )
			fclose( TRACE.file );
		TRACE.file = NULL;
	}

    trace_on = 0;
}

/**************************************************************************
 * trace_select
 * Switches tracing to the active CPU
 **************************************************************************/
static void trace_select( void )
{
	if( tracecpu == activecpu )
		return;
    if( trace_on && TRACE.file )
	{
		if( TRACE.loops )
		{
			fprintf( TRACE.file,
				"\n   (loops for %d instructions)\n\n",
				TRACE.loops );
			TRACE.loops = 0;
		}
		fprintf(TRACE.file,"\n=============== End of iteration #%d ===============\n\n",TRACE.iters++);
		fflush(TRACE.file);
	}
	if( activecpu < totalcpu )
		tracecpu = activecpu;
}

/**************************************************************************
 * trace_output
 * Outputs the next disassembled instruction to the trace file
 * Loops are detected and a loop count is output after the
 * first repetition instead of disassembling the loop over and over
 **************************************************************************/
static void trace_output( void )
{
	static char buffer[127+1];
	char *dst = buffer;

    if( trace_on && TRACE.file )
	{
		unsigned pc = cpu_get_pc();
		unsigned addr_width = (ABITS + 3) / 4;
		int count, i;

		// check for trace_loops
		for( i = count = 0; i < MAX_LOOPS; i++ )
			if( TRACE.last_pc[i] == pc )
				count++;
		if( count > 1 )
		{
			TRACE.loops++;
		}
		else
		{
			if( TRACE.loops )
			{
				dst += sprintf( dst,
					"\n   (loops for %d instructions)\n\n",
					TRACE.loops );
				TRACE.loops = 0;
			}
			if( TRACE.regs[0] )
			{
                for( i = 0; i < MAX_REGS && TRACE.regs[i]; i++ )
					dst += sprintf( dst, "%s ", cpu_dump_reg(TRACE.regs[i]) );
            }
			dst += sprintf( dst, "%0*X: ", addr_width, pc );
			cpu_dasm( dst, pc );
			strcat( dst, "\n" );
			fprintf( TRACE.file, "%s", buffer );
			memmove(
				&TRACE.last_pc[0],
				&TRACE.last_pc[1],
				(MAX_LOOPS-1)*sizeof(TRACE.last_pc[0]) );
			TRACE.last_pc[MAX_LOOPS-1] = pc;
		}
    }
}

/**************************************************************************
 * hit_brk_exec
 * Return non zero if execution breakpoint for the activecpu,
 * the temporary breakpoint or the break on 'activecpu' was hit
 **************************************************************************/
static int hit_brk_exec(void)
{
	static char dbg_info[63+1];
    UINT32 pc = cpu_get_pc();

	if( DBG.brk_temp != INVALID && DBG.brk_temp == pc )
	{
		sprintf( dbg_info, "Hit temp breakpoint at $%X", DBG.brk_temp);
		dbg_info_once = dbg_info;
		return 1;
	}
	if( DBG.brk_exec != INVALID && DBG.brk_exec == pc )
	{
		if( DBG.brk_exec_times > 0 )
		{
			if( --DBG.brk_exec_times == 0 )
			{
				sprintf( dbg_info, "Hit exec breakpoint %d times", DBG.brk_exec_reset);
				dbg_info_once = dbg_info;
				return 1;
            }
			return 0;
		}
		sprintf( dbg_info, "Hit exec breakpoint at $%X", DBG.brk_exec);
		dbg_info_once = dbg_info;
		return 1;
	}

    return 0;
}

/**************************************************************************
 * hit_brk_data
 * Return non zero if the data watchpoint for the activecpu
 * was hit (ie. monitored data changed)
 **************************************************************************/
static int hit_brk_data(void)
{
	static char dbg_info[63+1];
	UINT32 data;

	if( DBG.brk_data == INVALID ) return 0;

	data = RDMEM(DBG.brk_data);

	if( DBG.brk_data_oldval != data )
	{
		DBG.brk_data_oldval = data;
		if( DBG.brk_data_newval != INVALID )
		{
			if( DBG.brk_data_newval == data )
			{
				sprintf( dbg_info, "Hit data watchpoint at $%X value $%X", DBG.brk_data, DBG.brk_data_newval);
                dbg_info_once = dbg_info;
                return 1;
			}
			return 0;
		}
		sprintf( dbg_info, "Hit data watchpoint at $%X", DBG.brk_data);
		dbg_info_once = dbg_info;
        return 1;
	}
	return 0;
}


/**************************************************************************
 * hit_brk_regs
 * Return non zero if the register breakpoint for the active CPU
 * was hit (ie. monitored register changed)
 **************************************************************************/
static int hit_brk_regs(void)
{
	static char dbg_info[63+1];
	UINT32 data;

	if( DBG.brk_regs == INVALID ) return 0;

	data = cpu_get_reg(DBG.brk_regs);

	if( DBG.brk_regs_oldval != data )
	{
		DBG.brk_regs_oldval = data;
		if( DBG.brk_regs_newval != INVALID )
		{
			if( DBG.brk_regs_newval == (data & DBG.brk_regs_mask) )
			{
				if( DBG.brk_regs_mask != 0xffffffff )
					sprintf( dbg_info, "Hit register %s & $%X watchpoint value $%X", get_register_name(DBG.brk_regs), DBG.brk_regs_mask, DBG.brk_regs_newval);
				else
					sprintf( dbg_info, "Hit register %s watchpoint value $%X", get_register_name(DBG.brk_regs), DBG.brk_regs_newval);
                dbg_info_once = dbg_info;
                return 1;
			}
			return 0;
		}
		sprintf( dbg_info, "Hit register %s watchpoint", get_register_name(DBG.brk_regs));
		dbg_info_once = dbg_info;
        return 1;
	}
	return 0;
}


/**************************************************************************
 * name_rom
 * Find the name for a rom from the drivers list
 **************************************************************************/
static const char *name_rom( const char *type, int region, unsigned *base, unsigned start )
{
    const struct RomModule *romp = Machine->gamedrv->rom;
	unsigned offset = *base;

    while( romp->name || romp->offset || romp->length )
    {
		romp++; /* skip memory region definition */

        while( romp->length )
        {
			const char *name;
			int length;

			name = romp->name;
            length = 0;

            do
            {
                /* ROM_RELOAD */
                if (romp->name == (char *)-1)
                    length = 0; /* restart */

                length += romp->length & ~ROMFLAG_MASK;

                romp++;

            } while (romp->length && (romp->name == 0 || romp->name == (char *)-1));

			/* region found already ? */
			if( region == 0 )
			{
				/* address inside that range ? */
				if( offset < length )
				{
					/* put back that offset */
					*base = offset;
					return name;
				}
				/* subtract length of that ROM */
				offset -= length;
			}

        }
		--region;
    }

    /* default to ROM + xxxx (base - start) */
	*base -= start;
	return type;
}

/**************************************************************************
 * name_rdmem
 * Find a descriptive name for the given memory read region of activecpu
 **************************************************************************/
static const char *name_rdmem( unsigned base )
{
	static char buffer[16][79+1];
    static int which = 0;
	const struct MachineCPU *cpu = &Machine->drv->cpu[activecpu];
	const struct MemoryReadAddress *mr = cpu->memory_read;
	int ram_cnt = 1, nop_cnt = 1;
    const char *name;
	char *dst;

	which = ++which % 16;
	dst = buffer[which];
	*dst = '\0';

	while( *dst == '\0' && mr->start != -1 )
    {
        if( base >= mr->start && base <= mr->end )
        {
			unsigned offset = base - mr->start;

//			if( mr->description )
//				sprintf(dst, "%s+%04X", mr->description, lshift(offset) );
//			else
//			if( mr->base && *mr->base == videoram )
//				sprintf(dst, "video+%04X", lshift(offset) );
//			else
//			if( mr->base && *mr->base == colorram )
//				sprintf(dst, "color+%04X", lshift(offset) );
//			else
//			if( mr->base && *mr->base == spriteram )
//				sprintf(dst, "sprite+%04X", lshift(offset) );
//			else
			switch( (FPTR)mr->handler )
            {
			case (FPTR)MRA_RAM:
				sprintf(dst, "RAM%d+%04X", ram_cnt, lshift(offset) );
				break;
			case (FPTR)MRA_ROM:
				name = name_rom("ROM", cpu->memory_region, &base, mr->start );
				sprintf(dst, "%s+%04X", name, lshift(base) );
				break;
			case (FPTR)MRA_BANK1:
				sprintf(dst, "BANK1+%04X", lshift(offset) );
				break;
			case (FPTR)MRA_BANK2:
				sprintf(dst, "BANK2+%04X", lshift(offset) );
				break;
			case (FPTR)MRA_BANK3:
				sprintf(dst, "BANK3+%04X", lshift(offset) );
				break;
			case (FPTR)MRA_BANK4:
				sprintf(dst, "BANK4+%04X", lshift(offset) );
				break;
			case (FPTR)MRA_BANK5:
				sprintf(dst, "BANK5+%04X", lshift(offset) );
				break;
			case (FPTR)MRA_BANK6:
				sprintf(dst, "BANK6+%04X", lshift(offset) );
				break;
			case (FPTR)MRA_BANK7:
				sprintf(dst, "BANK7+%04X", lshift(offset) );
				break;
			case (FPTR)MRA_BANK8:
				sprintf(dst, "BANK8+%04X", lshift(offset) );
				break;
			case (FPTR)MRA_NOP:
				sprintf(dst, "NOP%d+%04X", nop_cnt, lshift(offset) );
				break;
			default:
				if( (FPTR)mr->handler == (FPTR)input_port_0_r )
					sprintf(dst, "input_port_0+%04X", lshift(offset) );
				else
				if( (FPTR)mr->handler == (FPTR)input_port_1_r )
					sprintf(dst, "input_port_1+%04X", lshift(offset) );
				else
				if( (FPTR)mr->handler == (FPTR)input_port_2_r )
					sprintf(dst, "input_port_2+%04X", lshift(offset) );
				else
				if( (FPTR)mr->handler == (FPTR)input_port_3_r )
					sprintf(dst, "input_port_3+%04X", lshift(offset) );
				else
				if( (FPTR)mr->handler == (FPTR)input_port_4_r )
					sprintf(dst, "input_port_4+%04X", lshift(offset) );
				else
				if( (FPTR)mr->handler == (FPTR)input_port_5_r )
					sprintf(dst, "input_port_5+%04X", lshift(offset) );
				else
				if( (FPTR)mr->handler == (FPTR)input_port_6_r )
					sprintf(dst, "input_port_6+%04X", lshift(offset) );
				else
				if( (FPTR)mr->handler == (FPTR)input_port_7_r )
					sprintf(dst, "input_port_7+%04X", lshift(offset) );
				else
				if( (FPTR)mr->handler == (FPTR)input_port_8_r )
					sprintf(dst, "input_port_8+%04X", lshift(offset) );
				else
				if( (FPTR)mr->handler == (FPTR)input_port_9_r )
					sprintf(dst, "input_port_9+%04X", lshift(offset) );
				else
				if( (FPTR)mr->handler == (FPTR)input_port_10_r )
					sprintf(dst, "input_port_10+%04X", lshift(offset) );
				else
				if( (FPTR)mr->handler == (FPTR)input_port_11_r )
					sprintf(dst, "input_port_11+%04X", lshift(offset) );
				else
				if( (FPTR)mr->handler == (FPTR)input_port_12_r )
					sprintf(dst, "input_port_12+%04X", lshift(offset) );
				else
				if( (FPTR)mr->handler == (FPTR)input_port_13_r )
					sprintf(dst, "input_port_13+%04X", lshift(offset) );
				else
				if( (FPTR)mr->handler == (FPTR)input_port_14_r )
					sprintf(dst, "input_port_14+%04X", lshift(offset) );
				else
				if( (FPTR)mr->handler == (FPTR)input_port_15_r )
					sprintf(dst, "input_port_15+%04X", lshift(offset) );
            }
        }
        switch( (FPTR)mr->handler )
        {
			case (FPTR)MRA_RAM: ram_cnt++; break;
			case (FPTR)MRA_NOP: nop_cnt++; break;
        }
        mr++;
    }

	return dst;
}

/**************************************************************************
 * name_wrmem
 * Find a descriptive name for the given memory write region of activecpu
 **************************************************************************/
static const char *name_wrmem( unsigned base )
{
    static char buffer[16][79+1];
    static int which = 0;
	const struct MachineCPU *cpu = &Machine->drv->cpu[activecpu];
    const struct MemoryWriteAddress *mw = cpu->memory_write;
	int ram_cnt = 1, nop_cnt = 1;
	const char *name;
    char *dst;

    which = ++which % 16;
    dst = buffer[which];
	*dst = '\0';

    ram_cnt = nop_cnt = 1;
	while( *dst == '\0' && mw->start != -1 )
    {
        if( base >= mw->start && base <= mw->end )
        {
//			if( mw->description )
//				sprintf(dst, "%s+%04X", mw->description, lshift(base - mw->start) );
//			else
			if( mw->base && *mw->base == videoram )
				sprintf(dst, "video+%04X", lshift(base - mw->start) );
			else
			if( mw->base && *mw->base == colorram )
				sprintf(dst, "color+%04X", lshift(base - mw->start) );
			else
			if( mw->base && *mw->base == spriteram )
				sprintf(dst, "sprite+%04X", lshift(base - mw->start) );
            else
            switch( (FPTR)mw->handler )
            {
			case (FPTR)MWA_RAM:
				sprintf(dst, "RAM%d+%04X", ram_cnt, lshift(base - mw->start) );
				break;
			case (FPTR)MWA_ROM:
				name = name_rom("ROM", cpu->memory_region, &base, mw->start );
				sprintf(dst, "%s+%04X", name, lshift(base) );
				break;
			case (FPTR)MWA_RAMROM:
				name = name_rom("RAMROM", cpu->memory_region, &base, mw->start);
				sprintf(dst, "%s+%04X", name, lshift(base) );
				break;
			case (FPTR)MWA_BANK1:
				sprintf(dst, "BANK1+%04X", lshift(base - mw->start) );
				break;
			case (FPTR)MWA_BANK2:
				sprintf(dst, "BANK2+%04X", lshift(base - mw->start) );
				break;
			case (FPTR)MWA_BANK3:
				sprintf(dst, "BANK3+%04X", lshift(base - mw->start) );
				break;
			case (FPTR)MWA_BANK4:
				sprintf(dst, "BANK4+%04X", lshift(base - mw->start) );
				break;
			case (FPTR)MWA_BANK5:
				sprintf(dst, "BANK5+%04X", lshift(base - mw->start) );
				break;
			case (FPTR)MWA_BANK6:
				sprintf(dst, "BANK6+%04X", lshift(base - mw->start) );
				break;
			case (FPTR)MWA_BANK7:
				sprintf(dst, "BANK7+%04X", lshift(base - mw->start) );
				break;
			case (FPTR)MWA_BANK8:
				sprintf(dst, "BANK8+%04X", lshift(base - mw->start) );
				break;
			case (FPTR)MWA_NOP:
				sprintf(dst, "NOP%d+%04X", nop_cnt, lshift(base - mw->start) );
				break;
			}
        }
        switch( (FPTR)mw->handler )
        {
            case (FPTR)MRA_RAM: ram_cnt++; break;
            case (FPTR)MRA_NOP: nop_cnt++; break;
        }
        mw++;
    }

    return dst;
}

/**************************************************************************
 * name_memory
 * Find a descriptive name for the given memory region of activecpu
 **************************************************************************/
static const char *name_memory( unsigned base )
{
	static char buffer[8][79+1];
    static int which = 0;
	const char *rd, *wr;

	/* search readmem and writemem names */
    rd = name_rdmem( base );
	wr = name_wrmem( base );

	/* both empty, so it's no specific region */
    if( *rd == '\0' && *wr == '\0' )
	{
		which = ++which % 8;
		sprintf(buffer[which], "N/A:%04X", base);
        return buffer[which];
    }

	which = ++which % 8;

    /* both names differ? */
    if( strcmp(rd,wr) )
		/* well, return one of the names... */
        sprintf(buffer[which], "%s\t%s", rd, wr);
	else
		/* return the name for readmem... */
		sprintf(buffer[which], "%s", rd);

    return buffer[which];
}

/**************************************************************************
 * win_create
 * Wrapper function to fill a struct sWindow and call win_open()
 **************************************************************************/
static int win_create(int n, UINT8 prio, int x, int y, int w, int h,
	UINT8 co_text, UINT8 co_frame, UINT8 chr, UINT32 attributes)
{
    struct sWindow win;
	/* fill in the default values for window creation */
	memset( &win, 0, sizeof(struct sWindow) );
	win.filler = chr;
	win.prio = prio;
	win.x = x;
	win.y = y;
	win.w = w;
	win.h = h;
	win.flags = NO_SCROLL | NO_WRAP | BORDER_TOP | ((n)? HIDDEN : 0) | attributes;
	win.co_text = co_text;
	win.co_frame = co_frame;
	win.co_title = cur_col[E_TITLE];
	win.saved_text = ' ';
	win.saved_attr = WIN_WHITE;
	return win_open(n, &win);
}

static int DECL_SPEC win_msgbox( UINT8 color, const char *title, const char *fmt, ... )
{
	UINT32 win = WIN_MSGBOX;
    va_list arg;
	int i;

	win_create( win, 0,
		4,6,60,3, color, cur_col[E_FRAME], ' ',
		BORDER_TOP | BORDER_LEFT | BORDER_RIGHT | BORDER_BOTTOM | SHADOW );
	win_set_title( win, title );

    va_start( arg, fmt );
	win_vprintf( win, fmt, arg );
	va_end( arg );

    win_show( win );
    i = keyboard_read_sync();
    win_close( win );

    return i;
}

/**************************************************************************
 * dbg_set_rect
 * set a rectangle from x,y,w and h
 **************************************************************************/
INLINE void dbg_set_rect( struct rectangle *r, int x, int y, int w, int h )
{
	r->min_x = x;
	r->max_x = x + w - 1;
	r->min_y = y;
	r->max_y = y + h - 1;
}

/**************************************************************************
 * dbg_open_windows
 * Depending on the CPU type, create a window layout specified
 * by the CPU core - returned by function cputype_win_layout()
 **************************************************************************/
static void dbg_open_windows( void )
{
    UINT32 flags;
	UINT32 i, w, h, aw, ah;

    /* Initialize windowing engine */
	osd_get_screen_size( &w, &h );
	win_init_engine( w, h );

	/* anything more than 80x25 available? */
	aw = w - 80;
	ah = h - 25;

    for( i = 0; i < totalcpu; i++ )
	{
		const UINT8 *win_layout = (UINT8*)cpunum_win_layout(i);
		struct rectangle regs, dasm, mem1, mem2, cmds;

        #define REGS_X  win_layout[0*4+0]
		#define REGS_Y	win_layout[0*4+1]
		#define REGS_W	win_layout[0*4+2]
		#define REGS_H	win_layout[0*4+3]
		#define DASM_X	win_layout[1*4+0]
		#define DASM_Y	win_layout[1*4+1]
		#define DASM_W	win_layout[1*4+2]
		#define DASM_H	win_layout[1*4+3]
		#define MEM1_X	win_layout[2*4+0]
		#define MEM1_Y	win_layout[2*4+1]
		#define MEM1_W	win_layout[2*4+2]
		#define MEM1_H	win_layout[2*4+3]
		#define MEM2_X	win_layout[3*4+0]
		#define MEM2_Y	win_layout[3*4+1]
		#define MEM2_W	win_layout[3*4+2]
		#define MEM2_H	win_layout[3*4+3]
		#define CMDS_X	win_layout[4*4+0]
		#define CMDS_Y	win_layout[4*4+1]
		#define CMDS_W	win_layout[4*4+2]
		#define CMDS_H	win_layout[4*4+3]

		/* cmds window is fixed w and h, always at the bottom */
		dbg_set_rect(&cmds, CMDS_X,CMDS_Y+ah,CMDS_W+aw,CMDS_H);
		if( DASM_Y == 0 )
		{
			if( DASM_H + 1 == CMDS_Y )
			{
				/********************
				 * dasm   * regs	*
				 *		  ***********
				 *		  * mem1	*
				 *		  ***********
				 *		  * mem2	*
				 ********************
				 * cmds 			*
				 ********************/
				 dbg_set_rect(&regs, REGS_X+aw,REGS_Y,REGS_W,REGS_H);
				 dbg_set_rect(&dasm, DASM_X,DASM_Y,DASM_W+aw,DASM_H+ah);
				 dbg_set_rect(&mem1, MEM1_X+aw,MEM1_Y,MEM1_W,MEM1_H+(ah+1)/2);
				 dbg_set_rect(&mem2, MEM2_X+aw,MEM2_Y+(ah+1)/2,MEM2_W,MEM2_H+ah/2);
            }
			else
			if( MEM1_X == MEM2_X )
			{
				/********************
				 * dasm   * regs	*
				 ********** 		*
				 * mem1   * 		*
				 ********** 		*
				 * mem2   * 		*
				 ********************
				 * cmds 			*
                 ********************/
                 dbg_set_rect(&regs, REGS_X+aw,REGS_Y,REGS_W,REGS_H);
				 dbg_set_rect(&dasm, DASM_X,DASM_Y,DASM_W+aw,DASM_H);
				 dbg_set_rect(&mem1, MEM1_X,MEM1_Y,MEM1_W+aw,MEM1_H+(ah+1)/2);
				 dbg_set_rect(&mem2, MEM2_X,MEM2_Y+(ah+1)/2,MEM2_W+aw,MEM2_H+ah/2);
            }
			else
			{
				/********************
				 * dasm   * regs	*
				 *		  * 		*
				 *		  * 		*
				 ********************
				 * mem1   * mem2	*
				 ********************
				 * cmds 			*
                 ********************/
                 dbg_set_rect(&regs, REGS_X+aw,REGS_Y,REGS_W,REGS_H);
				 dbg_set_rect(&dasm, DASM_X,DASM_Y,DASM_W+aw,DASM_H+(ah+1)/2);
				 dbg_set_rect(&mem1, MEM1_X,MEM1_Y+(ah+1)/2,MEM1_W+aw,MEM1_H+ah/2);
				 dbg_set_rect(&mem2, MEM2_X+aw,MEM2_Y,MEM2_W,MEM2_H+ah);
            }
        }
		else
		{
			/********************
			 * regs 			*
			 ********************
			 * dasm   * mem1	*
			 *		  ***********
			 *		  * mem2	*
			 ********************
			 * cmds 			*
			 ********************/
			 dbg_set_rect(&regs, REGS_X,REGS_Y,REGS_W+aw,REGS_H);
			 dbg_set_rect(&dasm, DASM_X,DASM_Y,DASM_W+(aw+1)/2,DASM_H+ah);
			 dbg_set_rect(&mem1, MEM1_X+(aw+1)/2,MEM1_Y,MEM1_W+aw/2,MEM1_H+(ah+1)/2);
			 dbg_set_rect(&mem2, MEM2_X+(aw+1)/w,MEM2_Y+(ah+1)/2,MEM2_W+aw/2,MEM2_H+ah/2);
        }

        flags = BORDER_TOP;
        if( regs.max_x + 1 < w ) flags |= BORDER_RIGHT;
        win_create(WIN_REGS(i), 1,
            regs.min_x,regs.min_y,
            regs.max_x+1-regs.min_x,regs.max_y+1-regs.min_y,
			cur_col[E_REGS], cur_col[E_FRAME], ' ', flags );

		flags = BORDER_TOP | BORDER_RIGHT;
		win_create(WIN_DASM(i), 1,
			dasm.min_x, dasm.min_y,
			dasm.max_x+1-dasm.min_x,dasm.max_y+1-dasm.min_y,
			cur_col[E_DASM], cur_col[E_FRAME], ' ', flags );

		flags = BORDER_TOP;
		if( mem1.max_x + 1 < w ) flags |= BORDER_RIGHT;
		win_create(WIN_MEM1(i), 1,
			mem1.min_x,mem1.min_y,
			mem1.max_x+1-mem1.min_x,mem1.max_y+1-mem1.min_y,
			cur_col[E_MEM1], cur_col[E_FRAME], ' ', flags );

		flags = BORDER_TOP;
		if( mem2.max_x + 1 < w) flags |= BORDER_RIGHT;
		win_create(WIN_MEM2(i), 1,
			mem2.min_x,mem2.min_y,
			mem2.max_x+1-mem2.min_x,mem2.max_y+1-mem2.min_y,
			cur_col[E_MEM2], cur_col[E_FRAME], ' ', flags );

		flags = BORDER_TOP;
		win_create(WIN_CMDS(i), 1,
			cmds.min_x,cmds.min_y,
			cmds.max_x+1-cmds.min_x,cmds.max_y+1-cmds.min_y,
			cur_col[E_CMDS], cur_col[E_FRAME], ' ', flags );

        win_set_title(WIN_CMDS(i), "Command (press F1 for help)");
	}
}

/**************************************************************************
 * dbg_close_windows
 * Close all windows and shut down the window engine
 **************************************************************************/
static void dbg_close_windows( void )
{
	int i;

    for( i = 0; i < totalcpu; i++ )
	{
		win_close( WIN_REGS(i) );
		win_close( WIN_DASM(i) );
		win_close( WIN_MEM1(i) );
		win_close( WIN_MEM2(i) );
		win_close( WIN_CMDS(i) );
	}
	win_exit_engine();
}

/**************************************************************************
 * dasm_line
 * disassemble <times> instructions from pc and return the final pc
 **************************************************************************/
static unsigned dasm_line( unsigned pc, int times )
{
	static char buffer[127+1];

    while( times-- > 0 )
		pc += cpu_dasm( buffer, pc );
	pc = lshift( rshift(pc) & AMASK );

    return pc;
}


/**************************************************************************
 * dump_regs
 * Update the register display
 * Compare register values against the ones stored in reg->backup[]
 * Store new values in reg->newval[] which is copied to reg->backup[]
 * before the next instruction is executed (at the end of MAME_Debug).
 **************************************************************************/
static void dump_regs( void )
{
	char title[80+1];
	UINT32 win = WIN_REGS(activecpu);
	s_regs *regs = &DBGREGS;
	s_edit *pedit = regs->edit;
	UINT32 *old = regs->backup;
	UINT32 *val = regs->newval;
	UINT32 width;
	const char *name = cpu_name(), *flags = cpu_flags();
    int w = win_get_w(win);
	int h = win_get_h(win);
    int i, j, l;
	UINT8 color;
	const INT8 *reg = (INT8*)cpu_reg_layout();

	/* Called the very first time: find max_width */
    if( regs->count == 0 )
	{
		for(i = 0; reg[i]; i++)
		{
			if( reg[i] == -1 )
				continue;		/* skip row breaks */
			width = strlen( cpu_dump_reg(reg[i]) );
			if( width >= regs->max_width )
				regs->max_width = width + 1;
		}
	}

    win_set_curpos( win, 0, 0 );
    sprintf( title, "CPU #%d %-8s Flags:%s  Cycles:%6u", activecpu, name, flags, cpu_geticount() );
	l = strlen(title);
	if( l + 2 < w )
	{
        /* Everything should fit into the caption */
		if( l + 4 < w )
			/* We can even separate the cycles to the right corner */
			sprintf( title, "CPU #%d %-8s Flags:%s\tCycles:%6u", activecpu, name, flags, cpu_geticount() );
		win_set_title( win, title );
	}
	else
	{
		/* At least CPU # and flags should fit into the caption */
		sprintf( title, "CPU #%d %-8s Flags:%s", activecpu, name, flags );
		l = strlen(title);
		if( l + 2 < w )
		{
			if( l + 4 < w )
				sprintf( title, "CPU #%d %-8s\tFlags:%s", activecpu, name, flags );
            win_set_title( win, title );
			win_printf( win, "Cycles:%6u\n", cpu_geticount() );
			if( --h <= 0) return;
		}
		else
		{
			sprintf( title, "CPU #%d %-8s Cyc:%6u", activecpu, name, cpu_geticount() );
			l = strlen(title);
			if( l + 2 < w )
			{
				if( l + 4 < w )
					sprintf( title, "CPU #%d %-8s\tCyc:%6u", activecpu, name, cpu_geticount() );
				win_set_title( win, title );
				if( strlen(cpu_flags()) + 8 < w )
					win_printf( win, "Flags: %s\n", flags );
				else
				if( strlen(cpu_flags()) + 2 < w )
					win_printf( win, "F:%s\n", flags );
				else
                    win_printf( win, "%s\n", flags );
                if( --h <= 0) return;
            }
			else
			{
				/* Only CPU # and name fit into the caption */
				sprintf( title, "CPU #%d %-8s", activecpu, name );
				l = strlen(title);
				win_set_title( win, title );
				if( strlen(cpu_flags()) + 8 < w )
					win_printf( win, "Flags: %s\n", flags );
				else
				if( strlen(cpu_flags()) + 2 < w )
					win_printf( win, "F:%s\n", flags );
				else
					win_printf( win, "%s\n", flags );
				if( --h <= 0) return;
				win_printf( win, "Cycles:%6u\n", cpu_geticount() );
				if( --h <= 0) return;
			}
		}
	}

    for( i = 0, j = 0; *reg; i++, reg++ )
	{
		if( *reg == -1 )
		{
			win_erase_eol( win, ' ' );
			win_putc( win, '\n');
			if( --h <= 0 ) return;
		}
		else
		{
			name = cpu_dump_reg(*reg);
			if( *name == '\0' )
				continue;

            regs->id[j] = *reg;
			*val = cpu_get_reg(regs->id[j]);
			color = cur_col[E_REGS];
			if( DBG.brk_regs == *reg )
				color = cur_col[E_BRK_REGS];
            if( *val != *old )
			{
				regs->changed = 1;
				color = (color & 0xf0) | cur_col[E_CHANGES];
			}
			win_set_color( win, color );

			/* edit structure not yet initialized? */
			if( regs->count == 0 )
			{
				char *p;
				/* Get the cursor position */
				pedit->x = win_get_cx(win);
				pedit->y = win_get_cy(win);
				strncpy( regs->name[j], name, sizeof(regs->name[j]) - 1 );
				if( strlen(name) >= regs->max_width )
					regs->max_width = strlen(name) + 1;
				/* Find a colon */
				p = strchr( regs->name[j], ':');
				if( p )
				{
                    pedit->w = strlen( p + 1 );
				}
				else
				{
					/* Or else find an apostrophe */
                    p = strchr( regs->name[j], '\'' );
					if( p )
					{
						/* Include the apostrophe in the name! */
                        ++p;
						pedit->w = strlen( p );
                    }
				}
				/* TODO: other characters to delimit a register name from it's value? */
				if( p )
				{
					/* length of the name (total length - length of nibbles) */
					pedit->n = strlen( name ) - pedit->w;
					/* terminate name at (or after) the delimiting character */
					*p = '\0';
					/* eventually strip trailing spaces */
					while( *--p == ' ' ) *p = '\0';
				}
				else
				{
					/* this is certainly wrong :( */
					pedit->w = strlen(regs->name[j]);
					pedit->n = 0;
				}
			}

			win_printf( win, "%s", name );

			win_set_color( win, cur_col[E_REGS] );
            /* If no row break follows, advance to the next tab stop */
			if( reg[1] != -1 )
				win_printf( win, "%*s", regs->max_width - pedit->w - pedit->n, "" );
            pedit++;
			val++;
			old++;
			j++;
		}
	}
	win_erase_eol( win, ' ' );

	/* Set the total count of registers */
	regs->count = j;
}

/**************************************************************************
 * dump_dasm
 * Update the disassembly display
 **************************************************************************/
static unsigned dump_dasm( unsigned pc )
{
    UINT32 win = WIN_DASM(activecpu);
    int w = win_get_w(win);
    int h = win_get_h(win);
	int x, y, l, line_pc_cpu = INVALID, line_pc_cur = INVALID;
    UINT8 color;
	char dasm[127+1];
	unsigned pc_first = pc, pc_next;
	unsigned width = (ABITS + 3) / 4;

	while( line_pc_cpu == INVALID )
	{
		pc = pc_first;

		for( y = 0; y < h; y++ )
		{
			win_set_curpos( win, 0, y );
			if( pc == DBG.brk_exec )
				color = cur_col[E_BRK_EXEC];
			else
				color = cur_col[E_DASM];
			if( pc == DBGDASM.pc_cpu )
			{
				color = (color & 0x0f) | (cur_col[E_PC] & 0xf0);
				line_pc_cpu = y;
			}
			if( pc == DBGDASM.pc_cur )
			{
				color = (color & 0x0f) | (cur_col[E_CURSOR] & 0xf0);
				line_pc_cur = y;
			}
            win_set_color( win, color );
			l = win_printf( win, "%0*X: ", width, pc );

			DBGDASM.dst_ea_value = INVALID;
			DBGDASM.src_ea_value = INVALID;
			pc_next = pc + cpu_dasm( dasm, pc );

			if( DBGDASM.pc_cur == pc )
				win_set_title( win, "%s", get_ea_info(pc) );

			if( dbg_dasm_opcodes )
			{
				unsigned p = rshift(pc);
				unsigned n = rshift(pc_next);
				switch( ALIGN )
				{
				case 1:
					for( x = 0; x < INSTL; x++ )
					{
						if ( p < n )
						{
							l += win_printf( win, "%02X ",
								RDMEM(order(p,1)) );
							p++;
						}
						else l += win_printf( win, "   " );
					}
					break;
				case 2:
					for( x = 0; x < INSTL; x += 2 )
					{
						if ( p < n )
						{
							l += win_printf( win, "%02X%02X ",
								RDMEM(order(p+0,2)), RDMEM(order(p+1,2)) );
							p += 2;
						}
						else l += win_printf( win, "     " );
					}
					break;
				case 4:
					for( x = 0; x < INSTL; x += 4 )
					{
						if ( p < n)
						{
							l += win_printf( win, "%02X%02X%02X%02X ",
								RDMEM(order(p+0,4)), RDMEM(order(p+1,4)),
								RDMEM(order(p+2,4)), RDMEM(order(p+3,4)) );
							p += 4;
						}
						else l += win_printf( win, "         " );
					}
					break;
                }
			}
			pc = lshift(rshift(pc_next) & AMASK);
            switch( dbg_dasm_case )
			{
				case 0: win_printf( win, "%-*.*s", w-l, w-l, dasm ); break;
				case 1: win_printf( win, "%-*.*s", w-l, w-l, lower(dasm) ); break;
				case 2: win_printf( win, "%-*.*s", w-l, w-l, upper(dasm) ); break;
			}
		}
		if( line_pc_cpu == INVALID )
		{
			/*
			 * We didn't find the exact instruction of the CPU PC.
			 * This has to be caused by a jump into the midst of
			 * another instruction down from the top. If the CPU PC
			 * is between pc_first and pc (end), try again on next
			 * instruction size boundary, else bail out...
			 */
			if( DBGDASM.pc_cpu > pc_first && DBGDASM.pc_cpu < pc )
				pc_first += ALIGN;
			else
				line_pc_cpu = 0;
		}
	}

	win_set_curpos( win, 0, line_pc_cur );

    return pc;
}

/**************************************************************************
 * dump_mem_hex
 * Update a memory window using the cpu_readmemXXX function
 * Changed values are displayed using foreground color cur_col[E_CHANGES]
 * The new values are stored into mem->newval[] of the activecpu
 **************************************************************************/
static void dump_mem_hex( int which, unsigned len_addr, unsigned len_data )
{
	UINT32 win = WIN_MEM(activecpu,which);
	s_edit *pedit = DBGMEM[which].edit;
	int w = win_get_w(win);
	int h = win_get_h(win);
	UINT8 *old = DBGMEM[which].backup;
	UINT8 *val = DBGMEM[which].newval;
	UINT8 color, dim_bright = 0;
	UINT8 spc_left = 0; 	/* assume no space left of address */
	UINT8 spc_addr = 0; 	/* assume no space after address */
	UINT8 spc_data = 1; 	/* assume one space between adjacent data elements */
	UINT8 spc_hyphen = 0;	/* assume no space around center hyphen */
	unsigned offs, column;

	/* how many elements (bytes,words,dwords) will fit in a line? */
	DBGMEM[which].width = (w - len_addr - 1) / (len_data + spc_data);

	/* display multiples of eight bytes per line only */
	if( DBGMEM[which].width > ((16/len_data)-1) )
		DBGMEM[which].width &= ~((16/len_data)-1);

	/* Is bytes per line not divideable by eight? */
	if( dbg_mem_squeezed && (DBGMEM[which].width & 7) )
	{
		/* We try an alternating dim,bright layout w/o data spacing */
		spc_data = 0;
		/* how many bytes will fit in a line? */
		DBGMEM[which].width = (w - len_addr - 1) / len_data;
		/* display multiples of eight data elements per line only */
		if( DBGMEM[which].width > ((16/len_data)-1) )
			DBGMEM[which].width &= ~((16/len_data)-1);
		dim_bright = 0x08;
	}

	/* calculate number of bytes per line */
	DBGMEM[which].bytes = DBGMEM[which].width * len_data / 2;
	/* calculate the DBGMEM[which].size using that data width */
	DBGMEM[which].size = DBGMEM[which].bytes * h;

	/* will a space after the address fit into the line? */
	if( ( len_addr + spc_addr + DBGMEM[which].width * (len_data + spc_data) + 1 ) < w )
		spc_addr = 1;

	/* will two spaces around the center hyphen fit into the line ? */
	if( ( len_addr + spc_addr + DBGMEM[which].width * (len_data + spc_data) + 2 ) < w )
		spc_hyphen = 1;

	while( ( 2*spc_left + len_addr + spc_addr + DBGMEM[which].width * (len_data + spc_data) - 1 + spc_hyphen ) + 2 < w )
		spc_left++;

	win_set_curpos( win, 0, 0 );

	for( offs = 0, column = 0; offs < DBGMEM[which].size; offs++, old++, val++ )
	{
		color = cur_col[E_MEM1+which];
		switch( len_data )
		{
		case 2: /* UINT8 mode */
			DBGMEM[which].address = (DBGMEM[which].base + order(offs,1)) & AMASK;
			break;
		case 4: /* UINT16 mode */
			DBGMEM[which].address = (DBGMEM[which].base + order(offs,2)) & AMASK;
			break;
		case 8: /* UINT32 mode */
			DBGMEM[which].address = (DBGMEM[which].base + order(offs,4)) & AMASK;
			break;
		}

        if( column == 0 )
        {
			win_set_color( win, cur_col[E_MEM1+which] );
			win_printf( win, "%*s%0*X:%*s",
				spc_left, "", len_addr, lshift((DBGMEM[which].base + offs) & AMASK), spc_addr, "" );
		}

		if( DBGMEM[which].address == DBG.brk_data )
			color = cur_col[E_BRK_DATA];

		*val = RDMEM( DBGMEM[which].address );

        if( *val != *old )
		{
			DBGMEM[which].changed = 1;
			color = (color & 0xf0) | (cur_col[E_CHANGES] & 0x0f);
		}

		if( (column * 2 / len_data) & 1 )
			color ^= dim_bright;

		/* edit structure not yet initialized? */
		if( pedit->w == 0 )
		{
			/* store memory edit x,y */
			pedit->x = win_get_cx( win );
			pedit->y = win_get_cy( win );
			pedit->w = 2;
			pedit->n = order(column % (len_data / 2), len_data / 2);
		}
		pedit++;

		win_set_color( win, color );
		switch( DBGMEM[which].ascii )
		{
			case 0: /* hex */
				win_printf( win, "%02X", *val );
				break;
			case 1: /* translated */
				win_printf( win, "%c ", trans_table[*val] );
				break;
			case 2: /* plain character, except for \r, \n and \t */
				if( *val == '\0' || *val == '\b' || *val == '\t' ||
					*val == '\r' || *val == '\n' )
					win_printf( win, ". " );
				else
                    win_printf( win, "%c ", *val );
				break;
		}

		if( ++column < DBGMEM[which].bytes )
		{
			win_set_color( win, cur_col[E_MEM1+which] );
			if( column == DBGMEM[which].bytes / 2 )
				win_printf( win, "%*s-%*s", spc_hyphen, "", spc_hyphen, "" );
			else
			if( spc_data && (column * 2 % len_data) == 0 )
				win_putc( win, ' ' );
		}
		else
		{
			win_putc( win, '\n');
			column = 0;
		}
	}
}

/**************************************************************************
 * dump_mem
 * Update a memory window
 * Dispatch to one of the memory handler specific output functions
 **************************************************************************/
static void dump_mem( int which, int set_title )
{
	unsigned len_addr = (ABITS + ASHIFT + 3) / 4;

    if( set_title )
		win_set_title( WIN_MEM(activecpu,which), name_memory(DBGMEM[which].base) );

	switch( DBGMEM[which].mode )
	{
		case MODE_HEX_UINT8:  dump_mem_hex( which, len_addr, 2 ); break;
		case MODE_HEX_UINT16: dump_mem_hex( which, len_addr, 4 ); break;
		case MODE_HEX_UINT32: dump_mem_hex( which, len_addr, 8 ); break;
	}
}

/**************************************************************************
 * edit_regs
 * Edit the registers
 **************************************************************************/
static void edit_regs( void )
{
	UINT32 win = WIN_REGS(activecpu);
	s_regs *regs = &DBGREGS;
	s_edit *pedit = regs->edit;
	unsigned shift, mask, val;
    const char *k;
	int i, x, y;

	/* Eventually update the cmdline window caption */
    edit_cmds_info();

    win_set_curpos( win, pedit[regs->idx].x + pedit[regs->idx].n + regs->nibble, pedit[regs->idx].y );
	osd_set_screen_curpos( win_get_cx_abs(win), win_get_cy_abs(win) );

    i = keyboard_read_sync();
	k = keyboard_name(i);

	shift = (pedit->w - 1 - regs->nibble) * 4;
	mask = ~(0x0000000f << shift);

	if( strlen(k) == 1 )
	{
		switch( k[0] )
		{
			case '0': case '1': case '2': case '3':
			case '4': case '5': case '6': case '7':
			case '8': case '9': case 'A': case 'B':
			case 'C': case 'D': case 'E': case 'F':
				val = k[0] - '0';
				if( val > 9 ) val -= 7;
				val <<= shift;
				/* now modify the register */
				cpu_set_reg( regs->id[regs->idx],
					( cpu_get_reg( regs->id[regs->idx] ) & mask ) | val );
				dump_regs();
				i = KEYCODE_RIGHT;	/* advance to next nibble */
		}
	}

    switch( i )
    {
		case KEYCODE_LEFT:
			if( --regs->nibble < 0 )
			{
				if( --regs->idx < 0 )
				{
					regs->idx = regs->count - 1;
				}
				regs->nibble = pedit[regs->idx].w - 1;
			}
			break;

		case KEYCODE_RIGHT:
			if( ++regs->nibble >= pedit[regs->idx].w )
			{
				regs->nibble = 0;
				if( ++regs->idx >= regs->count )
				{
					regs->idx = 0;
                }
            }
            break;

		case KEYCODE_UP:
			i = regs->idx;
            x = pedit[regs->idx].x;
			y = pedit[regs->idx].y;
			while( x != pedit[i].x || pedit[i].y == y )
			{
				if( --i < 0 )
                {
                    i = regs->count - 1;
                    if( pedit[i].y == y )
                    {
                        i = regs->idx;
                        break;
                    }
                }
			}
			if( i != regs->idx )
			{
				if( regs->nibble >= pedit[i].w )
					regs->nibble = pedit[i].w - 1;
                regs->idx = i;
			}
            break;

		case KEYCODE_DOWN:
			i = regs->idx;
			x = pedit[regs->idx].x;
			y = pedit[regs->idx].y;
			while( x != pedit[i].x || pedit[i].y == y )
			{
				if( ++i >= regs->count )
				{
					i = 0;
					if( pedit[i].y == y )
					{
						i = regs->idx;
						break;
					}
                }
			}
			if( i != regs->idx )
            {
				if( regs->nibble >= pedit[i].w )
                    regs->nibble = pedit[i].w - 1;
                regs->idx = i;
            }
            break;

		case KEYCODE_ENTER:
			DBG.window = EDIT_CMDS;
			break;

        default:
			cmd_default( i );
    }
}


/**************************************************************************
 * edit_dasm
 * Edit the disassembly output
 **************************************************************************/
static void edit_dasm(void)
{
	UINT32 win = WIN_DASM(activecpu);
    const char *k;
	int i, update_window = 0;

	/* Eventually update the cmdline window caption */
    edit_cmds_info();

	osd_set_screen_curpos( win_get_cx_abs(win), win_get_cy_abs(win) );

    i = keyboard_read_sync();
	k = keyboard_name(i);

    switch( i )
    {
		case KEYCODE_M: /* Toggle mode (opcode display) */
			sprintf( CMD, "%d", dbg_dasm_opcodes ^ 1 );
			cmd_set_dasm_opcodes();
			break;

		case KEYCODE_D: /* Default case disassembly */
			dbg_dasm_case = 0;
            update_window = 1;
            break;

		case KEYCODE_L: /* Lower case disassembly */
			dbg_dasm_case = 1;
			update_window = 1;
            break;

		case KEYCODE_U: /* Upper case disassembly */
			dbg_dasm_case = 2;
            update_window = 1;
            break;

		case KEYCODE_R: /* Toggle relative jumps display */
			dbg_dasm_relative_jumps ^= 1;
            update_window = 1;
            break;

        case KEYCODE_ENTER:
			DBG.window = EDIT_CMDS;
			break;

        default:
			cmd_default( i );
    }
	if( update_window )
	{
		DBGDASM.pc_end = dump_dasm( DBGDASM.pc_top );
	}
}


/**************************************************************************
 * edit_mem
 * Edit the memory dumps output
 **************************************************************************/
static void edit_mem( int which )
{
	UINT32 win = WIN_MEM(activecpu,which);
	s_edit *pedit = DBGMEM[which].edit;
    const char *k;
	unsigned shift, mask, val;
	int i, update_window = 0;

	/* Eventually update the cmdline window caption */
    edit_cmds_info();

	win_set_curpos( win, pedit[DBGMEM[which].offset].x + DBGMEM[which].nibble, pedit[DBGMEM[which].offset].y );
	osd_set_screen_curpos( win_get_cx_abs(win), win_get_cy_abs(win) );

	switch( DBGMEM[which].mode )
	{
		case MODE_HEX_UINT8:
			DBGMEM[which].address = (DBGMEM[which].base + DBGMEM[which].offset) & AMASK;
			break;
		case MODE_HEX_UINT16:
			DBGMEM[which].address = (DBGMEM[which].base + (DBGMEM[which].offset & ~1) + pedit[DBGMEM[which].offset].n ) & AMASK;
            break;
		case MODE_HEX_UINT32:
			DBGMEM[which].address = (DBGMEM[which].base + (DBGMEM[which].offset & ~3) + pedit[DBGMEM[which].offset].n ) & AMASK;
            break;
	}
	win_set_title( win, name_memory( DBGMEM[which].address ) );

    i = keyboard_read_sync();
	k = keyboard_name(i);

	shift = (pedit[DBGMEM[which].offset].w - 1 - DBGMEM[which].nibble) * 4;
	mask = ~(0x0f << shift);

	if( strlen(k) == 1 )
	{
		switch( k[0] )
		{
			case '0': case '1': case '2': case '3':
			case '4': case '5': case '6': case '7':
			case '8': case '9': case 'A': case 'B':
			case 'C': case 'D': case 'E': case 'F':
				val = k[0] - '0';
				if( val > 9 ) val -= 7;
				val <<= shift;
				/* now modify the register */
				WRMEM( DBGMEM[which].address, ( RDMEM( DBGMEM[which].address ) & mask ) | val );
				update_window = 1;
				i = KEYCODE_RIGHT;	/* advance to next nibble */
		}
	}

    switch( i )
    {
        case KEYCODE_LEFT:
			if( --DBGMEM[which].nibble < 0 )
			{
				if( --DBGMEM[which].offset < 0 )
				{
					DBGMEM[which].base = (DBGMEM[which].base - DBGMEM[which].bytes) & AMASK;
					DBGMEM[which].offset += DBGMEM[which].bytes;
					update_window = 1;
				}
				DBGMEM[which].nibble = pedit[DBGMEM[which].offset].w - 1;
			}
			break;

		case KEYCODE_RIGHT:
			if( ++DBGMEM[which].nibble >= pedit[DBGMEM[which].offset].w )
			{
				DBGMEM[which].nibble = 0;
				if( ++DBGMEM[which].offset >= DBGMEM[which].size )
				{
					DBGMEM[which].base = (DBGMEM[which].base + DBGMEM[which].bytes) & AMASK;
					DBGMEM[which].offset -= DBGMEM[which].bytes;
					update_window = 1;
                }
            }
            break;

		case KEYCODE_UP:
			DBGMEM[which].offset -= DBGMEM[which].bytes;
			if( DBGMEM[which].offset < 0 )
			{
				DBGMEM[which].base = (DBGMEM[which].base - DBGMEM[which].bytes) & AMASK;
				DBGMEM[which].offset += DBGMEM[which].bytes;
				update_window = 1;
            }
			break;

		case KEYCODE_DOWN:
			DBGMEM[which].offset += DBGMEM[which].bytes;
			if( DBGMEM[which].offset >= DBGMEM[which].size )
            {
				DBGMEM[which].base = (DBGMEM[which].base + DBGMEM[which].bytes) & AMASK;
				DBGMEM[which].offset -= DBGMEM[which].bytes;
				update_window = 1;
            }
            break;

		case KEYCODE_PGUP:
			DBGMEM[which].base = (DBGMEM[which].base - DBGMEM[which].size) & AMASK;
			update_window = 1;
            break;

		case KEYCODE_PGDN:
			DBGMEM[which].base = (DBGMEM[which].base + DBGMEM[which].size) & AMASK;
            update_window = 1;
            break;

		case KEYCODE_HOME:
			DBGMEM[which].offset = 0;
			DBGMEM[which].base = 0x00000000;
            update_window = 1;
            break;

		case KEYCODE_END:
			DBGMEM[which].offset = DBGMEM[which].size - 1;
			DBGMEM[which].base = (0xffffffff - DBGMEM[which].offset) & AMASK;
            update_window = 1;
            break;

		case KEYCODE_H:
			DBGMEM[which].ascii = (DBGMEM[which].ascii + 1) % 3;
			update_window = 1;
			break;


        case KEYCODE_M:
			DBGMEM[which].mode = ++(DBGMEM[which].mode) % 3;
			/* Reset cursor coordinates and sizes of the edit info */
			memset( DBGMEM[which].edit, 0, sizeof(DBGMEM[which].edit) );
			update_window = 1;
			break;

		case KEYCODE_ENTER:
			DBG.window = EDIT_CMDS;
			break;

        default:
			cmd_default( i );
			update_window = 1;
    }

	if( update_window )
	{
		memcpy( DBGMEM[which].backup, DBGMEM[which].newval, DBGMEM[which].size );
		dump_mem( which, 0 );
	}
}

/**************************************************************************
 * edit_cmds_info
 * Search the cmdline for the beginning of a known command and
 * display some information in the caption of the command line input
 **************************************************************************/
static int edit_cmds_info( void )
{
	char *cmd = CMD;
	char hist_info[31+1];
	int i, l;

	hist_info[0] = '\0';
    if( DBG.hist_cnt )
		sprintf( hist_info, "\tHistory: %d", DBG.hist_cnt);

    if( strlen(cmd) )
    {
        for( i = 0; commands[i].name; i++ )
        {
			l = strlen(cmd);
			if( strlen(commands[i].name) < l )
				l = strlen(commands[i].name);
			if( strncmp( cmd, commands[i].name, l ) == 0 && !isalnum(cmd[l]) )
            {
				win_set_title( WIN_CMDS(activecpu), "Command: %s %s%s",
					commands[i].name, commands[i].args, hist_info );
				return i;
            }
			if( commands[i].alias )
			{
				l = strlen(cmd);
				if( strlen(commands[i].alias) < l )
					l = strlen(commands[i].alias);
				if( strncmp( cmd, commands[i].alias, l ) == 0 && !isalnum(cmd[l]) )
				{
					win_set_title( WIN_CMDS(activecpu), "Command: %s %s (aka %s)%s",
						commands[i].alias, commands[i].args, commands[i].name, hist_info );
					return i;
				}
            }
        }
    }
	if( dbg_info_once )
	{
		win_set_title( WIN_CMDS(activecpu), "%s%s", dbg_info_once, hist_info );
		dbg_info_once = NULL;
    }
	else
	{
		win_set_title( WIN_CMDS(activecpu), "Command (press F1 for help)%s", hist_info );
	}
	return INVALID;
}

/**************************************************************************
 * edit_cmds_parse
 * Search the cmdline for a known command and if found,
 * strip it from cmdline and return it's index
 **************************************************************************/
static int edit_cmds_parse( char *cmdline )
{
    int i, l;

    for( i = 0; commands[i].valid; i++ )
    {
        if( !commands[i].name )
            continue;
        l = strlen( commands[i].name );
		if( !strncmp( cmdline, commands[i].name, l ) && !isalnum( cmdline[l] ) )
        {
			while( cmdline[l] && isspace( cmdline[l] ) ) l++;
			strcpy( cmdline, cmdline + l );
            return i;
        }
		if( commands[i].alias )
		{
			l = strlen( commands[i].alias );
			if( !strncmp( cmdline, commands[i].alias, l ) && !isalnum( cmdline[l] ) )
			{
				while( cmdline[l] && isspace( cmdline[l] ) ) l++;
				strcpy( cmdline, cmdline + l );
				return i;
			}
        }
    }
	return INVALID;
}


/**************************************************************************
 * edit_cmds_append
 * Append a character (string) to the command line
 **************************************************************************/
static void edit_cmds_append( const char *src )
{
	char *cmdline = DBG.cmdline;
	UINT32 win = WIN_CMDS(activecpu);
    if( strlen(cmdline) < 80 )
	{
		strcat(cmdline, src);
		win_printf( win, "%s", src );
	}
}

void edit_cmds_reset( void )
{
	unsigned win = WIN_CMDS(activecpu);
	DBG.cmdline[0] = '\0';
	win_set_color( win, cur_col[E_CMDS] );
	win_set_curpos( win, 0, 0 );
	osd_set_screen_curpos( win_get_cx_abs(win), win_get_cy_abs(win) );
    win_erase_eol( win, ' ' );
}

/**************************************************************************
 * edit_cmds
 * Edit the command line input
 **************************************************************************/
static void edit_cmds(void)
{
	char *cmdline = DBG.cmdline;
	UINT32 win = WIN_CMDS(activecpu);
	const char *k;
	int i, l, cmd;

	osd_set_screen_curpos( win_get_cx_abs(win), win_get_cy_abs(win) );

	cmd = edit_cmds_info();

	i = keyboard_read_sync();
	k = keyboard_name(i);
	l = strlen(k);

	if( l == 1 )
		edit_cmds_append(k);

    switch( i )
    {
		case KEYCODE_SPACE:
			/*
			 * Command completion for convenience:
			 * found a valid command and no space in the command line yet?
			 */
			if( cmd != INVALID && strchr(CMD, ' ') == NULL )
			{
				strcpy( CMD, commands[cmd].name );
				win_set_curpos( win, 0, 0 );
				win_printf( win, "%s", CMD );
			}
			edit_cmds_append(" ");
			break;

        case KEYCODE_BACKSPACE:
            if( strlen(cmdline) > 0 )
            {
                cmdline[strlen(cmdline)-1] = '\0';
				win_printf( win, "\b \b" );
            }
            break;

		case KEYCODE_ENTER:
			if( strlen(cmdline) )
            {
				cmd = edit_cmds_parse( cmdline );
				if( cmd != INVALID && commands[cmd].function )
					(*commands[cmd].function)();
                break;
            }
			else
			{
				/* ENTER in an empty line: do single step... */
                i = KEYCODE_F8;
			}
			/* fall through */
        default:
			cmd_default( i );
    }
}

/**************************************************************************
 **************************************************************************
 *
 *		Command functions
 *
 **************************************************************************
 **************************************************************************/

/**************************************************************************
 * cmd_help
 * Display a help window containing a list of the (currently)
 * available commands and keystrokes.
 **************************************************************************/
static void cmd_help( void )
{
    UINT32 win = WIN_HELP;
	const char *title = "";
	char *help = malloc(4096+1), *dst;
	const char *src;
	unsigned w, h;
	int cmd = INVALID;
	int i, k, l, top, lines;

    if( !help )
    {
		win_msgbox( cur_col[E_ERROR],
			"Memory problem!", "Couldn't allocate help text buffer" );
        return;
    }
    dst = help;

	/* Decide what to print in the first lines of the help window */
    switch( DBG.window )
    {
	case EDIT_CMDS:
		title = "MAME Debugger command help";
		cmd = edit_cmds_info();
		/* Did not find the start of a command? */
		if( cmd == INVALID )
		{
			dst += sprintf( dst, "Welcome to the new MAME debugger V0.53!") + 1;
			dst += sprintf( dst, " " ) + 1;
			dst += sprintf( dst, "Many commands accept either a value or a register name.") + 1;
			dst += sprintf( dst, "You can indeed type either \"R HL = SP\" or \"R HL = 1FD0\".") + 1;
			dst += sprintf( dst, "In the syntax, where you see <address> you may generally") + 1;
			dst += sprintf( dst, "use a number or a register name of the active CPU.") + 1;
			dst += sprintf( dst, "A <boolean> can be specified as ON/OFF, YES/NO, Y/N or 1/0.") + 1;
			dst += sprintf( dst, "Note: You may put your preferences into a file \"mamedbg.cfg\" in your") + 1;
			dst += sprintf( dst, "main directory. Furthermore you can put game specific settings into a") + 1;
			dst += sprintf( dst, "set of files <driver>.cf<cpu>, eg. \"dkong.cf0\" and \"dkong.cf1\".") + 1;
			dst += sprintf( dst, "The files can contain different settings for the CPUs of a game.") + 1;
        }
		else
		{
			if( commands[cmd].alias )
				dst += sprintf( dst, "%s %s (aka %s)", commands[cmd].name, commands[cmd].args, commands[cmd].alias ) + 1;
			else
				dst += sprintf( dst, "%s %s", commands[cmd].name, commands[cmd].args ) + 1;
			src = commands[cmd].info;
			while( *src )
			{
				*dst++ = (*src == '\n') ? '\0' : *src;
				src++;
			}
			*dst++ = '\0';
        }
		break;
	case EDIT_REGS:
		title = "MAME debugger CPU registers help";
		dst += sprintf( dst, "%s [%s] Version %s", cpu_name(), cpu_core_family(), cpu_core_version() ) + 1;
		dst += sprintf( dst, "Address bits   : %d [%08X]", cpu_address_bits(), cpu_address_mask() ) + 1;
		dst += sprintf( dst, "Code align unit: %d byte(s)", cpu_align_unit() ) + 1;
		dst += sprintf( dst, "This CPU is    : %s endian", (ENDIAN == CPU_IS_LE) ? "little" : "big") + 1;
		dst += sprintf( dst, "Source file    : %s", cpu_core_file() ) + 1;
		dst += sprintf( dst, "%s", cpu_core_credits() ) + 1;
        break;
	case EDIT_DASM:
		title = "MAME debugger disassembly help";
        dst += sprintf( dst, "%s [%s]", cpu_name(), cpu_core_family() ) + 1;
        break;
	case EDIT_MEM1:
	case EDIT_MEM2:
		title = "MAME debugger memory editor help";
		dst += sprintf( dst, "You can move around using the cursor key block.") + 1;
		dst += sprintf( dst, "Change memory by keying in hex digits (0-9, A-F).") + 1;
        break;
    }
	dst += sprintf( dst, " " ) + 1;
	dst += sprintf( dst, "Valid commands and keys are:" ) + 1;

    for( i = 0; commands[i].valid; i++ )
    {
        if( commands[i].valid & ( 1 << DBG.window ) )
        {
			/* Already displayed this help? */
            if( i == cmd )
				continue;
			dst += sprintf( dst, " " ) + 1;
            if( commands[i].name )
			{
                if( commands[i].alias )
					dst += sprintf( dst, "%s %s (aka %s)", commands[i].name, commands[i].args, commands[i].alias ) + 1;
				else
                    dst += sprintf( dst, "%s %s", commands[i].name, commands[i].args ) + 1;
				src = commands[i].info;
				while( *src )
				{
					if( *src == '\n' )
					{
						if( src[1] != '\0' )
							*dst++ = '\0';
					}
					else
						*dst++ = *src;
					src++;
				}
				*dst++ = '\0';
            }
            else
			{
				dst += sprintf( dst, "[%s]\t%s", keyboard_name(commands[i].key), commands[i].info ) + 1;
			}
        }
    }

    /* Terminate *help with a second NULL byte */
    *dst++ = '\0';

    /* Count lines */
    for( lines = 0, src = help; *src && i > 0; src += strlen(src) + 1 )
        lines++;

	osd_get_screen_size( &w, &h );
    win_create( win, 0,
		2,1,w-2-4,h-2-3, cur_col[E_HELP], cur_col[E_FRAME], ' ',
        BORDER_TOP | BORDER_LEFT | BORDER_RIGHT | BORDER_BOTTOM | SHADOW );
    win_set_title( win, title );
    win_show( win );
    h = win_get_h( win );

    top = 0;
    do
    {
        for( src = help, i = top; *src && i > 0; src += strlen(src) + 1 )
            i--;
        win_set_curpos( win, 0, 0 );
        l = 0;
        do
        {
            if( *src )
            {
                win_printf( win, src );
                src += strlen(src) + 1;
            }
			win_erase_eol( win, ' ' );
            win_putc( win, '\n');
            l++;
        } while( l < win_get_h(win) );

        k = keyboard_read_sync();
        switch( k )
        {
            case KEYCODE_UP:
                if( top > 0 )
                    top--;
				k = KEYCODE_NONE;
                break;
            case KEYCODE_ENTER:
            case KEYCODE_DOWN:
                if( top < lines - h )
                    top++;
				k = KEYCODE_NONE;
                break;
            case KEYCODE_PGUP:
                if( top - h > 0 )
                    top -= h;
                else
                    top = 0;
				k = KEYCODE_NONE;
                break;
            case KEYCODE_PGDN:
                if( top + h < lines - h )
                    top += h;
                else
                    top = lines - h;
				k = KEYCODE_NONE;
                break;
        }
	} while( k == KEYCODE_NONE );

    free( help );

    win_close( win );
}

/**************************************************************************
 * cmd_default
 * Handle a key stroke with no special meaning inside the active window
 **************************************************************************/
static void cmd_default( int code )
{
    int i;
    for( i = 0; commands[i].valid; i++ )
    {
        if( (commands[i].valid & (1<<DBG.window)) && commands[i].key == code )
        {
            if( commands[i].function )
                (*commands[i].function)();
            return;
        }
    }
}


/**************************************************************************
 * cmd_set_element_color
 * Set a specific color foreground and background
 **************************************************************************/
static void cmd_set_element_color( void )
{
	char *cmd = CMD;
	unsigned element, fg, bg, win;
	int length;

	element = get_option_or_value( &cmd, &length, ELEMENT_NAMES );
    if( length )
	{
		fg = get_option_or_value( &cmd, &length, COLOR_NAMES );
		if( length )
		{
			bg = get_option_or_value( &cmd, &length, COLOR_NAMES );
			if( !length ) bg = 0;	/* BLACK is default background */
        }
		else
		{
			bg = def_col[element] >> 4;
			fg = def_col[element] & 15;
		}
		cur_col[element] = fg + 16 * bg;
		switch( element )
		{
			case E_TITLE:
				for( win = 0; win < MAX_WINDOWS; win++ )
					win_set_title_color( win, cur_col[element] );
				break;
			case E_FRAME:
				for( win = 0; win < MAX_WINDOWS; win++ )
					win_set_frame_color( win, cur_col[element] );
				break;
        }
    }
	else
	{
		memcpy( cur_col, def_col, sizeof(cur_col) );
		for( win = 0; win < MAX_WINDOWS; win++ )
			win_set_title_color( win, cur_col[E_TITLE] );
		for( win = 0; win < MAX_WINDOWS; win++ )
			win_set_frame_color( win, cur_col[E_FRAME] );
    }

    edit_cmds_reset();
	dbg_update = 1;
}

/**************************************************************************
 * cmd_set_screen_size
 * Set new screen size width and height
 **************************************************************************/
static void cmd_set_screen_size( void )
{
	char *cmd = CMD;
	unsigned w, h, new_w, new_h;
	int length;

	osd_get_screen_size( &w, &h );

	new_w = dtou( &cmd, &length );
	if( length )
	{
		new_h = dtou( &cmd, &length );
		if( !length ) new_h = h;
	}
	else
	{
		new_w = 80;
		new_h = 25;
	}
	if( new_w < 80 ) new_w = 80;
	if( new_h < 25 ) new_h = 25;

	dbg_close_windows();
	osd_set_screen_size( new_w, new_h );
	dbg_open_windows();

    edit_cmds_reset();
	dbg_update = 1;
}

/**************************************************************************
 * cmd_brk_regs_set
 * Set the register breakpoint for the current CPU
 **************************************************************************/
static void cmd_brk_regs_set( void )
{
    char *cmd = CMD;
	unsigned data;
	int length;

	DBG.brk_regs = get_register_id( &cmd, &length );
	if( DBG.brk_regs != INVALID )
	{
		DBG.brk_regs_oldval = cpu_get_reg(DBG.brk_regs);
		data = get_register_or_value( &cmd, &length );
		if( length )
		{
			DBG.brk_regs_newval = data;
			data = get_register_or_value( &cmd, &length );
			if( length )
			{
				DBG.brk_regs_mask = data;
				/* Remove masked bits from the new value too ;-) */
				DBG.brk_regs_newval &= data;
			}
			else
			{
				DBG.brk_regs_mask = 0xffffffff;
			}
        }
		else
		{
			DBG.brk_regs_newval = INVALID;
            DBG.brk_regs_mask = 0xffffffff;
		}
	}
    dbg_update = 1;
    edit_cmds_reset();
}

/**************************************************************************
 * cmd_brk_regs_clear
 * Reset the watchpoint for the current CPU to INVALID
 **************************************************************************/
static void cmd_brk_regs_clear( void )
{
	if( DBG.brk_regs != INVALID )
	{
		DBG.brk_regs = INVALID;
		DBG.brk_regs_oldval = INVALID;
		DBG.brk_regs_newval = INVALID;
		DBG.brk_regs_mask = 0xffffffff;
		dbg_update = 1;
	}
    edit_cmds_reset();
}

/**************************************************************************
 * cmd_brk_data_set
 * Set the watchpoint for the current CPU to the specified address
 * The monitored data is one byte at the given address
 **************************************************************************/
static void cmd_brk_data_set( void )
{
    char *cmd = CMD;
    unsigned data;
	int length;

	DBG.brk_data = get_register_or_value( &cmd, &length );

	DBG.brk_data = rshift(DBG.brk_data) & AMASK; /* EHC 11/14/99: Need to shift + mask otherwise we die */

	if( length )
	{
		data = RDMEM(DBG.brk_data);

		DBG.brk_data_oldval = data;
		data = get_register_or_value( &cmd, &length );

		if( length )
		{
			DBG.brk_data_newval = data;
		}
		else
		{
			DBG.brk_data_newval = INVALID;
        }
	}

    dbg_update = 1;
    edit_cmds_reset();
}

/**************************************************************************
 * cmd_brk_data_clear
 * Reset the watchpoint for the current CPU to INVALID
 **************************************************************************/
static void cmd_brk_data_clear( void )
{
	if( DBG.brk_data != INVALID )
	{
		DBG.brk_data = INVALID;
		DBG.brk_data_oldval = INVALID;
		DBG.brk_data_newval = INVALID;
		dbg_update = 1;
	}
    edit_cmds_reset();
}

/**************************************************************************
 * cmd_brk_exec_set
 * Set the execution breakpoint for the current CPU to the specified address
 **************************************************************************/
static void cmd_brk_exec_set( void )
{
	char *cmd = CMD;
	unsigned times;
	int length;

	DBG.brk_exec = get_register_or_value( &cmd, &length );
	if( length )
	{
		times = get_register_or_value( &cmd, &length );
		if( length )
		{
			DBG.brk_exec_times = times;
			DBG.brk_exec_reset = times;
		}
		else
		{
			DBG.brk_exec_times = 0;
			DBG.brk_exec_reset = 0;
        }
    }
	else
	{
		DBG.brk_exec = DBGDASM.pc_cur;
		DBG.brk_exec_times = 0;
		DBG.brk_exec_reset = 0;
	}
	dbg_update = 1;
    edit_cmds_reset();
}

/**************************************************************************
 * cmd_brk_exec_clear
 * Reset the execution breakpoint for the current CPU to INVALID
 **************************************************************************/
static void cmd_brk_exec_clear( void )
{
	if( DBG.brk_exec != INVALID )
	{
		DBG.brk_exec = INVALID;
		DBG.brk_exec_times = 0;
		DBG.brk_exec_reset = 0;
		dbg_update = 1;
	}
    edit_cmds_reset();
}

/**************************************************************************
 * cmd_display_memory
 * Set one of the memory display window's start address
 **************************************************************************/
static void cmd_display_memory( void )
{
	char *cmd = CMD;
    unsigned which, address;
    int length;

	which = xtou( &cmd, &length );
	if( length )
	{
		address = get_register_or_value( &cmd, &length );
        if( length )
		{
			which = (which - 1) % MAX_MEM;
		}
		else
		{
			address = which;
			which = 0;
		}
		address = rshift(address) & AMASK;
        DBGMEM[which].base = address;
		dump_mem( which, 1 );
    }
    edit_cmds_reset();
}

/**************************************************************************
 * cmd_dasm_to_file
 * Disassemble a range of code and output it to a file
 **************************************************************************/
static void cmd_dasm_to_file( void )
{
	char buffer[127+1], *cmd = CMD;
	const char *filename;
	int length;
	FILE *file;
	unsigned i, pc, size, start, end, width, opcodes;

	filename = get_file_name( &cmd, &length );
	if( !length )
	{
		win_msgbox( cur_col[E_ERROR], "DASM arguments",
			"Filename missing");
		edit_cmds_reset();
        return;
	}
	start = get_register_or_value( &cmd, &length );
	if( !length )
	{
		win_msgbox( cur_col[E_ERROR], "DASM arguments",
			"Start address missing");
		edit_cmds_reset();
        return;
	}
	end = get_register_or_value( &cmd, &length );
	if( !length )
	{
		win_msgbox( cur_col[E_ERROR], "DASM arguments",
			"End address missing");
		edit_cmds_reset();
        return;
    }
	opcodes = get_boolean( &cmd, &length );
	if( !length ) opcodes = 1;	/* default to display opcodes */

    file = fopen(filename, "w");
	if( !file )
	{
		win_msgbox( cur_col[E_ERROR], "DASM to file",
			"Could not create %s", filename);
		edit_cmds_reset();
        return;
    }

	width = (ABITS + ASHIFT + 3) / 4;

    for( pc = start; pc <= end; /* */ )
	{
		unsigned p = rshift(pc);
		unsigned s;
		size = cpu_dasm( buffer, pc );
		s = rshift(size);

		fprintf(file, "%0*X: ", width, pc );
		switch( ALIGN )
		{
		case 1: /* dump bytes */
			for( i = 0; i < INSTL; i++ )
			{
				if ( i < s )
					fprintf( file, "%02X ",
						RDMEM(order(p+i,1)) );
				else
					fprintf( file, "   ");
			}
			break;
		case 2: /* dump words */
			for( i = 0; i < INSTL; i += 2 )
			{
				if ( i < s )
					fprintf( file, "%02X%02X ",
						RDMEM(order(p+i+0,2)), RDMEM(order(p+i+1,2)) );
				else
					fprintf( file, "     ");
			}
			break;
		case 4: /* dump dwords */
			for( i = 0; i < INSTL; i += 4 )
			{
				if ( i < s )
					fprintf( file, "%02X%02X%02X%02X ",
						RDMEM(order(p+i+0,4)), RDMEM(order(p+i+1,4)),
						RDMEM(order(p+i+2,4)), RDMEM(order(p+i+3,4)) );
				else
					fprintf( file, "     ");
			}
			break;
		}
		fprintf( file, "%s\n", buffer );
        if( (pc + size) < pc )
			break;
		pc += size;
	}
	fclose( file );

	edit_cmds_reset();
}

/**************************************************************************
 * cmd_dump_to_file
 * (Hex-)Dump a range of code and output it to a file
 **************************************************************************/
static void cmd_dump_to_file( void )
{
	UINT8 buffer[16];
	char *cmd = CMD;
	const char *filename;
	int length;
	FILE *file;
	unsigned x, offs, address = 0, start, end, width, data;
	unsigned datasize, asciimode;

	filename = get_file_name( &cmd, &length );
	if( !length )
	{
		win_msgbox( cur_col[E_ERROR], "DUMP arguments",
			"<filename> missing");
		edit_cmds_reset();
        return;
	}
	start = get_register_or_value( &cmd, &length );
	if( !length )
	{
		win_msgbox( cur_col[E_ERROR], "DUMP arguments",
			"<start> address missing");
		edit_cmds_reset();
        return;
	}
	start = rshift(start);
	end = get_register_or_value( &cmd, &length );
	if( !length )
	{
		win_msgbox( cur_col[E_ERROR], "DUMP arguments",
			"<end> address missing");
		edit_cmds_reset();
        return;
    }
	end = rshift(end);
	asciimode = 1;		/* default to translation table */
	datasize = ALIGN*2;	/* default to align unit of that CPU */
	data = get_option_or_value( &cmd, &length, "BYTE\0WORD\0DWORD\0");
	if( length )
	{
		if( data != 1 && data != 2 && data != 4 )
		{
			win_msgbox( cur_col[E_ERROR], "DUMP arguments",
				"Wrong <data size>. Only BYTE, WORD or DWORD\n(also 0, 1 or 2) are supported");
			data = 1;
		}
		datasize = data << 1;
		/* look if there's also an ASCII mode specified */
		data = get_option_or_value( &cmd, &length, "OFF\0TRANSLATE\0FULL\0" );
		if( length )
		{
			if( data > 2 )
			{
				win_msgbox( cur_col[E_ERROR], "DUMP arguments",
					"Wrong ASCII mode. Only OFF, TRANSLATE or FULL\n(also 0,1 or 2) are supported");
				data = 1;
			}
			asciimode = data;
		}
    }

    file = fopen(filename, "w");
	if( !file )
	{
		win_msgbox( cur_col[E_ERROR], "DUMP to file",
			"Could not create %s", filename);
		edit_cmds_reset();
        return;
    }

	width = (ABITS + ASHIFT + 3) / 4;

	for( x = 0, offs = 0; offs + start <= end; offs++ )
	{
		switch( datasize )
        {
            case 2:
				address = (start + order(offs,1)) & AMASK;
                break;
            case 4:
				address = (start + order(offs,2)) & AMASK;
                break;
            case 8:
				address = (start + order(offs,4)) & AMASK;
                break;
        }
		buffer[offs & 15] = RDMEM( address );
        if( (offs & 15) == 0 )
			fprintf(file, "%0*X: ", width, lshift((start + offs) & AMASK) );
		fprintf(file, "%02X", buffer[offs & 15] );
		if( (offs & 15) == 15 )
		{
			unsigned o;
			if( asciimode )
			{
				fputc( ' ', file );
				if( asciimode == 1 )
					for( o = 0; o < 16; o++ )
						fputc( trans_table[buffer[o]], file );
				else
					for( o = offs - 15; o <= offs; o++ )
						fputc( (buffer[o] < 32) ? '.' : buffer[o], file );
            }
			fprintf(file, "\n" );
			x = 0;
		}
		else
		if( (x += 2) == datasize )
		{
			if( (offs & 15) == 7 )
				fprintf(file, "-");
			else
				fprintf(file, " ");
			x = 0;
		}
	}
	fclose( file );

    edit_cmds_reset();
}

/**************************************************************************
 * cmd_save_to_file
 * Save binary image of a range of OP_ROM or OP_RAM
 **************************************************************************/
static void cmd_save_to_file( void )
{
	char *cmd = CMD;
	const char *filename;
	int length;
	FILE *file;
	unsigned start, end;
	unsigned save_what;

	filename = get_file_name( &cmd, &length );
	if( !length )
	{
		win_msgbox( cur_col[E_ERROR], "SAVE arguments",
			"<filename> missing");
		edit_cmds_reset();
        return;
	}
	start = get_register_or_value( &cmd, &length );
	if( !length )
	{
		win_msgbox( cur_col[E_ERROR], "SAVE arguments",
			"<start> address missing");
		edit_cmds_reset();
        return;
	}
	end = get_register_or_value( &cmd, &length );
	if( !length )
	{
		win_msgbox( cur_col[E_ERROR], "SAVE arguments",
			"<end> address missing");
		edit_cmds_reset();
        return;
    }

	save_what = get_option_or_value( &cmd, &length, "OPCODES\0DATA\0");
	if( !length ) save_what = 0;	/* default to OP_ROM */

    file = fopen(filename, "wb");
	if( !file )
	{
		win_msgbox( cur_col[E_ERROR], "SAVE to file",
			"Could not create %s", filename);
		edit_cmds_reset();
        return;
    }

	if( save_what )
		fwrite( &OP_RAM[start], 1, end+1-start, file );
	else
		fwrite( &OP_ROM[start], 1, end+1-start, file );

	fclose( file );

    edit_cmds_reset();
}

/**************************************************************************
 * cmd_edit_memory
 * Set one of the memory display window's start address and
 * switch to that window
 **************************************************************************/
static void cmd_edit_memory( void )
{
    char *cmd = CMD;
    unsigned which, address;
    int length;

    which = xtou( &cmd, &length );
    if( length )
    {
		which = (which - 1) % MAX_MEM;
		address = get_register_or_value( &cmd, &length );
		address = rshift(address) & AMASK;
        if( length )
		{
			DBGMEM[which].offset = address % DBGMEM[which].size;
			DBGMEM[which].base = address - DBGMEM[which].offset;
			DBGMEM[which].nibble = 0;
			dump_mem( which, 0 );
        }
        switch( which )
        {
            case 0: DBG.window = EDIT_MEM1; break;
            case 1: DBG.window = EDIT_MEM2; break;
        }
    }
    edit_cmds_reset();
}

/**************************************************************************
 * cmd_search_memory
 * Search the memory for a sequence of bytes
 **************************************************************************/
static void cmd_search_memory(void)
{
	static UINT8 search_data[16];
	static int search_count = 0;
	UINT32 win = MAX_WINDOWS-3;
	unsigned which = (DBG.window == WIN_MEM1(activecpu)) ? 0 : 1;
	unsigned w, h;
	unsigned shift, mask, val;
    const char *k;
	int i, offset, nibble;

	osd_get_screen_size( &w, &h );
	win_create( win, 1, 2, 2, w-4, 2,
		cur_col[E_PROMPT], cur_col[E_FRAME], ' ',
		BORDER_TOP | BORDER_BOTTOM | BORDER_LEFT | BORDER_RIGHT );
	win_set_title( win, "Search byte(s) in memory");
	win_show( win );

	offset = 0;
	nibble = 0;

    do
	{
		win_set_curpos( win, 0, 0 );
		for( i = 0; i < search_count; i++ )
			win_printf( win, "%02X ", search_data[i] );
		win_erase_eol( win, ' ' );
		win_set_curpos( win, 0, 1 );
		for( i = 0; i < search_count; i++ )
			win_printf( win, "%c  ", search_data[i] < 32 ? '.' : search_data[i] );
        win_erase_eol( win, ' ' );

		win_set_curpos( win, offset * 3 + nibble, 0 );
		osd_set_screen_curpos( win_get_cx_abs(win), win_get_cy_abs(win) );

        i = keyboard_read_sync();
		k = keyboard_name(i);

		shift = (1 - nibble) * 4;
		mask = ~(0xf << shift);

        if( strlen(k) == 1 )
		{
			switch( k[0] )
			{
				case '0': case '1': case '2': case '3':
				case '4': case '5': case '6': case '7':
				case '8': case '9': case 'A': case 'B':
				case 'C': case 'D': case 'E': case 'F':
					if( offset == 16 )
						break;
					if( offset == search_count )
						search_count++;
					val = k[0] - '0';
					if( val > 9 ) val -= 7;
					val <<= shift;
					/* now modify the register */
					search_data[offset] = (search_data[offset] & mask ) | val;
					i = KEYCODE_RIGHT;	/* advance to next nibble */
                    break;
            }
		}

		switch( i )
		{
			case KEYCODE_DEL:
				if( offset < search_count )
				{
					for( i = offset; i < 15; i++ )
						search_data[i] = search_data[i+1];
					search_count--;
				}
                break;

			case KEYCODE_INSERT:
				if( search_count < 16 )
				{
					for( i = 15; i > offset; i-- )
						search_data[i] = search_data[i-1];
					search_data[offset] = 0;
					search_count++;
				}
                break;

            case KEYCODE_BACKSPACE:
				if( nibble > 0 || offset > 0 )
				{
					if( nibble > 0 )
					{
						nibble--;
					}
					else
					if( offset > 0 )
					{
						offset--;
						nibble = 1;
					}
					shift = (1 - nibble) * 4;
					mask = ~(0xf << shift);
					/* now modify the value */
					search_data[offset] = search_data[offset] & mask;
				}
				break;

			case KEYCODE_LEFT:
				if( nibble > 0 )
				{
					nibble--;
				}
				else
				if( offset > 0 )
				{
					offset--;
					nibble = 1;
				}
				break;

			case KEYCODE_RIGHT:
				if( offset < search_count )
				{
					if( nibble < 1 )
					{
						nibble++;
					}
					else
					{
						offset++;
						nibble = 0;
					}
				}
				break;

			case KEYCODE_HOME:
				offset = 0;
				nibble = 0;
				break;

			case KEYCODE_END:
				offset = search_count;
				nibble = 0;
				break;
		}
	} while( i != KEYCODE_ENTER && i != KEYCODE_ESC );

	if( i == KEYCODE_ENTER && search_count > 0 )
	{
		static char dbg_info[32+1];
		unsigned addr, start;

		start = (DBGMEM[which].base + DBGMEM[which].offset) & AMASK;

		for( addr = start + 1; addr != start; addr = ++addr & AMASK )
		{
			if( (addr & (AMASK >> 8)) == 0 )
			{
				win_set_title( win, "[%3.0f%%] %s/%s",
					100.0 * addr / (AMASK + 1),
					kilobyte(addr), kilobyte(AMASK + 1) );
				osd_screen_update();
			}
			for( i = 0; i < search_count; i++)
				if( RDMEM( addr+i ) != search_data[i] )
					break;
			if( i == search_count )
			{
				sprintf(dbg_info, "Found at address $%X", addr);
				dbg_info_once = dbg_info;
				DBGMEM[which].offset = addr % DBGMEM[which].size;
				DBGMEM[which].base = addr - DBGMEM[which].offset;
				win_close(win);
                dbg_update = 1;
				return;
			}
		}
		sprintf(dbg_info, "Not found");
		dbg_info_once = dbg_info;
    }
    win_close(win);
}

/**************************************************************************
 * cmd_fast
 * Hmm.. no idea ;)
 **************************************************************************/
static void cmd_fast( void )
{
	dbg_fast = 1;
	cmd_go_break();
}

/**************************************************************************
 * cmd_go_break
 * Let the emulation run and optionally set a breakpoint
 **************************************************************************/
static void cmd_go_break( void )
{
	char *cmd = CMD;
	unsigned brk;
	int length;

	brk = get_register_or_value( &cmd, &length );
	if( length )
		DBG.brk_temp = brk;

	dbg_update = 0;
	dbg_active = 0;

    osd_sound_enable(1);
	osd_set_display(
		Machine->scrbitmap->width,
		Machine->scrbitmap->height,
		Machine->drv->video_attributes);
}

/**************************************************************************
 * cmd_here
 * Set a temporary breakpoint at the cursor PC and let the emulation run
 **************************************************************************/
static void cmd_here( void )
{
	DBG.brk_temp = DBGDASM.pc_cur;

	dbg_update = 0;
	dbg_active = 0;

    osd_sound_enable(1);
	osd_set_display(Machine->scrbitmap->width,
					Machine->scrbitmap->height,
					Machine->drv->video_attributes);

    edit_cmds_reset();
}

/**************************************************************************
 * cmd_set_ignore
 * Ignore a CPU while debugging and tracing
 **************************************************************************/
static void cmd_set_ignore( void )
{
	char *cmd = CMD;
    unsigned cpunum;
	int i, length, already_ignored;

	cpunum = xtou( &cmd, &length );
	if( cpunum < totalcpu )
	{
		if( !dbg[cpunum].ignore )
		{
			for( i = 0, already_ignored = 0; i < totalcpu; i++ )
				if(dbg[i].ignore) ++already_ignored;
			if( already_ignored + 1 >= totalcpu )
			{
				win_msgbox( cur_col[E_ERROR], "Ignore CPU",
					"No, I won't do that! ;-)\nIgnoring all CPUs is a bad idea.");
				edit_cmds_reset();
				return;
            }
			dbg[cpunum].ignore = 1;
			if( cpunum == activecpu )
				cmd_focus_next_cpu();
		}
	}
	else
	{
		win_msgbox( cur_col[E_ERROR], "Ignore CPU",
			"The selected CPU# is too high.\nOnly %d CPUs (0..%d) are used", totalcpu, totalcpu-1);
	}

    edit_cmds_reset();
}

/**************************************************************************
 * cmd_set_observe
 * Observe a CPU while debugging and tracing
 **************************************************************************/
static void cmd_set_observe( void )
{
	char *cmd = CMD;
    unsigned cpunum;
	int length;

	cpunum = xtou( &cmd, &length );
	if( cpunum < totalcpu )
	{
		dbg[cpunum].ignore = 0;
	}
	else
	{
		win_msgbox( cur_col[E_ERROR], "Observe CPU",
			"The selected CPU# is too high.\nOnly %d CPUs (0..%d) are used", totalcpu, totalcpu-1);
    }

    edit_cmds_reset();
}

/**************************************************************************
 * cmd_jump
 * Jump to the specified address in the disassembly window
 **************************************************************************/
static void cmd_jump( void )
{
	char *cmd = CMD;
	unsigned address;
	int length;

	address = get_register_or_value( &cmd, &length );
    if( length > 0 )
	{
		DBGDASM.pc_top = address;
		DBGDASM.pc_cur = DBGDASM.pc_top;
		DBGDASM.pc_end = dump_dasm( DBGDASM.pc_top );
	}
    edit_cmds_reset();
}

/**************************************************************************
 * cmd_replace_register
 * Either change a register to a specified value, change to the
 * registers window to edit a specified or (if none given) the
 * first register
 **************************************************************************/
static void cmd_replace_register( void )
{
    char *cmd = CMD;
	unsigned regnum, address;
    int length;

	regnum = get_register_id( &cmd, &length );
	if( regnum > 0 )
	{
		address = get_register_or_value( &cmd, &length );
		if( length )
		{
			cpu_set_reg( regnum, address );
			if( regnum > 1 )
				dump_regs();
			else
				/* Update in case PC changed */
				dbg_update = 1;
		}
		else
		{
			/* Edit the first register */
			for( DBGREGS.idx = 0; DBGREGS.idx < DBGREGS.count; DBGREGS.idx++ )
				if( DBGREGS.id[DBGREGS.idx] == regnum ) break;
			DBG.window = EDIT_REGS;
        }
    }
	else
	{
		/* Edit the first register */
		DBGREGS.idx = 0;
		DBG.window = EDIT_REGS;
    }
    edit_cmds_reset();
}

/**************************************************************************
 * cmd_set_memory_mode
 * Set display mode of a memory window
 **************************************************************************/
static void cmd_set_memory_mode( void )
{
	char *cmd = CMD;
	unsigned which, mode;
	int length;

	which = dtou( &cmd, &length );
    if( length )
	{
		which = (which - 1) % 2;
		mode = get_option_or_value( &cmd, &length, "BYTE\0WORD\0DWORD\0" );
		if( !length ) mode = 0; /* default to BYTE */
		DBGMEM[which].mode = mode;
	}
    else
	{
		DBGMEM[0].mode = 0;
		DBGMEM[1].mode = 0;
    }
	edit_cmds_reset();
    dbg_update = 1;
}

/**************************************************************************
 * cmd_set_dasm_relative_jumps
 * Turn display of relative jumps as $+/-offset on or off
 **************************************************************************/
static void cmd_set_dasm_relative_jumps( void )
{
	char *cmd = CMD;

	dbg_dasm_relative_jumps = get_boolean( &cmd, NULL );

    edit_cmds_reset();
    dbg_update = 1;
}

/**************************************************************************
 * cmd_trace_to_file
 * Turn tracing to file on or off
 * If it is to be turned on, expect filename and optionally
 * a list of register names to dump
 **************************************************************************/
static void cmd_trace_to_file( void )
{
	char *cmd = CMD;
	const char *filename;
	UINT8 regs[MAX_REGS], regcnt = 0;
	int length;

	filename = get_file_name( &cmd, &length );

	if( !my_stricmp( filename, "OFF" ) )
	{
		trace_done();
	}
	else
	{
		while( *cmd )
		{
			regs[regcnt] = get_register_id( &cmd, &length );
			if( length ) regcnt++;
		}
		regs[regcnt] = 0;

		trace_init( filename, regs );
	}

    edit_cmds_reset();
}

/**************************************************************************
 * cmd_dasm_up
 * Move cursor line to previous instruction
 **************************************************************************/
static void cmd_dasm_up( void )
{
	if ( (DBGDASM.pc_cur >= dasm_line( DBGDASM.pc_top, 1 ) ) &&
		 ((DBGDASM.pc_cur < DBGDASM.pc_end) || (DBGDASM.pc_end < DBGDASM.pc_top)) )
	{
		unsigned dasm_pc_1st = DBGDASM.pc_top;
		unsigned dasm_pc_2nd = DBGDASM.pc_top;
		while( dasm_pc_2nd != DBGDASM.pc_end )
		{
			dasm_pc_2nd = dasm_line( dasm_pc_1st, 1 );

			if( dasm_pc_2nd == DBGDASM.pc_cur )
			{
				DBGDASM.pc_cur = dasm_pc_1st;
				dasm_pc_2nd = DBGDASM.pc_end;
			}
			else
			{
				dasm_pc_1st = dasm_pc_2nd;
			}
		}
	}
	else
	if( DBGDASM.pc_top > 0 )
	{
		/*
		 * Try to find the previous instruction by searching from the
		 * longest instruction length towards the current address.
		 * If we can't find one then just go back one byte,
		 * which means that a previous guess was wrong.
		 */
		unsigned dasm_pc_tmp = rshift(DBGDASM.pc_top - lshift(INSTL)) & AMASK;
		int i;
		for( i = 0; i < INSTL; i += ALIGN )
		{
			if( dasm_line( lshift(dasm_pc_tmp), 1 ) == DBGDASM.pc_top )
				break;
			dasm_pc_tmp += ALIGN;
		}
		dasm_pc_tmp = lshift(dasm_pc_tmp);
		if( dasm_pc_tmp == DBGDASM.pc_top )
			dasm_pc_tmp -= ALIGN;
		DBGDASM.pc_cur = dasm_pc_tmp;
		if( DBGDASM.pc_cur < DBGDASM.pc_top )
			DBGDASM.pc_top = DBGDASM.pc_cur;
	}
	DBGDASM.pc_end = dump_dasm( DBGDASM.pc_top );
    edit_cmds_reset();
}

/**************************************************************************
 * cmd_dasm_down
 * Move cursor line to next instruction
 **************************************************************************/
static void cmd_dasm_down( void )
{
	DBGDASM.pc_cur = dasm_line( DBGDASM.pc_cur, 1 );
	if( DBGDASM.pc_cur >= DBGDASM.pc_end )
		  DBGDASM.pc_top = dasm_line( DBGDASM.pc_top, 1 );
	DBGDASM.pc_end = dump_dasm( DBGDASM.pc_top );
    edit_cmds_reset();
}

/**************************************************************************
 * cmd_dasm_page_up
 * Disassemble previous page
 **************************************************************************/
static void cmd_dasm_page_up( void )
{
	UINT32 i;
    /*
	 * This uses a 'rolling window' of start addresses to work out
	 * the best address to use to generate the previous pagefull of
	 * disassembly - CM 980428
     */
	if( DBGDASM.pc_top > 0 )
    {
		unsigned dasm_pc_row[50];	/* needs to be > max windows height */
        unsigned h = win_get_h(WIN_DASM(activecpu));
		unsigned dasm_pc_tmp = lshift((rshift(DBGDASM.pc_top) - h * INSTL) & AMASK);

		if( dasm_pc_tmp > DBGDASM.pc_top )
		{
			DBGDASM.pc_top = 0;
        }
        else
        {
			for( i= 0; dasm_pc_tmp < DBGDASM.pc_top; i++ )
			{
				dasm_pc_row[i % h] = dasm_pc_tmp;
				dasm_pc_tmp = dasm_line( dasm_pc_tmp, 1 );
			}

			/*
			 * If this ever happens, it's because our
			 * max_inst_len member is too small for the CPU
			 */
			if( i < h )
			{
				dasm_pc_tmp = dasm_pc_row[0];
				win_msgbox(cur_col[E_ERROR], "DBGDASM page up",
					"Increase cpu_intf[].max_inst_len? Line = %d\n", i);
			}
			else
			{
				DBGDASM.pc_top = dasm_pc_row[(i + 1) % h];
			}
		}
    }
	DBGDASM.pc_cur = DBGDASM.pc_top;
	DBGDASM.pc_end = dump_dasm( DBGDASM.pc_top );
    edit_cmds_reset();
}

/**************************************************************************
 * cmd_dasm_page_down
 * Disassemble next page
 **************************************************************************/
static void cmd_dasm_page_down( void )
{
    unsigned h = win_get_h(WIN_DASM(activecpu));

	DBGDASM.pc_top = dasm_line( DBGDASM.pc_top, h );
	DBGDASM.pc_cur = dasm_line( DBGDASM.pc_cur, h );
	DBGDASM.pc_end = dump_dasm( DBGDASM.pc_top );
    edit_cmds_reset();
}

/**************************************************************************
 * cmd_dasm_home
 * Disassemble first page
 **************************************************************************/
static void cmd_dasm_home( void )
{
	DBGDASM.pc_cur = DBGDASM.pc_top = 0;
	DBGDASM.pc_end = dump_dasm( DBGDASM.pc_top );
    edit_cmds_reset();
}

/**************************************************************************
 * cmd_dasm_end
 * Disassemble last page
 **************************************************************************/
static void cmd_dasm_end( void )
{
	unsigned h = win_get_h(WIN_DASM(activecpu));
	unsigned tmp_address = lshift(AMASK - h * INSTL + 1);
	unsigned end_address;
	for( ; ; )
	{
		end_address = dasm_line( tmp_address, h );
		if( end_address < tmp_address )
			break;
		tmp_address += ALIGN;
	}
	DBGDASM.pc_top = tmp_address;
	DBGDASM.pc_cur = dasm_line( DBGDASM.pc_top, h - 1 );
	DBGDASM.pc_end = dump_dasm( DBGDASM.pc_top );
    edit_cmds_reset();
}

/**************************************************************************
 * cmd_brk_exec_toggle
 * Toggle execution break point at cursor line
 **************************************************************************/
static void cmd_brk_exec_toggle( void )
{
	if( DBG.brk_exec == INVALID )
	{
		DBG.brk_exec = DBGDASM.pc_cur;
		DBG.brk_exec_times = 0;
		DBG.brk_exec_reset = 0;
		dbg_update = 1;
    }
	else
	if( DBG.brk_exec == DBGDASM.pc_cur )
	{
		DBG.brk_exec = INVALID;
		DBG.brk_exec_times = 0;
        DBG.brk_exec_reset = 0;
		dbg_update = 1;
    }
	else
	{
		win_msgbox( cur_col[E_PROMPT], "Breakpoint",
			"Cleared execution break point at $%X", DBG.brk_exec );
		DBG.brk_exec = INVALID;
	}
}

/**************************************************************************
 * cmd_dasm_hist_follow
 * Follow the current code or data reference
 **************************************************************************/
static void cmd_dasm_hist_follow( void )
{
	unsigned i, address, access = EA_NONE;

    address = INVALID;
	DBGDASM.dst_ea_value = INVALID;
	DBGDASM.src_ea_value = INVALID;

	dasm_line( DBGDASM.pc_cur, 1 );

	if( DBGDASM.src_ea_value != INVALID )
	{
		access = DBGDASM.src_ea_access;
		address = DBGDASM.src_ea_value;
    }
	if( DBGDASM.dst_ea_value != INVALID )
	{
		access = DBGDASM.dst_ea_access;
		address = DBGDASM.dst_ea_value;
	}
	if( address != INVALID )
	{
		if( DBG.hist_cnt < MAX_HIST )
		{
			i = DBG.hist_cnt;
			/* Save some current values */
			DBG.hist[i].dasm_top = DBGDASM.pc_top;
			DBG.hist[i].dasm_cur = DBGDASM.pc_cur;
			DBG.hist[i].mem1_base = DBGMEM[0].base;
			DBG.hist[i].mem1_offset = DBGMEM[0].offset;
			DBG.hist[i].mem1_nibble = DBGMEM[0].nibble;
			if( access == EA_ABS_PC || access == EA_REL_PC )
			{
				DBGDASM.pc_top = address;
				DBGDASM.pc_cur = address;
				DBGDASM.pc_end = dump_dasm( DBGDASM.pc_top );
                DBG.hist_cnt++;
            }
			else
			if( access == EA_MEM_RD || access == EA_MEM_WR || access == EA_MEM_RDWR ||
				access == EA_ZPG_RD || access == EA_ZPG_WR || access == EA_ZPG_RDWR )
			{
				DBGMEM[0].offset = address	% DBGMEM[0].size;
				DBGMEM[0].base = address - DBGMEM[0].offset;
				DBGMEM[0].nibble = 0;
				dump_mem( 0, 0 );
                DBG.hist_cnt++;
            }
        }
    }
}

/**************************************************************************
 * cmd_dasm_hist_back
 * Back to the previous point in the 'follow history'
 **************************************************************************/
static void cmd_dasm_hist_back( void )
{
	unsigned i;
	if( DBG.hist_cnt > 0)
	{
		i = --DBG.hist_cnt;
		DBGDASM.pc_top = DBG.hist[i].dasm_top;
		DBGDASM.pc_cur = DBG.hist[i].dasm_cur;
		DBGDASM.pc_end = dump_dasm( DBGDASM.pc_top );
		DBGMEM[0].base = DBG.hist[i].mem1_base;
		DBGMEM[0].offset = DBG.hist[i].mem1_offset;
		DBGMEM[0].nibble = DBG.hist[i].mem1_nibble;
		dump_mem( 0, 0 );
    }
}

/**************************************************************************
 * cmd_brk_exec_toggle
 * Toggle data break point at memory location
 **************************************************************************/
static void cmd_brk_data_toggle( void )
{
	int which = DBG.window == EDIT_MEM1 ? 0 : 1;

	if( DBG.brk_data == INVALID )
	{
		unsigned data;
		DBG.brk_data = DBGMEM[which].address;
		data = RDMEM(DBG.brk_data);
		DBG.brk_data_oldval = data;
		DBG.brk_data_newval = INVALID;
    }
	else
	if( DBG.brk_data == DBGMEM[which].address )
	{
		DBG.brk_data = INVALID;
		DBG.brk_data_oldval = INVALID;
        DBG.brk_data_newval = INVALID;
	}
	else
	{
		win_msgbox( cur_col[E_PROMPT], "Data watchpoint",
			"Cleared data watch point at $%X", DBG.brk_data );
		DBG.brk_data = INVALID;
	}
	dbg_update = 1;
    edit_cmds_reset();
}

/**************************************************************************
 * cmd_run_to_cursor
 * Set temporary break point at cursor line and go
 **************************************************************************/
static void cmd_run_to_cursor( void )
{
	DBG.brk_temp = DBGDASM.pc_cur;

    edit_cmds_reset();

    cmd_go();
}

/**************************************************************************
 * cmd_view_screen
 * Show game screen until a key is pressed
 **************************************************************************/
static void cmd_view_screen( void )
{
	unsigned w,h;

	osd_get_screen_size( &w, &h );
    osd_set_display(
		Machine->scrbitmap->width,
		Machine->scrbitmap->height,
        Machine->drv->video_attributes);

	/* Let memory changes eventually do something to the video */
	do
	{
		(*Machine->drv->vh_update)(Machine->scrbitmap,1);
		osd_update_video_and_audio();
	} while (keyboard_read_async() == KEYCODE_NONE);

	osd_set_screen_size( w, h );
	win_invalidate_video();
	win_show( WIN_REGS(activecpu) );
	win_show( WIN_DASM(activecpu) );
	win_show( WIN_MEM1(activecpu) );
	win_show( WIN_MEM2(activecpu) );
	win_show( WIN_CMDS(activecpu) );
}

/**************************************************************************
 * cmd_focus_next_cpu
 * Switch focus to the next CPU
 **************************************************************************/
static void cmd_focus_next_cpu( void )
{
    if( totalcpu > 1 )
    {
		win_set_title(WIN_CMDS(activecpu), "CPU #%d yield", activecpu);
		cpu_yield();
        dbg_update = 1;
		dbg_step = 1;
    }

    edit_cmds_reset();
}

/**************************************************************************
 * cmd_step
 * Step one instruction
 **************************************************************************/
static void cmd_step( void )
{
	dbg_step = 1;
    edit_cmds_reset();
}

/**************************************************************************
 * cmd_animate
 * Run CPU in animated mode
 **************************************************************************/
static void cmd_animate( void )
{
	char *cmd = CMD;
	unsigned data;
	int length;
	data = dtou( &cmd, &length );
	if( length )
		dbg_trace_delay = data;

	dbg_trace = 1;
	dbg_step = 1;
    edit_cmds_reset();
}

/**************************************************************************
 * cmd_step_over
 * Step over the instruction at the cursor line
 * Sets next PC and previous PS to detect a call/bsr type opcode
 * on the next entry into MAME_Debug
 **************************************************************************/
static void cmd_step_over( void )
{
	/* Set next PC to the instruction after the cursor line */
	DBG.next_pc = dasm_line( DBGDASM.pc_cur, 1 );
	DBG.prev_sp = cpu_get_sp();
	dbg_step = 1;
	edit_cmds_reset();
}

/**************************************************************************
 * cmd_switch_window
 * Switch back or forth to the next window:
 * commands, registers, disassembly, memory 1, memory 2
 **************************************************************************/
static void cmd_switch_window( void )
{
	if( keyboard_pressed(KEYCODE_LSHIFT) || keyboard_pressed(KEYCODE_RSHIFT) )
		DBG.window = --DBG.window % DBG_WINDOWS;
	else
		DBG.window = ++DBG.window % DBG_WINDOWS;
}

/**************************************************************************
 * cmd_go
 * Let the game run
 **************************************************************************/
static void cmd_go( void )
{
	debug_key_pressed = 0;

	dbg_update = 0;
	dbg_active = 0;

    edit_cmds_reset();

    osd_sound_enable(1);
	osd_set_display(
		Machine->scrbitmap->width,
		Machine->scrbitmap->height,
		Machine->drv->video_attributes);
}

/**************************************************************************
 * cmd_set_dasm_case
 * Set the case style for the disassembly window
 * "DEFAULT", "LOWER" or "UPPER" are recognized comparing the first letter
 **************************************************************************/
static void cmd_set_dasm_case( void )
{
	char *cmd = CMD;

	if( toupper(cmd[0]) == 'D' )
		dbg_dasm_case = 0;
	else
	if( toupper(cmd[0]) == 'L' )
        dbg_dasm_case = 1;
	else
	if( toupper(cmd[0]) == 'U' )
        dbg_dasm_case = 2;
	else
		dbg_dasm_case = xtou( &cmd, NULL );

    edit_cmds_reset();
	dbg_update = 1;
}

/**************************************************************************
 * cmd_set_mem_squeezed
 * Set allow squeezed memory display
 **************************************************************************/
static void cmd_set_mem_squeezed( void )
{
	char *cmd = CMD;

	dbg_mem_squeezed = get_boolean( &cmd, NULL );

    edit_cmds_reset();
	dbg_update = 1;
}

/**************************************************************************
 * cmd_set_dasm_opcodes
 * Set allow squeezed memory display
 **************************************************************************/
static void cmd_set_dasm_opcodes( void )
{
    char *cmd = CMD;
	UINT32 win = WIN_DASM(activecpu);
    UINT32 w = win_get_w( win );
	UINT32 dw = (INSTL / ALIGN) * (ALIGN * 2 + 1);
	int state;

	state = get_boolean( &cmd, NULL );

	if( dbg_dasm_opcodes != state )
    {
		dbg_dasm_opcodes = state;
        if( state )
		{
			win_set_prio( win, 0 );
            win_set_w( win, w + dw  );
		}
		else
		{
			win_set_prio( win, 1 );
            win_set_w( win, w - dw );
		}
		DBGDASM.pc_end = dump_dasm( DBGDASM.pc_top );
    }

    edit_cmds_reset();
	dbg_update = 1;
}

static void mame_debug_reset_statics( void )
{
	/* Reset the statics */
    memset( &dbg, 0, sizeof(dbg) );

    activecpu = INVALID;
    previous_activecpu = INVALID;
    totalcpu = 0;
    cputype = 0;

    dbg_fast = 0;
    dbg_step = 0;
    dbg_trace = 0;
    dbg_update = 0;
    dbg_update_cur = 0;
    dbg_active = 0;
    dbg_mem_squeezed = 0;
    dbg_dasm_opcodes = 0;
    dbg_dasm_case = 0;
    dbg_dasm_relative_jumps = 0;
    dbg_info_once = NULL;
}


/**************************************************************************
 *	mame_debug_init
 *	This function is called from cpu_run to startup the debugger
 **************************************************************************/
void mame_debug_init(void)
{
	char filename[127+1];
	FILE *file;

    mame_debug_reset_statics();
	osd_set_screen_size( 80, 25 );

    totalcpu = cpu_gettotalcpu();

	for( activecpu = 0; activecpu < totalcpu; activecpu++ )
	{
		DBG.window = EDIT_CMDS;
		DBG.brk_exec = INVALID;
		DBG.brk_exec_times = 0;
		DBG.brk_exec_reset = 0;
		DBG.brk_data = INVALID;
		DBG.brk_data_oldval = INVALID;
		DBG.brk_data_newval = INVALID;
		DBG.brk_regs = INVALID;
		DBG.brk_regs_oldval = INVALID;
		DBG.brk_regs_newval = INVALID;
		DBG.brk_regs_mask = 0xffffffff;
		DBG.brk_temp = INVALID;
		DBGMEM[0].base = 0x0000;
		DBGMEM[1].base = 1 << (ABITS / 2);
		switch( cpunum_align_unit(activecpu) )
		{
			case 1: DBGMEM[0].mode = DBGMEM[1].mode = MODE_HEX_UINT8;  break;
			case 2: DBGMEM[0].mode = DBGMEM[1].mode = MODE_HEX_UINT16; break;
			case 4: DBGMEM[0].mode = DBGMEM[1].mode = MODE_HEX_UINT32; break;
		}
	}

	/* create windows for the active CPU */
	dbg_open_windows();

	/* See if there is an existing global mamedbg config file */
	strcpy( filename, "mamedbg.cfg" );
	file = fopen( filename, "r" );
	if( file )
	{
		char *cmdline = CMD, *p;
		int cmd;

		while( !feof(file) )
		{
			fgets( cmdline, 80, file );
			cmdline[80] = '\0';
			if( *cmdline == ';' || *cmdline == '#' )
				continue;
			p = strchr( cmdline, '\r' );
			if( p ) *p = '\0';
			p = strchr( cmdline, '\n' );
			if( p ) *p = '\0';
			/* Make it all upper case */
			strcpy( cmdline, upper(cmdline) );
			cmd = edit_cmds_parse( cmdline );
			if( cmd != INVALID && commands[cmd].function )
				(*commands[cmd].function)();
		}
		fclose(file);
	}
    for( activecpu = 0; activecpu < totalcpu; activecpu++ )
    {
		/* See if there is an existing startup file <game>.cf<cpunum> */
		sprintf( filename, "%s.cf%d", Machine->gamedrv->name, activecpu );
        file = fopen( filename, "r" );
        if( file )
        {
            char *cmdline = CMD, *p;
            int cmd;

            while( !feof(file) )
            {
                fgets( cmdline, 80, file );
                cmdline[80] = '\0';
                if( *cmdline == ';' || *cmdline == '#' )
                    continue;
                p = strchr( cmdline, '\r' );
                if( p ) *p = '\0';
                p = strchr( cmdline, '\n' );
                if( p ) *p = '\0';
				/* Make it all upper case */
				strcpy( cmdline, upper(cmdline) );
                cmd = edit_cmds_parse( cmdline );
                if( cmd != INVALID && commands[cmd].function )
                    (*commands[cmd].function)();
            }
            fclose(file);
        }
    }

    debug_key_pressed = 1;

	first_time = 1;
}

/**************************************************************************
 *	mame_debug_exit
 *	This function is called from cpu_run to shutdown the debugger
 **************************************************************************/
void mame_debug_exit(void)
{
	dbg_close_windows();
	mame_debug_reset_statics();
}

/**************************************************************************
 **************************************************************************
 *      MAME_Debug
 *      This function is called from within an execution loop of a
 *      CPU core whenever mame_debug is non zero
 **************************************************************************
 **************************************************************************/
void MAME_Debug(void)
{
    if( ++debug_key_delay == 0x7fff )
    {
        debug_key_delay = 0;
        debug_key_pressed = keyboard_pressed(input_port_type_key(IPT_UI_ON_SCREEN_DISPLAY));
    }

    if( dbg_fast )
    {
        if( !debug_key_pressed ) return;
		dbg_fast = 0;
    }

    activecpu = cpu_getactivecpu();

    /* If this CPU shall be ignore, just return */
    if( DBG.ignore ) return;

    cputype = Machine->drv->cpu[activecpu].cpu_type & ~CPU_FLAGS_MASK;

    if( trace_on )
    {
        trace_select();
		trace_output();
    }

	if( DBG.prev_sp )
    {
        /* assume we're in debug */
		dbg_active = 1;
        /* See if we went into a function.
           A 'return' will cause the CPU's stack pointer to be
           greater than the previous stack pointer */
		if( cpu_get_pc() != DBG.next_pc && cpu_get_sp() < DBG.prev_sp )
        {
			/* if so, set the temporary breakpoint on the return PC */
			DBG.brk_temp = DBG.next_pc;
			dbg_update = 0;
			dbg_active = 0;
            osd_sound_enable(1);
            osd_set_display(
                Machine->scrbitmap->width,
                Machine->scrbitmap->height,
                Machine->drv->video_attributes);
        }
		DBG.prev_sp = 0;
    }

	if ( (hit_brk_exec() || hit_brk_data() || hit_brk_regs() || debug_key_pressed) && !dbg_active )
    {
		clock_t curr = clock();
		unsigned w, h;

        debug_key_pressed = 0;

		if( !first_time )
		{
			osd_sound_enable(0);
			do
			{
				osd_update_video_and_audio();	/* give time to the sound hardware to apply the volume change */
			} while( (clock() - curr) < (CLOCKS_PER_SEC / 15) );
		}

        first_time = 0;
		osd_get_screen_size( &w, &h );
		osd_set_screen_size( w, h );
        win_invalidate_video();

		edit_cmds_reset();

		DBG.brk_temp = INVALID;
		dbg_active = 1;
		dbg_update_cur = 1;
        dbg_trace = 0;
    }

	if( dbg_step )
    {
		DBGDASM.pc_cur = cpu_get_pc();
		dbg_step = 0;
    }

    /* Assume we need to update the windows */
	dbg_update = 1;

	while( dbg_active && !dbg_step )
    {
        if( dbg_trace )
        {
			static int trace_delay;
			dbg_step = 1;
            if( ++trace_delay < dbg_trace_delay )
			{
				dbg_update = 0;
			}
			else
			{
				trace_delay = 0;

				if( keyboard_pressed(KEYCODE_SPACE) )
				{
					dbg_trace = 0;
					dbg_step = 0;
				}
			}
        }

		if( dbg_update )
        {
            if( activecpu != previous_activecpu )
            {
                if( previous_activecpu != INVALID )
                {
                    win_hide( WIN_REGS(previous_activecpu) );
                    win_hide( WIN_DASM(previous_activecpu) );
                    win_hide( WIN_MEM1(previous_activecpu) );
                    win_hide( WIN_MEM2(previous_activecpu) );
                    win_hide( WIN_CMDS(previous_activecpu) );
                }
                win_show( WIN_REGS(activecpu) );
                win_show( WIN_DASM(activecpu) );
                win_show( WIN_MEM1(activecpu) );
                win_show( WIN_MEM2(activecpu) );
                win_show( WIN_CMDS(activecpu) );
            }

            dump_regs();
            dump_mem( 0, DBG.window != EDIT_MEM1 );
            dump_mem( 1, DBG.window != EDIT_MEM2 );
			DBGDASM.pc_cpu = cpu_get_pc();
            /* Check if pc_cpu is outside of our disassembly window */
			if( DBGDASM.pc_cpu < DBGDASM.pc_top || DBGDASM.pc_cpu >= DBGDASM.pc_end )
				DBGDASM.pc_top = DBGDASM.pc_cpu;
			DBGDASM.pc_end = dump_dasm( DBGDASM.pc_top );
			if( DBGDASM.pc_cur < DBGDASM.pc_top || DBGDASM.pc_cur >= DBGDASM.pc_end || dbg_update_cur )
            {
				DBGDASM.pc_cur = DBGDASM.pc_cpu;
				DBGDASM.pc_end = dump_dasm( DBGDASM.pc_top );
                dbg_update_cur = 0;
            }
			osd_screen_update();
            dbg_update = 0;
        }

        if( !dbg_trace )
        {
            switch( DBG.window )
            {
                case EDIT_REGS: edit_regs(); break;
                case EDIT_DASM: edit_dasm(); break;
                case EDIT_MEM1: edit_mem(0); break;
                case EDIT_MEM2: edit_mem(1); break;
                case EDIT_CMDS: edit_cmds(); break;
            }
        }
    }

    /* update backup copies of memory and registers */
	if( DBGMEM[0].changed )
    {
		DBGMEM[0].changed = 0;
		memcpy( DBGMEM[0].backup,
				DBGMEM[0].newval, DBGMEM[0].size );
    }
	if( DBGMEM[1].changed )
    {
		DBGMEM[1].changed = 0;
		memcpy( DBGMEM[1].backup,
				DBGMEM[1].newval, DBGMEM[0].size );
    }
	if( DBGREGS.changed )
    {
		DBGREGS.changed = 0;
		memcpy( DBGREGS.backup,
				DBGREGS.newval, DBGREGS.count * sizeof(UINT32) );
    }
}

#endif

