summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/debug/debugcpu.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/debug/debugcpu.c')
-rw-r--r--trunk/src/emu/debug/debugcpu.c3376
1 files changed, 3376 insertions, 0 deletions
diff --git a/trunk/src/emu/debug/debugcpu.c b/trunk/src/emu/debug/debugcpu.c
new file mode 100644
index 00000000000..ad260552815
--- /dev/null
+++ b/trunk/src/emu/debug/debugcpu.c
@@ -0,0 +1,3376 @@
+/*********************************************************************
+
+ debugcpu.c
+
+ Debugger CPU/memory interface engine.
+
+****************************************************************************
+
+ Copyright Aaron Giles
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name 'MAME' nor the names of its contributors may be
+ used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "emuopts.h"
+#include "osdepend.h"
+#include "debugcpu.h"
+#include "debugcmd.h"
+#include "debugcon.h"
+#include "express.h"
+#include "debugvw.h"
+#include "debugger.h"
+#include "debugint/debugint.h"
+#include "uiinput.h"
+#include "xmlfile.h"
+#include <ctype.h>
+#include <zlib.h>
+#if defined(SDLMAME_FREEBSD) || defined(SDLMAME_NETBSD) || defined(SDLMAME_OS2)
+# undef tolower
+#endif
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+#define NUM_TEMP_VARIABLES 10
+
+enum
+{
+ EXECUTION_STATE_STOPPED,
+ EXECUTION_STATE_RUNNING
+};
+
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+/* in mame.h: typedef struct _debugcpu_private debugcpu_private; */
+struct _debugcpu_private
+{
+ device_t *livecpu;
+ device_t *visiblecpu;
+ device_t *breakcpu;
+
+ FILE * source_file; /* script source file */
+
+ symbol_table * symtable; /* global symbol table */
+
+ bool within_instruction_hook;
+ bool vblank_occurred;
+ bool memory_modified;
+ bool debugger_access;
+
+ int execution_state;
+ device_t * m_stop_when_not_device; // stop execution when the device ceases to be this
+
+ UINT32 bpindex;
+ UINT32 wpindex;
+
+ UINT64 wpdata;
+ UINT64 wpaddr;
+ UINT64 tempvar[NUM_TEMP_VARIABLES];
+
+ osd_ticks_t last_periodic_update_time;
+
+ bool comments_loaded;
+};
+
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+/* internal helpers */
+static void debug_cpu_exit(running_machine &machine);
+static void on_vblank(running_machine &machine, screen_device &device, bool vblank_state);
+static void reset_transient_flags(running_machine &machine);
+static void process_source_file(running_machine &machine);
+
+/* expression handlers */
+static UINT64 expression_read_memory(void *param, const char *name, expression_space space, UINT32 address, int size);
+static UINT64 expression_read_program_direct(address_space *space, int opcode, offs_t address, int size);
+static UINT64 expression_read_memory_region(running_machine &machine, const char *rgntag, offs_t address, int size);
+static void expression_write_memory(void *param, const char *name, expression_space space, UINT32 address, int size, UINT64 data);
+static void expression_write_program_direct(address_space *space, int opcode, offs_t address, int size, UINT64 data);
+static void expression_write_memory_region(running_machine &machine, const char *rgntag, offs_t address, int size, UINT64 data);
+static expression_error::error_code expression_validate(void *param, const char *name, expression_space space);
+
+/* variable getters/setters */
+static UINT64 get_cpunum(symbol_table &table, void *ref);
+static UINT64 get_beamx(symbol_table &table, void *ref);
+static UINT64 get_beamy(symbol_table &table, void *ref);
+static UINT64 get_frame(symbol_table &table, void *ref);
+
+
+
+/***************************************************************************
+ INITIALIZATION AND CLEANUP
+***************************************************************************/
+
+/*-------------------------------------------------
+ debug_cpu_init - initialize the CPU
+ information for debugging
+-------------------------------------------------*/
+
+void debug_cpu_init(running_machine &machine)
+{
+ screen_device *first_screen = machine.first_screen();
+ debugcpu_private *global;
+ int regnum;
+
+ /* allocate and reset globals */
+ machine.debugcpu_data = global = auto_alloc_clear(machine, debugcpu_private);
+ global->execution_state = EXECUTION_STATE_STOPPED;
+ global->bpindex = 1;
+ global->wpindex = 1;
+
+ /* create a global symbol table */
+ global->symtable = global_alloc(symbol_table(&machine));
+
+ // configure our base memory accessors
+ debug_cpu_configure_memory(machine, *global->symtable);
+
+ /* add "wpaddr", "wpdata", "cycles", "cpunum", "logunmap" to the global symbol table */
+ global->symtable->add("wpaddr", symbol_table::READ_ONLY, &global->wpaddr);
+ global->symtable->add("wpdata", symbol_table::READ_ONLY, &global->wpdata);
+ global->symtable->add("cpunum", NULL, get_cpunum);
+ global->symtable->add("beamx", (void *)first_screen, get_beamx);
+ global->symtable->add("beamy", (void *)first_screen, get_beamy);
+ global->symtable->add("frame", (void *)first_screen, get_frame);
+
+ /* add the temporary variables to the global symbol table */
+ for (regnum = 0; regnum < NUM_TEMP_VARIABLES; regnum++)
+ {
+ char symname[10];
+ sprintf(symname, "temp%d", regnum);
+ global->symtable->add(symname, symbol_table::READ_WRITE, &global->tempvar[regnum]);
+ }
+
+ /* first CPU is visible by default */
+ global->visiblecpu = machine.firstcpu;
+
+ /* add callback for breaking on VBLANK */
+ if (machine.primary_screen != NULL)
+ machine.primary_screen->register_vblank_callback(vblank_state_delegate(FUNC(on_vblank), &machine));
+
+ machine.add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(FUNC(debug_cpu_exit), &machine));
+}
+
+
+void debug_cpu_configure_memory(running_machine &machine, symbol_table &table)
+{
+ table.configure_memory(&machine, expression_validate, expression_read_memory, expression_write_memory);
+}
+
+
+/*-------------------------------------------------
+ debug_cpu_flush_traces - flushes all traces;
+ this is useful if a trace is going on when we
+ fatalerror
+-------------------------------------------------*/
+
+void debug_cpu_flush_traces(running_machine &machine)
+{
+ /* this can be called on exit even when no debugging is enabled, so
+ make sure the devdebug is valid before proceeding */
+ for (device_t *device = machine.devicelist().first(); device != NULL; device = device->next())
+ if (device->debug() != NULL)
+ device->debug()->trace_flush();
+}
+
+
+
+/***************************************************************************
+ DEBUGGING STATUS AND INFORMATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ cpu_get_visible_cpu - return the visible CPU
+ device (the one that commands should apply to)
+-------------------------------------------------*/
+
+device_t *debug_cpu_get_visible_cpu(running_machine &machine)
+{
+ return machine.debugcpu_data->visiblecpu;
+}
+
+
+/*-------------------------------------------------
+ debug_cpu_within_instruction_hook - true if
+ the debugger is currently live
+-------------------------------------------------*/
+
+int debug_cpu_within_instruction_hook(running_machine &machine)
+{
+ return machine.debugcpu_data->within_instruction_hook;
+}
+
+
+/*-------------------------------------------------
+ debug_cpu_is_stopped - return true if the
+ current execution state is stopped
+-------------------------------------------------*/
+
+int debug_cpu_is_stopped(running_machine &machine)
+{
+ debugcpu_private *global = machine.debugcpu_data;
+ return (global != NULL) ? (global->execution_state == EXECUTION_STATE_STOPPED) : false;
+}
+
+
+
+/***************************************************************************
+ SYMBOL TABLE INTERFACES
+***************************************************************************/
+
+/*-------------------------------------------------
+ debug_cpu_get_global_symtable - return the
+ global symbol table
+-------------------------------------------------*/
+
+symbol_table *debug_cpu_get_global_symtable(running_machine &machine)
+{
+ return machine.debugcpu_data->symtable;
+}
+
+
+/*-------------------------------------------------
+ debug_cpu_get_visible_symtable - return the
+ locally-visible symbol table
+-------------------------------------------------*/
+
+symbol_table *debug_cpu_get_visible_symtable(running_machine &machine)
+{
+ return &machine.debugcpu_data->visiblecpu->debug()->symtable();
+}
+
+
+/*-------------------------------------------------
+ debug_cpu_source_script - specifies a debug
+ command script to execute
+-------------------------------------------------*/
+
+void debug_cpu_source_script(running_machine &machine, const char *file)
+{
+ debugcpu_private *global = machine.debugcpu_data;
+
+ /* close any existing source file */
+ if (global->source_file != NULL)
+ {
+ fclose(global->source_file);
+ global->source_file = NULL;
+ }
+
+ /* open a new one if requested */
+ if (file != NULL)
+ {
+ global->source_file = fopen(file, "r");
+ if (!global->source_file)
+ {
+ if (machine.phase() == MACHINE_PHASE_RUNNING)
+ debug_console_printf(machine, "Cannot open command file '%s'\n", file);
+ else
+ fatalerror("Cannot open command file '%s'", file);
+ }
+ }
+}
+
+
+
+//**************************************************************************
+// MEMORY AND DISASSEMBLY HELPERS
+//**************************************************************************
+
+//-------------------------------------------------
+// debug_comment_save - save all comments for
+// the given machine
+//-------------------------------------------------
+
+bool debug_comment_save(running_machine &machine)
+{
+ // if we don't have a root, bail
+ xml_data_node *root = xml_file_create();
+ if (root == NULL)
+ return false;
+
+ // wrap in a try/catch to handle errors
+ try
+ {
+ // create a comment node
+ xml_data_node *commentnode = xml_add_child(root, "mamecommentfile", NULL);
+ if (commentnode == NULL)
+ throw emu_exception();
+ xml_set_attribute_int(commentnode, "version", COMMENT_VERSION);
+
+ // create a system node
+ xml_data_node *systemnode = xml_add_child(commentnode, "system", NULL);
+ if (systemnode == NULL)
+ throw emu_exception();
+ xml_set_attribute(systemnode, "name", machine.system().name);
+
+ // for each device
+ bool found_comments = false;
+ for (device_t *device = machine.devicelist().first(); device != NULL; device = device->next())
+ if (device->debug()->comment_count() > 0)
+ {
+ // create a node for this device
+ xml_data_node *curnode = xml_add_child(systemnode, "cpu", NULL);
+ if (curnode == NULL)
+ throw emu_exception();
+ xml_set_attribute(curnode, "tag", device->tag());
+
+ // export the comments
+ if (!device->debug()->comment_export(*curnode))
+ throw emu_exception();
+ found_comments = true;
+ }
+
+ // flush the file
+ if (found_comments)
+ {
+ emu_file file(machine.options().comment_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
+ file_error filerr = file.open(machine.basename(), ".cmt");
+ if (filerr == FILERR_NONE)
+ xml_file_write(root, file);
+ }
+ }
+ catch (emu_exception &)
+ {
+ xml_file_free(root);
+ return false;
+ }
+
+ // free and get out of here
+ xml_file_free(root);
+ return true;
+}
+
+
+//-------------------------------------------------
+// debug_comment_load - load all comments for
+// the given machine
+//-------------------------------------------------
+
+bool debug_comment_load(running_machine &machine)
+{
+ // open the file
+ emu_file file(machine.options().comment_directory(), OPEN_FLAG_READ);
+ file_error filerr = file.open(machine.basename(), ".cmt");
+
+ // if an error, just return false
+ if (filerr != FILERR_NONE)
+ return false;
+
+ // wrap in a try/catch to handle errors
+ xml_data_node *root = xml_file_read(file, NULL);
+ try
+ {
+ // read the file
+ if (root == NULL)
+ throw emu_exception();
+
+ // find the config node
+ xml_data_node *commentnode = xml_get_sibling(root->child, "mamecommentfile");
+ if (commentnode == NULL)
+ throw emu_exception();
+
+ // validate the config data version
+ int version = xml_get_attribute_int(commentnode, "version", 0);
+ if (version != COMMENT_VERSION)
+ throw emu_exception();
+
+ // check to make sure the file is applicable
+ xml_data_node *systemnode = xml_get_sibling(commentnode->child, "system");
+ const char *name = xml_get_attribute_string(systemnode, "name", "");
+ if (strcmp(name, machine.system().name) != 0)
+ throw emu_exception();
+
+ // iterate over devices
+ for (xml_data_node *cpunode = xml_get_sibling(systemnode->child, "cpu"); cpunode; cpunode = xml_get_sibling(cpunode->next, "cpu"))
+ {
+ device_t *device = machine.device(xml_get_attribute_string(cpunode, "tag", ""));
+ if (device != NULL)
+ if (!device->debug()->comment_import(*cpunode))
+ throw emu_exception();
+ }
+ }
+ catch (emu_exception &)
+ {
+ // clean up in case of error
+ if (root != NULL)
+ xml_file_free(root);
+ return false;
+ }
+
+ // free the parser
+ xml_file_free(root);
+ return true;
+}
+
+
+
+/***************************************************************************
+ MEMORY AND DISASSEMBLY HELPERS
+***************************************************************************/
+
+/*-------------------------------------------------
+ debug_cpu_translate - return the physical
+ address corresponding to the given logical
+ address
+-------------------------------------------------*/
+
+int debug_cpu_translate(address_space *space, int intention, offs_t *address)
+{
+ device_memory_interface *memory;
+ if (space->device().interface(memory))
+ return memory->translate(space->spacenum(), intention, *address);
+ return true;
+}
+
+
+/***************************************************************************
+ DEBUGGER MEMORY ACCESSORS
+***************************************************************************/
+
+/*-------------------------------------------------
+ debug_read_byte - return a byte from the
+ the specified memory space
+-------------------------------------------------*/
+
+UINT8 debug_read_byte(address_space *_space, offs_t address, int apply_translation)
+{
+ address_space *space = const_cast<address_space *>(_space);
+ debugcpu_private *global = space->machine().debugcpu_data;
+ UINT64 custom;
+ UINT8 result;
+
+ /* mask against the logical byte mask */
+ address &= space->logbytemask();
+
+ /* all accesses from this point on are for the debugger */
+ space->set_debugger_access(global->debugger_access = true);
+
+ /* translate if necessary; if not mapped, return 0xff */
+ if (apply_translation && !debug_cpu_translate(space, TRANSLATE_READ_DEBUG, &address))
+ result = 0xff;
+
+ /* if there is a custom read handler, and it returns true, use that value */
+ else if (space->device().memory().read(space->spacenum(), address, 1, custom))
+ result = custom;
+
+ /* otherwise, call the byte reading function for the translated address */
+ else
+ result = space->read_byte(address);
+
+ /* no longer accessing via the debugger */
+ space->set_debugger_access(global->debugger_access = false);
+ return result;
+}
+
+
+/*-------------------------------------------------
+ debug_read_word - return a word from the
+ specified memory space
+-------------------------------------------------*/
+
+UINT16 debug_read_word(address_space *_space, offs_t address, int apply_translation)
+{
+ address_space *space = const_cast<address_space *>(_space);
+ debugcpu_private *global = space->machine().debugcpu_data;
+ UINT16 result;
+
+ /* mask against the logical byte mask */
+ address &= space->logbytemask();
+
+ /* if this is misaligned read, or if there are no word readers, just read two bytes */
+ if ((address & 1) != 0)
+ {
+ UINT8 byte0 = debug_read_byte(space, address + 0, apply_translation);
+ UINT8 byte1 = debug_read_byte(space, address + 1, apply_translation);
+
+ /* based on the endianness, the result is assembled differently */
+ if (space->endianness() == ENDIANNESS_LITTLE)
+ result = byte0 | (byte1 << 8);
+ else
+ result = byte1 | (byte0 << 8);
+ }
+
+ /* otherwise, this proceeds like the byte case */
+ else
+ {
+ UINT64 custom;
+
+ /* all accesses from this point on are for the debugger */
+ space->set_debugger_access(global->debugger_access = true);
+
+ /* translate if necessary; if not mapped, return 0xffff */
+ if (apply_translation && !debug_cpu_translate(space, TRANSLATE_READ_DEBUG, &address))
+ result = 0xffff;
+
+ /* if there is a custom read handler, and it returns true, use that value */
+ else if (space->device().memory().read(space->spacenum(), address, 2, custom))
+ result = custom;
+
+ /* otherwise, call the byte reading function for the translated address */
+ else
+ result = space->read_word(address);
+
+ /* no longer accessing via the debugger */
+ space->set_debugger_access(global->debugger_access = false);
+ }
+
+ return result;
+}
+
+
+/*-------------------------------------------------
+ debug_read_dword - return a dword from the
+ specified memory space
+-------------------------------------------------*/
+
+UINT32 debug_read_dword(address_space *_space, offs_t address, int apply_translation)
+{
+ address_space *space = const_cast<address_space *>(_space);
+ debugcpu_private *global = space->machine().debugcpu_data;
+ UINT32 result;
+
+ /* mask against the logical byte mask */
+ address &= space->logbytemask();
+
+ /* if this is misaligned read, or if there are no dword readers, just read two words */
+ if ((address & 3) != 0)
+ {
+ UINT16 word0 = debug_read_word(space, address + 0, apply_translation);
+ UINT16 word1 = debug_read_word(space, address + 2, apply_translation);
+
+ /* based on the endianness, the result is assembled differently */
+ if (space->endianness() == ENDIANNESS_LITTLE)
+ result = word0 | (word1 << 16);
+ else
+ result = word1 | (word0 << 16);
+ }
+
+ /* otherwise, this proceeds like the byte case */
+ else
+ {
+ UINT64 custom;
+
+ /* all accesses from this point on are for the debugger */
+ space->set_debugger_access(global->debugger_access = true);
+
+ /* translate if necessary; if not mapped, return 0xffffffff */
+ if (apply_translation && !debug_cpu_translate(space, TRANSLATE_READ_DEBUG, &address))
+ result = 0xffffffff;
+
+ /* if there is a custom read handler, and it returns true, use that value */
+ else if (space->device().memory().read(space->spacenum(), address, 4, custom))
+ result = custom;
+
+ /* otherwise, call the byte reading function for the translated address */
+ else
+ result = space->read_dword(address);
+
+ /* no longer accessing via the debugger */
+ space->set_debugger_access(global->debugger_access = false);
+ }
+
+ return result;
+}
+
+
+/*-------------------------------------------------
+ debug_read_qword - return a qword from the
+ specified memory space
+-------------------------------------------------*/
+
+UINT64 debug_read_qword(address_space *_space, offs_t address, int apply_translation)
+{
+ address_space *space = const_cast<address_space *>(_space);
+ debugcpu_private *global = space->machine().debugcpu_data;
+ UINT64 result;
+
+ /* mask against the logical byte mask */
+ address &= space->logbytemask();
+
+ /* if this is misaligned read, or if there are no qword readers, just read two dwords */
+ if ((address & 7) != 0)
+ {
+ UINT32 dword0 = debug_read_dword(space, address + 0, apply_translation);
+ UINT32 dword1 = debug_read_dword(space, address + 4, apply_translation);
+
+ /* based on the endianness, the result is assembled differently */
+ if (space->endianness() == ENDIANNESS_LITTLE)
+ result = dword0 | ((UINT64)dword1 << 32);
+ else
+ result = dword1 | ((UINT64)dword0 << 32);
+ }
+
+ /* otherwise, this proceeds like the byte case */
+ else
+ {
+ UINT64 custom;
+
+ /* all accesses from this point on are for the debugger */
+ space->set_debugger_access(global->debugger_access = true);
+
+ /* translate if necessary; if not mapped, return 0xffffffffffffffff */
+ if (apply_translation && !debug_cpu_translate(space, TRANSLATE_READ_DEBUG, &address))
+ result = ~(UINT64)0;
+
+ /* if there is a custom read handler, and it returns true, use that value */
+ else if (space->device().memory().read(space->spacenum(), address, 8, custom))
+ result = custom;
+
+ /* otherwise, call the byte reading function for the translated address */
+ else
+ result = space->read_qword(address);
+
+ /* no longer accessing via the debugger */
+ space->set_debugger_access(global->debugger_access = false);
+ }
+
+ return result;
+}
+
+
+/*-------------------------------------------------
+ debug_read_memory - return 1,2,4 or 8 bytes
+ from the specified memory space
+-------------------------------------------------*/
+
+UINT64 debug_read_memory(address_space *space, offs_t address, int size, int apply_translation)
+{
+ UINT64 result = ~(UINT64)0 >> (64 - 8*size);
+ switch (size)
+ {
+ case 1: result = debug_read_byte(space, address, apply_translation); break;
+ case 2: result = debug_read_word(space, address, apply_translation); break;
+ case 4: result = debug_read_dword(space, address, apply_translation); break;
+ case 8: result = debug_read_qword(space, address, apply_translation); break;
+ }
+ return result;
+}
+
+
+/*-------------------------------------------------
+ debug_write_byte - write a byte to the
+ specified memory space
+-------------------------------------------------*/
+
+void debug_write_byte(address_space *_space, offs_t address, UINT8 data, int apply_translation)
+{
+ address_space *space = const_cast<address_space *>(_space);
+ debugcpu_private *global = space->machine().debugcpu_data;
+
+ /* mask against the logical byte mask */
+ address &= space->logbytemask();
+
+ /* all accesses from this point on are for the debugger */
+ space->set_debugger_access(global->debugger_access = true);
+
+ /* translate if necessary; if not mapped, we're done */
+ if (apply_translation && !debug_cpu_translate(space, TRANSLATE_WRITE_DEBUG, &address))
+ ;
+
+ /* if there is a custom write handler, and it returns true, use that */
+ else if (space->device().memory().write(space->spacenum(), address, 1, data))
+ ;
+
+ /* otherwise, call the byte reading function for the translated address */
+ else
+ space->write_byte(address, data);
+
+ /* no longer accessing via the debugger */
+ space->set_debugger_access(global->debugger_access = false);
+ global->memory_modified = true;
+}
+
+
+/*-------------------------------------------------
+ debug_write_word - write a word to the
+ specified memory space
+-------------------------------------------------*/
+
+void debug_write_word(address_space *_space, offs_t address, UINT16 data, int apply_translation)
+{
+ address_space *space = const_cast<address_space *>(_space);
+ debugcpu_private *global = space->machine().debugcpu_data;
+
+ /* mask against the logical byte mask */
+ address &= space->logbytemask();
+
+ /* if this is a misaligned write, or if there are no word writers, just read two bytes */
+ if ((address & 1) != 0)
+ {
+ if (space->endianness() == ENDIANNESS_LITTLE)
+ {
+ debug_write_byte(space, address + 0, data >> 0, apply_translation);
+ debug_write_byte(space, address + 1, data >> 8, apply_translation);
+ }
+ else
+ {
+ debug_write_byte(space, address + 0, data >> 8, apply_translation);
+ debug_write_byte(space, address + 1, data >> 0, apply_translation);
+ }
+ }
+
+ /* otherwise, this proceeds like the byte case */
+ else
+ {
+ /* all accesses from this point on are for the debugger */
+ space->set_debugger_access(global->debugger_access = true);
+
+ /* translate if necessary; if not mapped, we're done */
+ if (apply_translation && !debug_cpu_translate(space, TRANSLATE_WRITE_DEBUG, &address))
+ ;
+
+ /* if there is a custom write handler, and it returns true, use that */
+ else if (space->device().memory().write(space->spacenum(), address, 2, data))
+ ;
+
+ /* otherwise, call the byte reading function for the translated address */
+ else
+ space->write_word(address, data);
+
+ /* no longer accessing via the debugger */
+ space->set_debugger_access(global->debugger_access = false);
+ global->memory_modified = true;
+ }
+}
+
+
+/*-------------------------------------------------
+ debug_write_dword - write a dword to the
+ specified memory space
+-------------------------------------------------*/
+
+void debug_write_dword(address_space *_space, offs_t address, UINT32 data, int apply_translation)
+{
+ address_space *space = const_cast<address_space *>(_space);
+ debugcpu_private *global = space->machine().debugcpu_data;
+
+ /* mask against the logical byte mask */
+ address &= space->logbytemask();
+
+ /* if this is a misaligned write, or if there are no dword writers, just read two words */
+ if ((address & 3) != 0)
+ {
+ if (space->endianness() == ENDIANNESS_LITTLE)
+ {
+ debug_write_word(space, address + 0, data >> 0, apply_translation);
+ debug_write_word(space, address + 2, data >> 16, apply_translation);
+ }
+ else
+ {
+ debug_write_word(space, address + 0, data >> 16, apply_translation);
+ debug_write_word(space, address + 2, data >> 0, apply_translation);
+ }
+ }
+
+ /* otherwise, this proceeds like the byte case */
+ else
+ {
+ /* all accesses from this point on are for the debugger */
+ space->set_debugger_access(global->debugger_access = true);
+
+ /* translate if necessary; if not mapped, we're done */
+ if (apply_translation && !debug_cpu_translate(space, TRANSLATE_WRITE_DEBUG, &address))
+ ;
+
+ /* if there is a custom write handler, and it returns true, use that */
+ else if (space->device().memory().write(space->spacenum(), address, 4, data))
+ ;
+
+ /* otherwise, call the byte reading function for the translated address */
+ else
+ space->write_dword(address, data);
+
+ /* no longer accessing via the debugger */
+ space->set_debugger_access(global->debugger_access = false);
+ global->memory_modified = true;
+ }
+}
+
+
+/*-------------------------------------------------
+ debug_write_qword - write a qword to the
+ specified memory space
+-------------------------------------------------*/
+
+void debug_write_qword(address_space *_space, offs_t address, UINT64 data, int apply_translation)
+{
+ address_space *space = const_cast<address_space *>(_space);
+ debugcpu_private *global = space->machine().debugcpu_data;
+
+ /* mask against the logical byte mask */
+ address &= space->logbytemask();
+
+ /* if this is a misaligned write, or if there are no qword writers, just read two dwords */
+ if ((address & 7) != 0)
+ {
+ if (space->endianness() == ENDIANNESS_LITTLE)
+ {
+ debug_write_dword(space, address + 0, data >> 0, apply_translation);
+ debug_write_dword(space, address + 4, data >> 32, apply_translation);
+ }
+ else
+ {
+ debug_write_dword(space, address + 0, data >> 32, apply_translation);
+ debug_write_dword(space, address + 4, data >> 0, apply_translation);
+ }
+ }
+
+ /* otherwise, this proceeds like the byte case */
+ else
+ {
+ /* all accesses from this point on are for the debugger */
+ space->set_debugger_access(global->debugger_access = true);
+
+ /* translate if necessary; if not mapped, we're done */
+ if (apply_translation && !debug_cpu_translate(space, TRANSLATE_WRITE_DEBUG, &address))
+ ;
+
+ /* if there is a custom write handler, and it returns true, use that */
+ else if (space->device().memory().write(space->spacenum(), address, 8, data))
+ ;
+
+ /* otherwise, call the byte reading function for the translated address */
+ else
+ space->write_qword(address, data);
+
+ /* no longer accessing via the debugger */
+ space->set_debugger_access(global->debugger_access = false);
+ global->memory_modified = true;
+ }
+}
+
+
+/*-------------------------------------------------
+ debug_write_memory - write 1,2,4 or 8 bytes
+ to the specified memory space
+-------------------------------------------------*/
+
+void debug_write_memory(address_space *space, offs_t address, UINT64 data, int size, int apply_translation)
+{
+ switch (size)
+ {
+ case 1: debug_write_byte(space, address, data, apply_translation); break;
+ case 2: debug_write_word(space, address, data, apply_translation); break;
+ case 4: debug_write_dword(space, address, data, apply_translation); break;
+ case 8: debug_write_qword(space, address, data, apply_translation); break;
+ }
+}
+
+
+/*-------------------------------------------------
+ debug_read_opcode - read 1,2,4 or 8 bytes at
+ the given offset from opcode space
+-------------------------------------------------*/
+
+UINT64 debug_read_opcode(address_space *_space, offs_t address, int size, int arg)
+{
+ address_space *space = const_cast<address_space *>(_space);
+ UINT64 result = ~(UINT64)0 & (~(UINT64)0 >> (64 - 8*size)), result2;
+ debugcpu_private *global = space->machine().debugcpu_data;
+
+ /* keep in logical range */
+ address &= space->logbytemask();
+
+ /* return early if we got the result directly */
+ space->set_debugger_access(global->debugger_access = true);
+ device_memory_interface *memory;
+ if (space->device().interface(memory) && memory->readop(address, size, result2))
+ {
+ space->set_debugger_access(global->debugger_access = false);
+ return result2;
+ }
+
+ /* if we're bigger than the address bus, break into smaller pieces */
+ if (size > space->data_width() / 8)
+ {
+ int halfsize = size / 2;
+ UINT64 r0 = debug_read_opcode(space, address + 0, halfsize, arg);
+ UINT64 r1 = debug_read_opcode(space, address + halfsize, halfsize, arg);
+
+ if (space->endianness() == ENDIANNESS_LITTLE)
+ return r0 | (r1 << (8 * halfsize));
+ else
+ return r1 | (r0 << (8 * halfsize));
+ }
+
+ /* translate to physical first */
+ if (!debug_cpu_translate(space, TRANSLATE_FETCH_DEBUG, &address))
+ return result;
+
+ /* keep in physical range */
+ address &= space->bytemask();
+ offs_t addrxor = 0;
+ switch (space->data_width() / 8 * 10 + size)
+ {
+ /* dump opcodes in bytes from a byte-sized bus */
+ case 11:
+ break;
+
+ /* dump opcodes in bytes from a word-sized bus */
+ case 21:
+ addrxor = (space->endianness() == ENDIANNESS_LITTLE) ? BYTE_XOR_LE(0) : BYTE_XOR_BE(0);
+ break;
+
+ /* dump opcodes in words from a word-sized bus */
+ case 22:
+ break;
+
+ /* dump opcodes in bytes from a dword-sized bus */
+ case 41:
+ addrxor = (space->endianness() == ENDIANNESS_LITTLE) ? BYTE4_XOR_LE(0) : BYTE4_XOR_BE(0);
+ break;
+
+ /* dump opcodes in words from a dword-sized bus */
+ case 42:
+ addrxor = (space->endianness() == ENDIANNESS_LITTLE) ? WORD_XOR_LE(0) : WORD_XOR_BE(0);
+ break;
+
+ /* dump opcodes in dwords from a dword-sized bus */
+ case 44:
+ break;
+
+ /* dump opcodes in bytes from a qword-sized bus */
+ case 81:
+ addrxor = (space->endianness() == ENDIANNESS_LITTLE) ? BYTE8_XOR_LE(0) : BYTE8_XOR_BE(0);
+ break;
+
+ /* dump opcodes in words from a qword-sized bus */
+ case 82:
+ addrxor = (space->endianness() == ENDIANNESS_LITTLE) ? WORD2_XOR_LE(0) : WORD2_XOR_BE(0);
+ break;
+
+ /* dump opcodes in dwords from a qword-sized bus */
+ case 84:
+ addrxor = (space->endianness() == ENDIANNESS_LITTLE) ? DWORD_XOR_LE(0) : DWORD_XOR_BE(0);
+ break;
+
+ /* dump opcodes in qwords from a qword-sized bus */
+ case 88:
+ break;
+
+ default:
+ fatalerror("debug_read_opcode: unknown type = %d", space->data_width() / 8 * 10 + size);
+ break;
+ }
+
+ /* turn on debugger access */
+ if (!global->debugger_access)
+ space->set_debugger_access(global->debugger_access = true);
+
+ /* switch off the size and handle unaligned accesses */
+ switch (size)
+ {
+ case 1:
+ result = (arg) ? space->direct().read_raw_byte(address, addrxor) : space->direct().read_decrypted_byte(address, addrxor);
+ break;
+
+ case 2:
+ result = (arg) ? space->direct().read_raw_word(address & ~1, addrxor) : space->direct().read_decrypted_word(address & ~1, addrxor);
+ if ((address & 1) != 0)
+ {
+ result2 = (arg) ? space->direct().read_raw_word((address & ~1) + 2, addrxor) : space->direct().read_decrypted_word((address & ~1) + 2, addrxor);
+ if (space->endianness() == ENDIANNESS_LITTLE)
+ result = (result >> (8 * (address & 1))) | (result2 << (16 - 8 * (address & 1)));
+ else
+ result = (result << (8 * (address & 1))) | (result2 >> (16 - 8 * (address & 1)));
+ result &= 0xffff;
+ }
+ break;
+
+ case 4:
+ result = (arg) ? space->direct().read_raw_dword(address & ~3, addrxor) : space->direct().read_decrypted_dword(address & ~3, addrxor);
+ if ((address & 3) != 0)
+ {
+ result2 = (arg) ? space->direct().read_raw_dword((address & ~3) + 4, addrxor) : space->direct().read_decrypted_dword((address & ~3) + 4, addrxor);
+ if (space->endianness() == ENDIANNESS_LITTLE)
+ result = (result >> (8 * (address & 3))) | (result2 << (32 - 8 * (address & 3)));
+ else
+ result = (result << (8 * (address & 3))) | (result2 >> (32 - 8 * (address & 3)));
+ result &= 0xffffffff;
+ }
+ break;
+
+ case 8:
+ result = (arg) ? space->direct().read_raw_qword(address & ~7, addrxor) : space->direct().read_decrypted_qword(address & ~7, addrxor);
+ if ((address & 7) != 0)
+ {
+ result2 = (arg) ? space->direct().read_raw_qword((address & ~7) + 8, addrxor) : space->direct().read_decrypted_qword((address & ~7) + 8, addrxor);
+ if (space->endianness() == ENDIANNESS_LITTLE)
+ result = (result >> (8 * (address & 7))) | (result2 << (64 - 8 * (address & 7)));
+ else
+ result = (result << (8 * (address & 7))) | (result2 >> (64 - 8 * (address & 7)));
+ }
+ break;
+ }
+
+ /* no longer accessing via the debugger */
+ space->set_debugger_access(global->debugger_access = false);
+ return result;
+}
+
+
+
+/***************************************************************************
+ INTERNAL HELPERS
+***************************************************************************/
+
+/*-------------------------------------------------
+ debug_cpu_exit - free all memory
+-------------------------------------------------*/
+
+static void debug_cpu_exit(running_machine &machine)
+{
+ debugcpu_private *global = machine.debugcpu_data;
+
+ /* free the global symbol table */
+ if (global != NULL)
+ global_free(global->symtable);
+}
+
+
+/*-------------------------------------------------
+ on_vblank - called when a VBLANK hits
+-------------------------------------------------*/
+
+static void on_vblank(running_machine &machine, screen_device &device, bool vblank_state)
+{
+ /* just set a global flag to be consumed later */
+ if (vblank_state)
+ device.machine().debugcpu_data->vblank_occurred = true;
+}
+
+
+/*-------------------------------------------------
+ reset_transient_flags - reset the transient
+ flags on all CPUs
+-------------------------------------------------*/
+
+static void reset_transient_flags(running_machine &machine)
+{
+ /* loop over CPUs and reset the transient flags */
+ for (device_t *device = machine.devicelist().first(); device != NULL; device = device->next())
+ device->debug()->reset_transient_flag();
+ machine.debugcpu_data->m_stop_when_not_device = NULL;
+}
+
+
+/*-------------------------------------------------
+ process_source_file - executes commands from
+ a source file
+-------------------------------------------------*/
+
+static void process_source_file(running_machine &machine)
+{
+ debugcpu_private *global = machine.debugcpu_data;
+
+ /* loop until the file is exhausted or until we are executing again */
+ while (global->source_file != NULL && global->execution_state == EXECUTION_STATE_STOPPED)
+ {
+ char buf[512];
+ int i;
+ char *s;
+
+ /* stop at the end of file */
+ if (feof(global->source_file))
+ {
+ fclose(global->source_file);
+ global->source_file = NULL;
+ return;
+ }
+
+ /* fetch the next line */
+ memset(buf, 0, sizeof(buf));
+ fgets(buf, sizeof(buf), global->source_file);
+
+ /* strip out comments (text after '//') */
+ s = strstr(buf, "//");
+ if (s)
+ *s = '\0';
+
+ /* strip whitespace */
+ i = (int)strlen(buf);
+ while((i > 0) && (isspace((UINT8)buf[i-1])))
+ buf[--i] = '\0';
+
+ /* execute the command */
+ if (buf[0])
+ debug_console_execute_command(machine, buf, 1);
+ }
+}
+
+
+
+/***************************************************************************
+ EXPRESSION HANDLERS
+***************************************************************************/
+
+/*-------------------------------------------------
+ expression_get_device - return a device
+ based on a case insensitive tag search
+-------------------------------------------------*/
+
+static device_t *expression_get_device(running_machine &machine, const char *tag)
+{
+ device_t *device;
+
+ for (device = machine.devicelist().first(); device != NULL; device = device->next())
+ if (mame_stricmp(device->tag(), tag) == 0)
+ return device;
+
+ return NULL;
+}
+
+
+/*-------------------------------------------------
+ expression_read_memory - read 1,2,4 or 8 bytes
+ at the given offset in the given address
+ space
+-------------------------------------------------*/
+
+static UINT64 expression_read_memory(void *param, const char *name, expression_space spacenum, UINT32 address, int size)
+{
+ running_machine &machine = *(running_machine *)param;
+ UINT64 result = ~(UINT64)0 >> (64 - 8*size);
+ device_t *device = NULL;
+ address_space *space;
+
+ switch (spacenum)
+ {
+ case EXPSPACE_PROGRAM_LOGICAL:
+ case EXPSPACE_DATA_LOGICAL:
+ case EXPSPACE_IO_LOGICAL:
+ case EXPSPACE_SPACE3_LOGICAL:
+ if (name != NULL)
+ device = expression_get_device(machine, name);
+ if (device == NULL)
+ device = debug_cpu_get_visible_cpu(machine);
+ space = device->memory().space(AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_LOGICAL));
+ if (space != NULL)
+ result = debug_read_memory(space, space->address_to_byte(address), size, true);
+ break;
+
+ case EXPSPACE_PROGRAM_PHYSICAL:
+ case EXPSPACE_DATA_PHYSICAL:
+ case EXPSPACE_IO_PHYSICAL:
+ case EXPSPACE_SPACE3_PHYSICAL:
+ if (name != NULL)
+ device = expression_get_device(machine, name);
+ if (device == NULL)
+ device = debug_cpu_get_visible_cpu(machine);
+ space = device->memory().space(AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_PHYSICAL));
+ if (space != NULL)
+ result = debug_read_memory(space, space->address_to_byte(address), size, false);
+ break;
+
+ case EXPSPACE_OPCODE:
+ case EXPSPACE_RAMWRITE:
+ if (name != NULL)
+ device = expression_get_device(machine, name);
+ if (device == NULL)
+ device = debug_cpu_get_visible_cpu(machine);
+ result = expression_read_program_direct(device->memory().space(AS_PROGRAM), (spacenum == EXPSPACE_OPCODE), address, size);
+ break;
+
+ case EXPSPACE_REGION:
+ if (name == NULL)
+ break;
+ result = expression_read_memory_region(machine, name, address, size);
+ break;
+
+ default:
+ break;
+ }
+ return result;
+}
+
+
+/*-------------------------------------------------
+ expression_read_program_direct - read memory
+ directly from an opcode or RAM pointer
+-------------------------------------------------*/
+
+static UINT64 expression_read_program_direct(address_space *_space, int opcode, offs_t address, int size)
+{
+ address_space *space = const_cast<address_space *>(_space);
+ UINT64 result = ~(UINT64)0 >> (64 - 8*size);
+
+ if (space != NULL)
+ {
+ UINT8 *base;
+
+ /* adjust the address into a byte address, but not if being called recursively */
+ if ((opcode & 2) == 0)
+ address = space->address_to_byte(address);
+
+ /* call ourself recursively until we are byte-sized */
+ if (size > 1)
+ {
+ int halfsize = size / 2;
+ UINT64 r0, r1;
+
+ /* read each half, from lower address to upper address */
+ r0 = expression_read_program_direct(space, opcode | 2, address + 0, halfsize);
+ r1 = expression_read_program_direct(space, opcode | 2, address + halfsize, halfsize);
+
+ /* assemble based on the target endianness */
+ if (space->endianness() == ENDIANNESS_LITTLE)
+ result = r0 | (r1 << (8 * halfsize));
+ else
+ result = r1 | (r0 << (8 * halfsize));
+ }
+
+ /* handle the byte-sized final requests */
+ else
+ {
+ /* lowmask specified which address bits are within the databus width */
+ offs_t lowmask = space->data_width() / 8 - 1;
+
+ /* get the base of memory, aligned to the address minus the lowbits */
+ if (opcode & 1)
+ base = (UINT8 *)space->direct().read_decrypted_ptr(address & ~lowmask);
+ else
+ base = (UINT8 *)space->get_read_ptr(address & ~lowmask);
+
+ /* if we have a valid base, return the appropriate byte */
+ if (base != NULL)
+ {
+ if (space->endianness() == ENDIANNESS_LITTLE)
+ result = base[BYTE8_XOR_LE(address) & lowmask];
+ else
+ result = base[BYTE8_XOR_BE(address) & lowmask];
+ }
+ }
+ }
+ return result;
+}
+
+
+/*-------------------------------------------------
+ expression_read_memory_region - read memory
+ from a memory region
+-------------------------------------------------*/
+
+static UINT64 expression_read_memory_region(running_machine &machine, const char *rgntag, offs_t address, int size)
+{
+ const memory_region *region = machine.region(rgntag);
+ UINT64 result = ~(UINT64)0 >> (64 - 8*size);
+
+ /* make sure we get a valid base before proceeding */
+ if (region != NULL)
+ {
+ /* call ourself recursively until we are byte-sized */
+ if (size > 1)
+ {
+ int halfsize = size / 2;
+ UINT64 r0, r1;
+
+ /* read each half, from lower address to upper address */
+ r0 = expression_read_memory_region(machine, rgntag, address + 0, halfsize);
+ r1 = expression_read_memory_region(machine, rgntag, address + halfsize, halfsize);
+
+ /* assemble based on the target endianness */
+ if (region->endianness() == ENDIANNESS_LITTLE)
+ result = r0 | (r1 << (8 * halfsize));
+ else
+ result = r1 | (r0 << (8 * halfsize));
+ }
+
+ /* only process if we're within range */
+ else if (address < region->bytes())
+ {
+ /* lowmask specified which address bits are within the databus width */
+ UINT32 lowmask = region->width() - 1;
+ UINT8 *base = region->base() + (address & ~lowmask);
+
+ /* if we have a valid base, return the appropriate byte */
+ if (region->endianness() == ENDIANNESS_LITTLE)
+ result = base[BYTE8_XOR_LE(address) & lowmask];
+ else
+ result = base[BYTE8_XOR_BE(address) & lowmask];
+ }
+ }
+ return result;
+}
+
+
+/*-------------------------------------------------
+ expression_write_memory - write 1,2,4 or 8
+ bytes at the given offset in the given address
+ space
+-------------------------------------------------*/
+
+static void expression_write_memory(void *param, const char *name, expression_space spacenum, UINT32 address, int size, UINT64 data)
+{
+ running_machine &machine = *(running_machine *)param;
+ device_t *device = NULL;
+ address_space *space;
+
+ switch (spacenum)
+ {
+ case EXPSPACE_PROGRAM_LOGICAL:
+ case EXPSPACE_DATA_LOGICAL:
+ case EXPSPACE_IO_LOGICAL:
+ case EXPSPACE_SPACE3_LOGICAL:
+ if (name != NULL)
+ device = expression_get_device(machine, name);
+ if (device == NULL)
+ device = debug_cpu_get_visible_cpu(machine);
+ space = device->memory().space(AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_LOGICAL));
+ if (space != NULL)
+ debug_write_memory(space, space->address_to_byte(address), data, size, true);
+ break;
+
+ case EXPSPACE_PROGRAM_PHYSICAL:
+ case EXPSPACE_DATA_PHYSICAL:
+ case EXPSPACE_IO_PHYSICAL:
+ case EXPSPACE_SPACE3_PHYSICAL:
+ if (name != NULL)
+ device = expression_get_device(machine, name);
+ if (device == NULL)
+ device = debug_cpu_get_visible_cpu(machine);
+ space = device->memory().space(AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_PHYSICAL));
+ if (space != NULL)
+ debug_write_memory(space, space->address_to_byte(address), data, size, false);
+ break;
+
+ case EXPSPACE_OPCODE:
+ case EXPSPACE_RAMWRITE:
+ if (name != NULL)
+ device = expression_get_device(machine, name);
+ if (device == NULL)
+ device = debug_cpu_get_visible_cpu(machine);
+ expression_write_program_direct(device->memory().space(AS_PROGRAM), (spacenum == EXPSPACE_OPCODE), address, size, data);
+ break;
+
+ case EXPSPACE_REGION:
+ if (name == NULL)
+ break;
+ expression_write_memory_region(machine, name, address, size, data);
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+/*-------------------------------------------------
+ expression_write_program_direct - write memory
+ directly to an opcode or RAM pointer
+-------------------------------------------------*/
+
+static void expression_write_program_direct(address_space *_space, int opcode, offs_t address, int size, UINT64 data)
+{
+ address_space *space = const_cast<address_space *>(_space);
+ if (space != NULL)
+ {
+ debugcpu_private *global = space->machine().debugcpu_data;
+ UINT8 *base;
+
+ /* adjust the address into a byte address, but not if being called recursively */
+ if ((opcode & 2) == 0)
+ address = space->address_to_byte(address);
+
+ /* call ourself recursively until we are byte-sized */
+ if (size > 1)
+ {
+ int halfsize = size / 2;
+ UINT64 r0, r1, halfmask;
+
+ /* break apart based on the target endianness */
+ halfmask = ~(UINT64)0 >> (64 - 8 * halfsize);
+ if (space->endianness() == ENDIANNESS_LITTLE)
+ {
+ r0 = data & halfmask;
+ r1 = (data >> (8 * halfsize)) & halfmask;
+ }
+ else
+ {
+ r0 = (data >> (8 * halfsize)) & halfmask;
+ r1 = data & halfmask;
+ }
+
+ /* write each half, from lower address to upper address */
+ expression_write_program_direct(space, opcode | 2, address + 0, halfsize, r0);
+ expression_write_program_direct(space, opcode | 2, address + halfsize, halfsize, r1);
+ }
+
+ /* handle the byte-sized final case */
+ else
+ {
+ /* lowmask specified which address bits are within the databus width */
+ offs_t lowmask = space->data_width() / 8 - 1;
+
+ /* get the base of memory, aligned to the address minus the lowbits */
+ if (opcode & 1)
+ base = (UINT8 *)space->direct().read_decrypted_ptr(address & ~lowmask);
+ else
+ base = (UINT8 *)space->get_read_ptr(address & ~lowmask);
+
+ /* if we have a valid base, write the appropriate byte */
+ if (base != NULL)
+ {
+ if (space->endianness() == ENDIANNESS_LITTLE)
+ base[BYTE8_XOR_LE(address) & lowmask] = data;
+ else
+ base[BYTE8_XOR_BE(address) & lowmask] = data;
+ global->memory_modified = true;
+ }
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ expression_write_memory_region - write memory
+ from a memory region
+-------------------------------------------------*/
+
+static void expression_write_memory_region(running_machine &machine, const char *rgntag, offs_t address, int size, UINT64 data)
+{
+ debugcpu_private *global = machine.debugcpu_data;
+ const memory_region *region = machine.region(rgntag);
+
+ /* make sure we get a valid base before proceeding */
+ if (region != NULL)
+ {
+ /* call ourself recursively until we are byte-sized */
+ if (size > 1)
+ {
+ int halfsize = size / 2;
+ UINT64 r0, r1, halfmask;
+
+ /* break apart based on the target endianness */
+ halfmask = ~(UINT64)0 >> (64 - 8 * halfsize);
+ if (region->endianness() == ENDIANNESS_LITTLE)
+ {
+ r0 = data & halfmask;
+ r1 = (data >> (8 * halfsize)) & halfmask;
+ }
+ else
+ {
+ r0 = (data >> (8 * halfsize)) & halfmask;
+ r1 = data & halfmask;
+ }
+
+ /* write each half, from lower address to upper address */
+ expression_write_memory_region(machine, rgntag, address + 0, halfsize, r0);
+ expression_write_memory_region(machine, rgntag, address + halfsize, halfsize, r1);
+ }
+
+ /* only process if we're within range */
+ else if (address < region->bytes())
+ {
+ /* lowmask specified which address bits are within the databus width */
+ UINT32 lowmask = region->width() - 1;
+ UINT8 *base = region->base() + (address & ~lowmask);
+
+ /* if we have a valid base, set the appropriate byte */
+ if (region->endianness() == ENDIANNESS_LITTLE)
+ base[BYTE8_XOR_LE(address) & lowmask] = data;
+ else
+ base[BYTE8_XOR_BE(address) & lowmask] = data;
+ global->memory_modified = true;
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ expression_validate - validate that the
+ provided expression references an
+ appropriate name
+-------------------------------------------------*/
+
+static expression_error::error_code expression_validate(void *param, const char *name, expression_space space)
+{
+ running_machine &machine = *(running_machine *)param;
+ device_t *device = NULL;
+
+ switch (space)
+ {
+ case EXPSPACE_PROGRAM_LOGICAL:
+ case EXPSPACE_DATA_LOGICAL:
+ case EXPSPACE_IO_LOGICAL:
+ case EXPSPACE_SPACE3_LOGICAL:
+ if (name != NULL)
+ {
+ device = expression_get_device(machine, name);
+ if (device == NULL)
+ return expression_error::INVALID_MEMORY_NAME;
+ }
+ if (device == NULL)
+ device = debug_cpu_get_visible_cpu(machine);
+ if (device->memory().space(AS_PROGRAM + (space - EXPSPACE_PROGRAM_LOGICAL)) == NULL)
+ return expression_error::NO_SUCH_MEMORY_SPACE;
+ break;
+
+ case EXPSPACE_PROGRAM_PHYSICAL:
+ case EXPSPACE_DATA_PHYSICAL:
+ case EXPSPACE_IO_PHYSICAL:
+ case EXPSPACE_SPACE3_PHYSICAL:
+ if (name != NULL)
+ {
+ device = expression_get_device(machine, name);
+ if (device == NULL)
+ return expression_error::INVALID_MEMORY_NAME;
+ }
+ if (device == NULL)
+ device = debug_cpu_get_visible_cpu(machine);
+ if (device->memory().space(AS_PROGRAM + (space - EXPSPACE_PROGRAM_PHYSICAL)) == NULL)
+ return expression_error::NO_SUCH_MEMORY_SPACE;
+ break;
+
+ case EXPSPACE_OPCODE:
+ case EXPSPACE_RAMWRITE:
+ if (name != NULL)
+ {
+ device = expression_get_device(machine, name);
+ if (device == NULL)
+ return expression_error::INVALID_MEMORY_NAME;
+ }
+ if (device == NULL)
+ device = debug_cpu_get_visible_cpu(machine);
+ if (device->memory().space(AS_PROGRAM) == NULL)
+ return expression_error::NO_SUCH_MEMORY_SPACE;
+ break;
+
+ case EXPSPACE_REGION:
+ if (name == NULL)
+ return expression_error::MISSING_MEMORY_NAME;
+ if (machine.region(name)->base() == NULL)
+ return expression_error::INVALID_MEMORY_NAME;
+ break;
+
+ default:
+ return expression_error::NO_SUCH_MEMORY_SPACE;
+ }
+ return expression_error::NONE;
+}
+
+
+
+/***************************************************************************
+ VARIABLE GETTERS/SETTERS
+***************************************************************************/
+
+/*-------------------------------------------------
+ get_beamx - get beam horizontal position
+-------------------------------------------------*/
+
+static UINT64 get_beamx(symbol_table &table, void *ref)
+{
+ screen_device *screen = reinterpret_cast<screen_device *>(ref);
+ return (screen != NULL) ? screen->hpos() : 0;
+}
+
+
+/*-------------------------------------------------
+ get_beamy - get beam vertical position
+-------------------------------------------------*/
+
+static UINT64 get_beamy(symbol_table &table, void *ref)
+{
+ screen_device *screen = reinterpret_cast<screen_device *>(ref);
+ return (screen != NULL) ? screen->vpos() : 0;
+}
+
+
+/*-------------------------------------------------
+ get_frame - get current frame number
+-------------------------------------------------*/
+
+static UINT64 get_frame(symbol_table &table, void *ref)
+{
+ screen_device *screen = reinterpret_cast<screen_device *>(ref);
+ return (screen != NULL) ? screen->frame_number() : 0;
+}
+
+
+/*-------------------------------------------------
+ get_cpunum - getter callback for the
+ 'cpunum' symbol
+-------------------------------------------------*/
+
+static UINT64 get_cpunum(symbol_table &table, void *ref)
+{
+ running_machine &machine = *reinterpret_cast<running_machine *>(table.globalref());
+ device_t *target = machine.debugcpu_data->visiblecpu;
+
+ device_execute_interface *exec = NULL;
+ int index = 0;
+ for (bool gotone = machine.devicelist().first(exec); gotone; gotone = exec->next(exec))
+ {
+ if (&exec->device() == target)
+ return index;
+ index++;
+ }
+ return 0;
+}
+
+
+
+//**************************************************************************
+// DEVICE DEBUG
+//**************************************************************************
+
+//-------------------------------------------------
+// device_debug - constructor
+//-------------------------------------------------
+
+device_debug::device_debug(device_t &device)
+ : m_device(device),
+ m_exec(NULL),
+ m_memory(NULL),
+ m_state(NULL),
+ m_disasm(NULL),
+ m_flags(0),
+ m_symtable(&device, debug_cpu_get_global_symtable(device.machine())),
+ m_instrhook(NULL),
+ m_dasm_override(NULL),
+ m_opwidth(0),
+ m_stepaddr(0),
+ m_stepsleft(0),
+ m_stopaddr(0),
+ m_stoptime(attotime::zero),
+ m_stopirq(0),
+ m_stopexception(0),
+ m_endexectime(attotime::zero),
+ m_pc_history_index(0),
+ m_bplist(NULL),
+ m_trace(NULL),
+ m_hotspots(NULL),
+ m_hotspot_count(0),
+ m_hotspot_threshhold(0)
+{
+ memset(m_pc_history, 0, sizeof(m_pc_history));
+ memset(m_wplist, 0, sizeof(m_wplist));
+
+ // find out which interfaces we have to work with
+ device.interface(m_exec);
+ device.interface(m_memory);
+ device.interface(m_state);
+ device.interface(m_disasm);
+
+ // set up state-related stuff
+ if (m_state != NULL)
+ {
+ // add a global symbol for the current instruction pointer
+ if (m_exec != NULL)
+ m_symtable.add("cycles", NULL, get_cycles);
+
+ // add entries to enable/disable unmap reporting for each space
+ if (m_memory != NULL)
+ {
+ if (m_memory->space(AS_PROGRAM) != NULL)
+ m_symtable.add("logunmap", (void *)m_memory->space(AS_PROGRAM), get_logunmap, set_logunmap);
+ if (m_memory->space(AS_DATA) != NULL)
+ m_symtable.add("logunmapd", (void *)m_memory->space(AS_DATA), get_logunmap, set_logunmap);
+ if (m_memory->space(AS_IO) != NULL)
+ m_symtable.add("logunmapi", (void *)m_memory->space(AS_IO), get_logunmap, set_logunmap);
+ }
+
+ // add all registers into it
+ astring tempstr;
+ for (const device_state_entry *entry = m_state->state_first(); entry != NULL; entry = entry->next())
+ m_symtable.add(tempstr.cpy(entry->symbol()).tolower(), (void *)(FPTR)entry->index(), get_state, set_state);
+ }
+
+ // set up execution-related stuff
+ if (m_exec != NULL)
+ {
+ m_flags = DEBUG_FLAG_OBSERVING | DEBUG_FLAG_HISTORY;
+ m_opwidth = min_opcode_bytes();
+
+ // if no curpc, add one
+ if (m_state != NULL && m_symtable.find("curpc") == NULL)
+ m_symtable.add("curpc", NULL, get_current_pc);
+ }
+}
+
+
+//-------------------------------------------------
+// ~device_debug - constructor
+//-------------------------------------------------
+
+device_debug::~device_debug()
+{
+ // free breakpoints and watchpoints
+ breakpoint_clear_all();
+ watchpoint_clear_all();
+}
+
+
+//-------------------------------------------------
+// start_hook - the scheduler calls this hook
+// before beginning execution for the given device
+//-------------------------------------------------
+
+void device_debug::start_hook(attotime endtime)
+{
+ debugcpu_private *global = m_device.machine().debugcpu_data;
+
+ assert((m_device.machine().debug_flags & DEBUG_FLAG_ENABLED) != 0);
+
+ // stash a pointer to the current live CPU
+ assert(global->livecpu == NULL);
+ global->livecpu = &m_device;
+
+ // if we're a new device, stop now
+ if (global->m_stop_when_not_device != NULL && global->m_stop_when_not_device != &m_device)
+ {
+ global->m_stop_when_not_device = NULL;
+ global->execution_state = EXECUTION_STATE_STOPPED;
+ reset_transient_flags(m_device.machine());
+ }
+
+ // update the target execution end time
+ m_endexectime = endtime;
+
+ // if we're running, do some periodic updating
+ if (global->execution_state != EXECUTION_STATE_STOPPED)
+ {
+ // check for periodic updates
+ if (&m_device == global->visiblecpu && osd_ticks() > global->last_periodic_update_time + osd_ticks_per_second()/4)
+ {
+ m_device.machine().debug_view().update_all();
+ m_device.machine().debug_view().flush_osd_updates();
+ global->last_periodic_update_time = osd_ticks();
+ }
+
+ // check for pending breaks
+ else if (&m_device == global->breakcpu)
+ {
+ global->execution_state = EXECUTION_STATE_STOPPED;
+ global->breakcpu = NULL;
+ }
+
+ // if a VBLANK occurred, check on things
+ if (global->vblank_occurred)
+ {
+ global->vblank_occurred = false;
+
+ // if we were waiting for a VBLANK, signal it now
+ if ((m_flags & DEBUG_FLAG_STOP_VBLANK) != 0)
+ {
+ global->execution_state = EXECUTION_STATE_STOPPED;
+ debug_console_printf(m_device.machine(), "Stopped at VBLANK\n");
+ }
+ }
+ // check for debug keypresses
+ if (ui_input_pressed(m_device.machine(), IPT_UI_DEBUG_BREAK))
+ global->visiblecpu->debug()->halt_on_next_instruction("User-initiated break\n");
+ }
+
+ // recompute the debugging mode
+ compute_debug_flags();
+}
+
+
+//-------------------------------------------------
+// stop_hook - the scheduler calls this hook when
+// ending execution for the given device
+//-------------------------------------------------
+
+void device_debug::stop_hook()
+{
+ debugcpu_private *global = m_device.machine().debugcpu_data;
+
+ assert(global->livecpu == &m_device);
+
+ // clear the live CPU
+ global->livecpu = NULL;
+}
+
+
+//-------------------------------------------------
+// interrupt_hook - called when an interrupt is
+// acknowledged
+//-------------------------------------------------
+
+void device_debug::interrupt_hook(int irqline)
+{
+ debugcpu_private *global = m_device.machine().debugcpu_data;
+
+ // see if this matches a pending interrupt request
+ if ((m_flags & DEBUG_FLAG_STOP_INTERRUPT) != 0 && (m_stopirq == -1 || m_stopirq == irqline))
+ {
+ global->execution_state = EXECUTION_STATE_STOPPED;
+ debug_console_printf(m_device.machine(), "Stopped on interrupt (CPU '%s', IRQ %d)\n", m_device.tag(), irqline);
+ compute_debug_flags();
+ }
+}
+
+
+//-------------------------------------------------
+// exception_hook - called when an exception is
+// generated
+//-------------------------------------------------
+
+void device_debug::exception_hook(int exception)
+{
+ debugcpu_private *global = m_device.machine().debugcpu_data;
+
+ // see if this matches a pending interrupt request
+ if ((m_flags & DEBUG_FLAG_STOP_EXCEPTION) != 0 && (m_stopexception == -1 || m_stopexception == exception))
+ {
+ global->execution_state = EXECUTION_STATE_STOPPED;
+ debug_console_printf(m_device.machine(), "Stopped on exception (CPU '%s', exception %d)\n", m_device.tag(), exception);
+ compute_debug_flags();
+ }
+}
+
+
+//-------------------------------------------------
+// instruction_hook - called by the CPU cores
+// before executing each instruction
+//-------------------------------------------------
+
+void device_debug::instruction_hook(offs_t curpc)
+{
+ running_machine &machine = m_device.machine();
+ debugcpu_private *global = machine.debugcpu_data;
+
+ // note that we are in the debugger code
+ global->within_instruction_hook = true;
+
+ // update the history
+ m_pc_history[m_pc_history_index++ % HISTORY_SIZE] = curpc;
+
+ // are we tracing?
+ if (m_trace != NULL)
+ m_trace->update(curpc);
+
+ // per-instruction hook?
+ if (global->execution_state != EXECUTION_STATE_STOPPED && (m_flags & DEBUG_FLAG_HOOKED) != 0 && (*m_instrhook)(m_device, curpc))
+ global->execution_state = EXECUTION_STATE_STOPPED;
+
+ // handle single stepping
+ if (global->execution_state != EXECUTION_STATE_STOPPED && (m_flags & DEBUG_FLAG_STEPPING_ANY) != 0)
+ {
+ // is this an actual step?
+ if (m_stepaddr == ~0 || curpc == m_stepaddr)
+ {
+ // decrement the count and reset the breakpoint
+ m_stepsleft--;
+ m_stepaddr = ~0;
+
+ // if we hit 0, stop
+ if (m_stepsleft == 0)
+ global->execution_state = EXECUTION_STATE_STOPPED;
+
+ // update every 100 steps until we are within 200 of the end
+ else if ((m_flags & DEBUG_FLAG_STEPPING_OUT) == 0 && (m_stepsleft < 200 || m_stepsleft % 100 == 0))
+ {
+ machine.debug_view().update_all();
+ machine.debug_view().flush_osd_updates();
+ debugger_refresh_display(machine);
+ }
+ }
+ }
+
+ // handle breakpoints
+ if (global->execution_state != EXECUTION_STATE_STOPPED && (m_flags & (DEBUG_FLAG_STOP_TIME | DEBUG_FLAG_STOP_PC | DEBUG_FLAG_LIVE_BP)) != 0)
+ {
+ // see if we hit a target time
+ if ((m_flags & DEBUG_FLAG_STOP_TIME) != 0 && machine.time() >= m_stoptime)
+ {
+ debug_console_printf(machine, "Stopped at time interval %.1g\n", machine.time().as_double());
+ global->execution_state = EXECUTION_STATE_STOPPED;
+ }
+
+ // check the temp running breakpoint and break if we hit it
+ else if ((m_flags & DEBUG_FLAG_STOP_PC) != 0 && m_stopaddr == curpc)
+ {
+ debug_console_printf(machine, "Stopped at temporary breakpoint %X on CPU '%s'\n", m_stopaddr, m_device.tag());
+ global->execution_state = EXECUTION_STATE_STOPPED;
+ }
+
+ // check for execution breakpoints
+ else if ((m_flags & DEBUG_FLAG_LIVE_BP) != 0)
+ breakpoint_check(curpc);
+ }
+
+ // if we are supposed to halt, do it now
+ if (global->execution_state == EXECUTION_STATE_STOPPED)
+ {
+ bool firststop = true;
+
+ // load comments if we haven't yet
+ if (!global->comments_loaded)
+ {
+ debug_comment_load(m_device.machine());
+ global->comments_loaded = true;
+ }
+
+ // reset any transient state
+ reset_transient_flags(m_device.machine());
+ global->breakcpu = NULL;
+
+ // remember the last visible CPU in the debugger
+ global->visiblecpu = &m_device;
+
+ // update all views
+ machine.debug_view().update_all();
+ debugger_refresh_display(m_device.machine());
+
+ // wait for the debugger; during this time, disable sound output
+ m_device.machine().sound().debugger_mute(true);
+ while (global->execution_state == EXECUTION_STATE_STOPPED)
+ {
+ // flush any pending updates before waiting again
+ machine.debug_view().flush_osd_updates();
+
+ // clear the memory modified flag and wait
+ global->memory_modified = false;
+ if (machine.debug_flags & DEBUG_FLAG_OSD_ENABLED)
+ machine.osd().wait_for_debugger(m_device, firststop);
+ else if (machine.debug_flags & DEBUG_FLAG_ENABLED)
+ debugint_wait_for_debugger(m_device, firststop);
+ firststop = false;
+
+ // if something modified memory, update the screen
+ if (global->memory_modified)
+ {
+ machine.debug_view().update_all(DVT_DISASSEMBLY);
+ debugger_refresh_display(m_device.machine());
+ }
+
+ // check for commands in the source file
+ process_source_file(m_device.machine());
+
+ // if an event got scheduled, resume
+ if (machine.scheduled_event_pending())
+ global->execution_state = EXECUTION_STATE_RUNNING;
+ }
+ m_device.machine().sound().debugger_mute(false);
+
+ // remember the last visible CPU in the debugger
+ global->visiblecpu = &m_device;
+ }
+
+ // handle step out/over on the instruction we are about to execute
+ if ((m_flags & (DEBUG_FLAG_STEPPING_OVER | DEBUG_FLAG_STEPPING_OUT)) != 0 && m_stepaddr == ~0)
+ prepare_for_step_overout(pc());
+
+ // no longer in debugger code
+ global->within_instruction_hook = false;
+}
+
+
+//-------------------------------------------------
+// memory_read_hook - the memory system calls
+// this hook when watchpoints are enabled and a
+// memory read happens
+//-------------------------------------------------
+
+void device_debug::memory_read_hook(address_space &space, offs_t address, UINT64 mem_mask)
+{
+ // check watchpoints
+ watchpoint_check(space, WATCHPOINT_READ, address, 0, mem_mask);
+
+ // check hotspots
+ if (m_hotspots != NULL)
+ hotspot_check(space, address);
+}
+
+
+//-------------------------------------------------
+// memory_write_hook - the memory system calls
+// this hook when watchpoints are enabled and a
+// memory write happens
+//-------------------------------------------------
+
+void device_debug::memory_write_hook(address_space &space, offs_t address, UINT64 data, UINT64 mem_mask)
+{
+ watchpoint_check(space, WATCHPOINT_WRITE, address, data, mem_mask);
+}
+
+
+//-------------------------------------------------
+// set_instruction_hook - set a hook to be
+// called on each instruction for a given device
+//-------------------------------------------------
+
+void device_debug::set_instruction_hook(debug_instruction_hook_func hook)
+{
+ // set the hook and also the CPU's flag for fast knowledge of the hook
+ m_instrhook = hook;
+ if (hook != NULL)
+ m_flags |= DEBUG_FLAG_HOOKED;
+ else
+ m_flags &= ~DEBUG_FLAG_HOOKED;
+}
+
+
+//-------------------------------------------------
+// disassemble - disassemble a line at a given
+// PC on a given device
+//-------------------------------------------------
+
+offs_t device_debug::disassemble(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram) const
+{
+ offs_t result = 0;
+
+ // check for disassembler override
+ if (m_dasm_override != NULL)
+ result = (*m_dasm_override)(m_device, buffer, pc, oprom, opram, 0);
+
+ // if we have a disassembler, run it
+ if (result == 0 && m_disasm != NULL)
+ result = m_disasm->disassemble(buffer, pc, oprom, opram, 0);
+
+ // make sure we get good results
+ assert((result & DASMFLAG_LENGTHMASK) != 0);
+#ifdef MAME_DEBUG
+if (m_memory != NULL && m_disasm != NULL)
+{
+ address_space *space = m_memory->space(AS_PROGRAM);
+ int bytes = space->address_to_byte(result & DASMFLAG_LENGTHMASK);
+ assert(bytes >= m_disasm->min_opcode_bytes());
+ assert(bytes <= m_disasm->max_opcode_bytes());
+ (void) bytes; // appease compiler
+}
+#endif
+
+ return result;
+}
+
+
+//-------------------------------------------------
+// ignore - ignore/observe a given device
+//-------------------------------------------------
+
+void device_debug::ignore(bool ignore)
+{
+ debugcpu_private *global = m_device.machine().debugcpu_data;
+
+ assert(m_exec != NULL);
+
+ if (ignore)
+ m_flags &= ~DEBUG_FLAG_OBSERVING;
+ else
+ m_flags |= DEBUG_FLAG_OBSERVING;
+
+ if (&m_device == global->livecpu && ignore)
+ go_next_device();
+}
+
+
+//-------------------------------------------------
+// single_step - single step the device past the
+// requested number of instructions
+//-------------------------------------------------
+
+void device_debug::single_step(int numsteps)
+{
+ debugcpu_private *global = m_device.machine().debugcpu_data;
+
+ assert(m_exec != NULL);
+
+ m_stepsleft = numsteps;
+ m_stepaddr = ~0;
+ m_flags |= DEBUG_FLAG_STEPPING;
+ global->execution_state = EXECUTION_STATE_RUNNING;
+}
+
+
+//-------------------------------------------------
+// single_step_over - single step the device over
+// the requested number of instructions
+//-------------------------------------------------
+
+void device_debug::single_step_over(int numsteps)
+{
+ debugcpu_private *global = m_device.machine().debugcpu_data;
+
+ assert(m_exec != NULL);
+
+ m_stepsleft = numsteps;
+ m_stepaddr = ~0;
+ m_flags |= DEBUG_FLAG_STEPPING_OVER;
+ global->execution_state = EXECUTION_STATE_RUNNING;
+}
+
+
+//-------------------------------------------------
+// single_step_out - single step the device
+// out of the current function
+//-------------------------------------------------
+
+void device_debug::single_step_out()
+{
+ debugcpu_private *global = m_device.machine().debugcpu_data;
+
+ assert(m_exec != NULL);
+
+ m_stepsleft = 100;
+ m_stepaddr = ~0;
+ m_flags |= DEBUG_FLAG_STEPPING_OUT;
+ global->execution_state = EXECUTION_STATE_RUNNING;
+}
+
+
+//-------------------------------------------------
+// go - execute the device until it hits the given
+// address
+//-------------------------------------------------
+
+void device_debug::go(offs_t targetpc)
+{
+ debugcpu_private *global = m_device.machine().debugcpu_data;
+
+ assert(m_exec != NULL);
+
+ m_stopaddr = targetpc;
+ m_flags |= DEBUG_FLAG_STOP_PC;
+ global->execution_state = EXECUTION_STATE_RUNNING;
+}
+
+
+//-------------------------------------------------
+// go_vblank - execute until the next VBLANK
+//-------------------------------------------------
+
+void device_debug::go_vblank()
+{
+ debugcpu_private *global = m_device.machine().debugcpu_data;
+
+ assert(m_exec != NULL);
+
+ global->vblank_occurred = false;
+ m_flags |= DEBUG_FLAG_STOP_VBLANK;
+ global->execution_state = EXECUTION_STATE_RUNNING;
+}
+
+
+//-------------------------------------------------
+// go_interrupt - execute until the specified
+// interrupt fires on the device
+//-------------------------------------------------
+
+void device_debug::go_interrupt(int irqline)
+{
+ debugcpu_private *global = m_device.machine().debugcpu_data;
+
+ assert(m_exec != NULL);
+
+ m_stopirq = irqline;
+ m_flags |= DEBUG_FLAG_STOP_INTERRUPT;
+ global->execution_state = EXECUTION_STATE_RUNNING;
+}
+
+
+//-------------------------------------------------
+// go_exception - execute until the specified
+// exception fires on the visible CPU
+//-------------------------------------------------
+
+void device_debug::go_exception(int exception)
+{
+ debugcpu_private *global = m_device.machine().debugcpu_data;
+
+ assert(m_exec != NULL);
+
+ m_stopexception = exception;
+ m_flags |= DEBUG_FLAG_STOP_EXCEPTION;
+ global->execution_state = EXECUTION_STATE_RUNNING;
+}
+
+
+//-------------------------------------------------
+// go_milliseconds - execute until the specified
+// delay elapses
+//-------------------------------------------------
+
+void device_debug::go_milliseconds(UINT64 milliseconds)
+{
+ debugcpu_private *global = m_device.machine().debugcpu_data;
+
+ assert(m_exec != NULL);
+
+ m_stoptime = m_device.machine().time() + attotime::from_msec(milliseconds);
+ m_flags |= DEBUG_FLAG_STOP_TIME;
+ global->execution_state = EXECUTION_STATE_RUNNING;
+}
+
+
+//-------------------------------------------------
+// go_next_device - execute until we hit the next
+// device
+//-------------------------------------------------
+
+void device_debug::go_next_device()
+{
+ debugcpu_private *global = m_device.machine().debugcpu_data;
+
+ assert(m_exec != NULL);
+
+ global->m_stop_when_not_device = &m_device;
+ global->execution_state = EXECUTION_STATE_RUNNING;
+}
+
+
+//-------------------------------------------------
+// halt_on_next_instruction - halt in the
+// debugger on the next instruction
+//-------------------------------------------------
+
+void device_debug::halt_on_next_instruction(const char *fmt, ...)
+{
+ debugcpu_private *global = m_device.machine().debugcpu_data;
+ va_list arg;
+
+ assert(m_exec != NULL);
+
+ // if something is pending on this CPU already, ignore this request
+ if (&m_device == global->breakcpu)
+ return;
+
+ // output the message to the console
+ va_start(arg, fmt);
+ debug_console_vprintf(m_device.machine(), fmt, arg);
+ va_end(arg);
+
+ // if we are live, stop now, otherwise note that we want to break there
+ if (&m_device == global->livecpu)
+ {
+ global->execution_state = EXECUTION_STATE_STOPPED;
+ if (global->livecpu != NULL)
+ global->livecpu->debug()->compute_debug_flags();
+ }
+ else
+ global->breakcpu = &m_device;
+}
+
+
+//-------------------------------------------------
+// breakpoint_set - set a new breakpoint,
+// returning its index
+//-------------------------------------------------
+
+int device_debug::breakpoint_set(offs_t address, const char *condition, const char *action)
+{
+ // allocate a new one
+ breakpoint *bp = auto_alloc(m_device.machine(), breakpoint(m_symtable, m_device.machine().debugcpu_data->bpindex++, address, condition, action));
+
+ // hook it into our list
+ bp->m_next = m_bplist;
+ m_bplist = bp;
+
+ // update the flags and return the index
+ breakpoint_update_flags();
+ return bp->m_index;
+}
+
+
+//-------------------------------------------------
+// breakpoint_clear - clear a breakpoint by index,
+// returning true if we found it
+//-------------------------------------------------
+
+bool device_debug::breakpoint_clear(int index)
+{
+ // scan the list to see if we own this breakpoint
+ for (breakpoint **bp = &m_bplist; *bp != NULL; bp = &(*bp)->m_next)
+ if ((*bp)->m_index == index)
+ {
+ breakpoint *deleteme = *bp;
+ *bp = deleteme->m_next;
+ auto_free(m_device.machine(), deleteme);
+ breakpoint_update_flags();
+ return true;
+ }
+
+ // we don't own it, return false
+ return false;
+}
+
+
+//-------------------------------------------------
+// breakpoint_clear_all - clear all breakpoints
+//-------------------------------------------------
+
+void device_debug::breakpoint_clear_all()
+{
+ // clear the head until we run out
+ while (m_bplist != NULL)
+ breakpoint_clear(m_bplist->index());
+}
+
+
+//-------------------------------------------------
+// breakpoint_enable - enable/disable a breakpoint
+// by index, returning true if we found it
+//-------------------------------------------------
+
+bool device_debug::breakpoint_enable(int index, bool enable)
+{
+ // scan the list to see if we own this breakpoint
+ for (breakpoint *bp = m_bplist; bp != NULL; bp = bp->next())
+ if (bp->m_index == index)
+ {
+ bp->m_enabled = enable;
+ breakpoint_update_flags();
+ return true;
+ }
+
+ // we don't own it, return false
+ return false;
+}
+
+
+//-------------------------------------------------
+// breakpoint_enable_all - enable/disable all
+// breakpoints
+//-------------------------------------------------
+
+void device_debug::breakpoint_enable_all(bool enable)
+{
+ // apply the enable to all breakpoints we own
+ for (breakpoint *bp = m_bplist; bp != NULL; bp = bp->next())
+ breakpoint_enable(bp->index(), enable);
+}
+
+
+//-------------------------------------------------
+// watchpoint_set - set a new watchpoint,
+// returning its index
+//-------------------------------------------------
+
+int device_debug::watchpoint_set(address_space &space, int type, offs_t address, offs_t length, const char *condition, const char *action)
+{
+ assert(space.spacenum() < ARRAY_LENGTH(m_wplist));
+
+ // allocate a new one
+ watchpoint *wp = auto_alloc(m_device.machine(), watchpoint(m_symtable, m_device.machine().debugcpu_data->bpindex++, space, type, address, length, condition, action));
+
+ // hook it into our list
+ wp->m_next = m_wplist[space.spacenum()];
+ m_wplist[space.spacenum()] = wp;
+
+ // update the flags and return the index
+ watchpoint_update_flags(wp->m_space);
+ return wp->m_index;
+}
+
+
+//-------------------------------------------------
+// watchpoint_clear - clear a watchpoint by index,
+// returning true if we found it
+//-------------------------------------------------
+
+bool device_debug::watchpoint_clear(int index)
+{
+ // scan the list to see if we own this breakpoint
+ for (address_spacenum spacenum = AS_0; spacenum < ARRAY_LENGTH(m_wplist); spacenum++)
+ for (watchpoint **wp = &m_wplist[spacenum]; *wp != NULL; wp = &(*wp)->m_next)
+ if ((*wp)->m_index == index)
+ {
+ watchpoint *deleteme = *wp;
+ address_space &space = deleteme->m_space;
+ *wp = deleteme->m_next;
+ auto_free(m_device.machine(), deleteme);
+ watchpoint_update_flags(space);
+ return true;
+ }
+
+ // we don't own it, return false
+ return false;
+}
+
+
+//-------------------------------------------------
+// watchpoint_clear_all - clear all watchpoints
+//-------------------------------------------------
+
+void device_debug::watchpoint_clear_all()
+{
+ // clear the head until we run out
+ for (address_spacenum spacenum = AS_0; spacenum < ARRAY_LENGTH(m_wplist); spacenum++)
+ while (m_wplist[spacenum] != NULL)
+ watchpoint_clear(m_wplist[spacenum]->index());
+}
+
+
+//-------------------------------------------------
+// watchpoint_enable - enable/disable a watchpoint
+// by index, returning true if we found it
+//-------------------------------------------------
+
+bool device_debug::watchpoint_enable(int index, bool enable)
+{
+ // scan the list to see if we own this watchpoint
+ for (address_spacenum spacenum = AS_0; spacenum < ARRAY_LENGTH(m_wplist); spacenum++)
+ for (watchpoint *wp = m_wplist[spacenum]; wp != NULL; wp = wp->next())
+ if (wp->m_index == index)
+ {
+ wp->m_enabled = enable;
+ watchpoint_update_flags(wp->m_space);
+ return true;
+ }
+
+ // we don't own it, return false
+ return false;
+}
+
+
+//-------------------------------------------------
+// watchpoint_enable_all - enable/disable all
+// watchpoints
+//-------------------------------------------------
+
+void device_debug::watchpoint_enable_all(bool enable)
+{
+ // apply the enable to all watchpoints we own
+ for (address_spacenum spacenum = AS_0; spacenum < ARRAY_LENGTH(m_wplist); spacenum++)
+ for (watchpoint *wp = m_wplist[spacenum]; wp != NULL; wp = wp->next())
+ watchpoint_enable(wp->index(), enable);
+}
+
+
+//-------------------------------------------------
+// hotspot_track - enable/disable tracking of
+// hotspots
+//-------------------------------------------------
+
+void device_debug::hotspot_track(int numspots, int threshhold)
+{
+ // if we already have tracking enabled, kill it
+ auto_free(m_device.machine(), m_hotspots);
+ m_hotspots = NULL;
+
+ // only start tracking if we have a non-zero count
+ if (numspots > 0)
+ {
+ // allocate memory for hotspots
+ m_hotspots = auto_alloc_array(m_device.machine(), hotspot_entry, numspots);
+ memset(m_hotspots, 0xff, sizeof(*m_hotspots) * numspots);
+
+ // fill in the info
+ m_hotspot_count = numspots;
+ m_hotspot_threshhold = threshhold;
+ }
+
+ // update the watchpoint flags to include us
+ if (m_memory != NULL && m_memory->space(AS_PROGRAM) != NULL)
+ watchpoint_update_flags(*m_memory->space(AS_PROGRAM));
+}
+
+
+//-------------------------------------------------
+// history_pc - return an entry from the PC
+// history
+//-------------------------------------------------
+
+offs_t device_debug::history_pc(int index) const
+{
+ if (index > 0)
+ index = 0;
+ if (index <= -HISTORY_SIZE)
+ index = -HISTORY_SIZE + 1;
+ return m_pc_history[(m_pc_history_index + ARRAY_LENGTH(m_pc_history) - 1 + index) % ARRAY_LENGTH(m_pc_history)];
+}
+
+
+//-------------------------------------------------
+// comment_add - adds a comment to the list at
+// the given address
+//-------------------------------------------------
+
+void device_debug::comment_add(offs_t addr, const char *comment, rgb_t color)
+{
+ // create a new item for the list
+ UINT32 crc = compute_opcode_crc32(addr);
+ dasm_comment *newcomment = auto_alloc(m_device.machine(), dasm_comment(comment, addr, color, crc));
+
+ // figure out where to insert it
+ dasm_comment *prev = NULL;
+ dasm_comment *curr;
+ for (curr = m_comment_list.first(); curr != NULL; prev = curr, curr = curr->next())
+ if (curr->m_address >= addr)
+ break;
+
+ // we could be the new head
+ if (prev == NULL)
+ m_comment_list.prepend(*newcomment);
+
+ // or else we just insert ourselves here
+ else
+ {
+ newcomment->m_next = prev->m_next;
+ prev->m_next = newcomment;
+ }
+
+ // scan forward from here to delete any exact matches
+ for ( ; curr != NULL && curr->m_address == addr; curr = curr->next())
+ if (curr->m_crc == crc)
+ {
+ m_comment_list.remove(*curr);
+ break;
+ }
+
+ // force an update
+ m_comment_change++;
+}
+
+
+//-------------------------------------------------
+// comment_remove - removes a comment at the
+// given address with a matching CRC
+//-------------------------------------------------
+
+bool device_debug::comment_remove(offs_t addr)
+{
+ // scan the list for a match
+ UINT32 crc = compute_opcode_crc32(addr);
+ for (dasm_comment *curr = m_comment_list.first(); curr != NULL; curr = curr->next())
+ {
+ // if we're past the address, we failed
+ if (curr->m_address > addr)
+ break;
+
+ // find an exact match
+ if (curr->m_address == addr && curr->m_crc == crc)
+ {
+ // remove it and force an update
+ m_comment_list.remove(*curr);
+ m_comment_change++;
+ return true;
+ }
+ }
+
+ // failure is an option
+ return false;
+}
+
+
+//-------------------------------------------------
+// comment_text - return the text of a comment
+//-------------------------------------------------
+
+const char *device_debug::comment_text(offs_t addr) const
+{
+ // scan the list for a match
+ UINT32 crc = compute_opcode_crc32(addr);
+ for (dasm_comment *curr = m_comment_list.first(); curr != NULL; curr = curr->next())
+ {
+ // if we're past the address, we failed
+ if (curr->m_address > addr)
+ break;
+
+ // find an exact match
+ if (curr->m_address == addr && curr->m_crc == crc)
+ return curr->m_text;
+ }
+
+ // failure is an option
+ return NULL;
+}
+
+
+//-------------------------------------------------
+// comment_export - export the comments to the
+// given XML data node
+//-------------------------------------------------
+
+bool device_debug::comment_export(xml_data_node &curnode)
+{
+ // iterate through the comments
+ astring crc_buf;
+ for (dasm_comment *curr = m_comment_list.first(); curr != NULL; curr = curr->next())
+ {
+ xml_data_node *datanode = xml_add_child(&curnode, "comment", xml_normalize_string(curr->m_text));
+ if (datanode == NULL)
+ return false;
+ xml_set_attribute_int(datanode, "address", curr->m_address);
+ xml_set_attribute_int(datanode, "color", curr->m_color);
+ crc_buf.printf("%08X", curr->m_crc);
+ xml_set_attribute(datanode, "crc", crc_buf);
+ }
+ return true;
+}
+
+
+//-------------------------------------------------
+// comment_import - import the comments from the
+// given XML data node
+//-------------------------------------------------
+
+bool device_debug::comment_import(xml_data_node &cpunode)
+{
+ // iterate through nodes
+ for (xml_data_node *datanode = xml_get_sibling(cpunode.child, "comment"); datanode; datanode = xml_get_sibling(datanode->next, "comment"))
+ {
+ // extract attributes
+ offs_t address = xml_get_attribute_int(datanode, "address", 0);
+ rgb_t color = xml_get_attribute_int(datanode, "color", 0);
+ UINT32 crc;
+ sscanf(xml_get_attribute_string(datanode, "crc", 0), "%08X", &crc);
+
+ // add the new comment; we assume they were saved ordered
+ m_comment_list.append(*auto_alloc(m_device.machine(), dasm_comment(datanode->value, address, color, crc)));
+ }
+ return true;
+}
+
+
+//-------------------------------------------------
+// comment_dump - logs comments to the error.log
+// at a given address
+//-------------------------------------------------
+
+void device_debug::comment_dump(offs_t addr)
+{
+ // determine the CRC at the given address (if valid)
+ UINT32 crc = (addr == ~0) ? 0 : compute_opcode_crc32(addr);
+
+ // dump everything that matches
+ bool found = false;
+ for (dasm_comment *curr = m_comment_list.first(); curr != NULL; curr = curr->next())
+ if (addr == ~0 || (curr->m_address == addr && curr->m_crc == crc))
+ {
+ found = true;
+ logerror("%08X %08X - %s\n", curr->m_address, curr->m_crc, curr->m_text.cstr());
+ }
+
+ // if nothing found, indicate as much
+ if (!found)
+ logerror("No comment exists for address : 0x%x\n", addr);
+}
+
+
+//-------------------------------------------------
+// compute_opcode_crc32 - determine the CRC of
+// the opcode bytes at the given address
+//-------------------------------------------------
+
+UINT32 device_debug::compute_opcode_crc32(offs_t address) const
+{
+ // no memory interface, just fail
+ if (m_memory == NULL)
+ return 0;
+
+ // no program interface, just fail
+ address_space *space = m_memory->space(AS_PROGRAM);
+ if (space == NULL)
+ return 0;
+
+ // zero out the buffers
+ UINT8 opbuf[64], argbuf[64];
+ memset(opbuf, 0x00, sizeof(opbuf));
+ memset(argbuf, 0x00, sizeof(argbuf));
+
+ // fetch the bytes up to the maximum
+ int maxbytes = m_disasm->max_opcode_bytes();
+ for (int index = 0; index < maxbytes; index++)
+ {
+ opbuf[index] = debug_read_opcode(space, address + index, 1, false);
+ argbuf[index] = debug_read_opcode(space, address + index, 1, true);
+ }
+
+ // disassemble and then convert to bytes
+ char buff[256];
+ int numbytes = disassemble(buff, address & space->logaddrmask(), opbuf, argbuf) & DASMFLAG_LENGTHMASK;
+ numbytes = space->address_to_byte(numbytes);
+
+ // return a CRC of the resulting bytes
+ return crc32(0, argbuf, numbytes);
+}
+
+
+//-------------------------------------------------
+// trace - trace execution of a given device
+//-------------------------------------------------
+
+void device_debug::trace(FILE *file, bool trace_over, const char *action)
+{
+ // delete any existing tracers
+ auto_free(m_device.machine(), m_trace);
+ m_trace = NULL;
+
+ // if we have a new file, make a new tracer
+ if (file != NULL)
+ m_trace = auto_alloc(m_device.machine(), tracer(*this, *file, trace_over, action));
+}
+
+
+//-------------------------------------------------
+// trace_printf - output data into the given
+// device's tracefile, if tracing
+//-------------------------------------------------
+
+void device_debug::trace_printf(const char *fmt, ...)
+{
+ if (m_trace != NULL)
+ {
+ va_list va;
+ va_start(va, fmt);
+ m_trace->vprintf(fmt, va);
+ va_end(va);
+ }
+}
+
+
+//-------------------------------------------------
+// compute_debug_flags - compute the global
+// debug flags for optimal efficiency
+//-------------------------------------------------
+
+void device_debug::compute_debug_flags()
+{
+ running_machine &machine = m_device.machine();
+ debugcpu_private *global = machine.debugcpu_data;
+
+ // clear out global flags by default, keep DEBUG_FLAG_OSD_ENABLED
+ machine.debug_flags &= DEBUG_FLAG_OSD_ENABLED;
+ machine.debug_flags |= DEBUG_FLAG_ENABLED;
+
+ // if we are ignoring this CPU, or if events are pending, we're done
+ if ((m_flags & DEBUG_FLAG_OBSERVING) == 0 || machine.scheduled_event_pending() || machine.save_or_load_pending())
+ return;
+
+ // if we're stopped, keep calling the hook
+ if (global->execution_state == EXECUTION_STATE_STOPPED)
+ machine.debug_flags |= DEBUG_FLAG_CALL_HOOK;
+
+ // if we're tracking history, or we're hooked, or stepping, or stopping at a breakpoint
+ // make sure we call the hook
+ if ((m_flags & (DEBUG_FLAG_HISTORY | DEBUG_FLAG_HOOKED | DEBUG_FLAG_STEPPING_ANY | DEBUG_FLAG_STOP_PC | DEBUG_FLAG_LIVE_BP)) != 0)
+ machine.debug_flags |= DEBUG_FLAG_CALL_HOOK;
+
+ // also call if we are tracing
+ if (m_trace != NULL)
+ machine.debug_flags |= DEBUG_FLAG_CALL_HOOK;
+
+ // if we are stopping at a particular time and that time is within the current timeslice, we need to be called
+ if ((m_flags & DEBUG_FLAG_STOP_TIME) && m_endexectime <= m_stoptime)
+ machine.debug_flags |= DEBUG_FLAG_CALL_HOOK;
+}
+
+
+//-------------------------------------------------
+// prepare_for_step_overout - prepare things for
+// stepping over an instruction
+//-------------------------------------------------
+
+void device_debug::prepare_for_step_overout(offs_t pc)
+{
+ // disassemble the current instruction and get the flags
+ astring dasmbuffer;
+ offs_t dasmresult = dasm_wrapped(dasmbuffer, pc);
+
+ // if flags are supported and it's a call-style opcode, set a temp breakpoint after that instruction
+ if ((dasmresult & DASMFLAG_SUPPORTED) != 0 && (dasmresult & DASMFLAG_STEP_OVER) != 0)
+ {
+ int extraskip = (dasmresult & DASMFLAG_OVERINSTMASK) >> DASMFLAG_OVERINSTSHIFT;
+ pc += dasmresult & DASMFLAG_LENGTHMASK;
+
+ // if we need to skip additional instructions, advance as requested
+ while (extraskip-- > 0)
+ pc += dasm_wrapped(dasmbuffer, pc) & DASMFLAG_LENGTHMASK;
+ m_stepaddr = pc;
+ }
+
+ // if we're stepping out and this isn't a step out instruction, reset the steps until stop to a high number
+ if ((m_flags & DEBUG_FLAG_STEPPING_OUT) != 0)
+ {
+ if ((dasmresult & DASMFLAG_SUPPORTED) != 0 && (dasmresult & DASMFLAG_STEP_OUT) == 0)
+ m_stepsleft = 100;
+ else
+ m_stepsleft = 1;
+ }
+}
+
+
+//-------------------------------------------------
+// breakpoint_update_flags - update the device's
+// breakpoint flags
+//-------------------------------------------------
+
+void device_debug::breakpoint_update_flags()
+{
+ // see if there are any enabled breakpoints
+ m_flags &= ~DEBUG_FLAG_LIVE_BP;
+ for (breakpoint *bp = m_bplist; bp != NULL; bp = bp->m_next)
+ if (bp->m_enabled)
+ {
+ m_flags |= DEBUG_FLAG_LIVE_BP;
+ break;
+ }
+
+ // push the flags out globally
+ debugcpu_private *global = m_device.machine().debugcpu_data;
+ if (global->livecpu != NULL)
+ global->livecpu->debug()->compute_debug_flags();
+}
+
+
+//-------------------------------------------------
+// breakpoint_check - check the breakpoints for
+// a given device
+//-------------------------------------------------
+
+void device_debug::breakpoint_check(offs_t pc)
+{
+ // see if we match
+ for (breakpoint *bp = m_bplist; bp != NULL; bp = bp->m_next)
+ if (bp->hit(pc))
+ {
+ // halt in the debugger by default
+ debugcpu_private *global = m_device.machine().debugcpu_data;
+ global->execution_state = EXECUTION_STATE_STOPPED;
+
+ // if we hit, evaluate the action
+ if (bp->m_action)
+ debug_console_execute_command(m_device.machine(), bp->m_action, 0);
+
+ // print a notification, unless the action made us go again
+ if (global->execution_state == EXECUTION_STATE_STOPPED)
+ debug_console_printf(m_device.machine(), "Stopped at breakpoint %X\n", bp->m_index);
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// watchpoint_update_flags - update the device's
+// watchpoint flags
+//-------------------------------------------------
+
+void device_debug::watchpoint_update_flags(address_space &space)
+{
+ // if hotspots are enabled, turn on all reads
+ bool enableread = false;
+ if (m_hotspots != NULL)
+ enableread = true;
+
+ // see if there are any enabled breakpoints
+ bool enablewrite = false;
+ for (watchpoint *wp = m_wplist[space.spacenum()]; wp != NULL; wp = wp->m_next)
+ if (wp->m_enabled)
+ {
+ if (wp->m_type & WATCHPOINT_READ)
+ enableread = true;
+ if (wp->m_type & WATCHPOINT_WRITE)
+ enablewrite = true;
+ }
+
+ // push the flags out globally
+ space.enable_read_watchpoints(enableread);
+ space.enable_write_watchpoints(enablewrite);
+}
+
+
+//-------------------------------------------------
+// watchpoint_check - check the watchpoints
+// for a given CPU and address space
+//-------------------------------------------------
+
+void device_debug::watchpoint_check(address_space &space, int type, offs_t address, UINT64 value_to_write, UINT64 mem_mask)
+{
+ debugcpu_private *global = space.machine().debugcpu_data;
+
+ // if we're within debugger code, don't stop
+ if (global->within_instruction_hook || global->debugger_access)
+ return;
+ global->within_instruction_hook = true;
+
+ // adjust address, size & value_to_write based on mem_mask.
+ offs_t size = 0;
+ if (mem_mask != 0)
+ {
+ int bus_size = space.data_width() / 8;
+ int address_offset = 0;
+
+ while (address_offset < bus_size && (mem_mask & 0xff) == 0)
+ {
+ address_offset++;
+ value_to_write >>= 8;
+ mem_mask >>= 8;
+ }
+
+ while (mem_mask != 0)
+ {
+ size++;
+ mem_mask >>= 8;
+ }
+
+ if (space.endianness() == ENDIANNESS_LITTLE)
+ address += address_offset;
+ else
+ address += bus_size - size - address_offset;
+ }
+
+ // if we are a write watchpoint, stash the value that will be written
+ global->wpaddr = address;
+ if (type & WATCHPOINT_WRITE)
+ global->wpdata = value_to_write;
+
+ // see if we match
+ for (watchpoint *wp = m_wplist[space.spacenum()]; wp != NULL; wp = wp->m_next)
+ if (wp->hit(type, address, size))
+ {
+ // halt in the debugger by default
+ global->execution_state = EXECUTION_STATE_STOPPED;
+
+ // if we hit, evaluate the action
+ if (wp->m_action)
+ debug_console_execute_command(space.machine(), wp->m_action, 0);
+
+ // print a notification, unless the action made us go again
+ if (global->execution_state == EXECUTION_STATE_STOPPED)
+ {
+ static const char *const sizes[] =
+ {
+ "0bytes", "byte", "word", "3bytes", "dword", "5bytes", "6bytes", "7bytes", "qword"
+ };
+ offs_t pc = (space.device().debug()->m_state != NULL) ? space.device().debug()->m_state->pc() : 0;
+ astring buffer;
+
+ if (type & WATCHPOINT_WRITE)
+ {
+ buffer.printf("Stopped at watchpoint %X writing %s to %08X (PC=%X)", wp->m_index, sizes[size], space.byte_to_address(address), pc);
+ if (value_to_write >> 32)
+ buffer.catprintf(" (data=%X%08X)", (UINT32)(value_to_write >> 32), (UINT32)value_to_write);
+ else
+ buffer.catprintf(" (data=%X)", (UINT32)value_to_write);
+ }
+ else
+ buffer.printf("Stopped at watchpoint %X reading %s from %08X (PC=%X)", wp->m_index, sizes[size], space.byte_to_address(address), pc);
+ debug_console_printf(space.machine(), "%s\n", buffer.cstr());
+ space.device().debug()->compute_debug_flags();
+ }
+ break;
+ }
+
+ global->within_instruction_hook = false;
+}
+
+
+//-------------------------------------------------
+// hotspot_check - check for hotspots on a
+// memory read access
+//-------------------------------------------------
+
+void device_debug::hotspot_check(address_space &space, offs_t address)
+{
+ offs_t curpc = pc();
+
+ // see if we have a match in our list
+ int hotindex;
+ for (hotindex = 0; hotindex < m_hotspot_count; hotindex++)
+ if (m_hotspots[hotindex].m_access == address && m_hotspots[hotindex].m_pc == curpc && m_hotspots[hotindex].m_space == &space)
+ break;
+
+ // if we didn't find any, make a new entry
+ if (hotindex == m_hotspot_count)
+ {
+ // if the bottom of the list is over the threshhold, print it
+ hotspot_entry &spot = m_hotspots[m_hotspot_count - 1];
+ if (spot.m_count > m_hotspot_threshhold)
+ debug_console_printf(space.machine(), "Hotspot @ %s %08X (PC=%08X) hit %d times (fell off bottom)\n", space.name(), spot.m_access, spot.m_pc, spot.m_count);
+
+ // move everything else down and insert this one at the top
+ memmove(&m_hotspots[1], &m_hotspots[0], sizeof(m_hotspots[0]) * (m_hotspot_count - 1));
+ m_hotspots[0].m_access = address;
+ m_hotspots[0].m_pc = curpc;
+ m_hotspots[0].m_space = &space;
+ m_hotspots[0].m_count = 1;
+ }
+
+ // if we did find one, increase the count and move it to the top
+ else
+ {
+ m_hotspots[hotindex].m_count++;
+ if (hotindex != 0)
+ {
+ hotspot_entry temp = m_hotspots[hotindex];
+ memmove(&m_hotspots[1], &m_hotspots[0], sizeof(m_hotspots[0]) * hotindex);
+ m_hotspots[0] = temp;
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// dasm_wrapped - wraps calls to the disassembler
+// by fetching the opcode bytes to a temporary
+// buffer and then disassembling them
+//-------------------------------------------------
+
+UINT32 device_debug::dasm_wrapped(astring &buffer, offs_t pc)
+{
+ assert(m_memory != NULL && m_disasm != NULL);
+
+ // determine the adjusted PC
+ address_space *space = m_memory->space(AS_PROGRAM);
+ offs_t pcbyte = space->address_to_byte(pc) & space->bytemask();
+
+ // fetch the bytes up to the maximum
+ UINT8 opbuf[64], argbuf[64];
+ int maxbytes = max_opcode_bytes();
+ for (int numbytes = 0; numbytes < maxbytes; numbytes++)
+ {
+ opbuf[numbytes] = debug_read_opcode(space, pcbyte + numbytes, 1, false);
+ argbuf[numbytes] = debug_read_opcode(space, pcbyte + numbytes, 1, true);
+ }
+
+ // disassemble to our buffer
+ buffer.expand(200);
+ return disassemble(buffer.text, pc, opbuf, argbuf);
+}
+
+
+//-------------------------------------------------
+// get_current_pc - getter callback for a device's
+// current instruction pointer
+//-------------------------------------------------
+
+UINT64 device_debug::get_current_pc(symbol_table &table, void *ref)
+{
+ device_t *device = reinterpret_cast<device_t *>(table.globalref());
+ return device->debug()->pc();
+}
+
+
+//-------------------------------------------------
+// get_cycles - getter callback for the
+// 'cycles' symbol
+//-------------------------------------------------
+
+UINT64 device_debug::get_cycles(symbol_table &table, void *ref)
+{
+ device_t *device = reinterpret_cast<device_t *>(table.globalref());
+ return device->debug()->m_exec->cycles_remaining();
+}
+
+
+//-------------------------------------------------
+// get_logunmap - getter callback for the logumap
+// symbols
+//-------------------------------------------------
+
+UINT64 device_debug::get_logunmap(symbol_table &table, void *ref)
+{
+ address_space *space = reinterpret_cast<address_space *>(table.globalref());
+ return space->log_unmap();
+}
+
+
+//-------------------------------------------------
+// set_logunmap - setter callback for the logumap
+// symbols
+//-------------------------------------------------
+
+void device_debug::set_logunmap(symbol_table &table, void *ref, UINT64 value)
+{
+ address_space *space = reinterpret_cast<address_space *>(table.globalref());
+ space->set_log_unmap(value ? true : false);
+}
+
+
+//-------------------------------------------------
+// get_state - getter callback for a device's
+// state symbols
+//-------------------------------------------------
+
+UINT64 device_debug::get_state(symbol_table &table, void *ref)
+{
+ device_t *device = reinterpret_cast<device_t *>(table.globalref());
+ return device->debug()->m_state->state(reinterpret_cast<FPTR>(ref));
+}
+
+
+//-------------------------------------------------
+// set_state - setter callback for a device's
+// state symbols
+//-------------------------------------------------
+
+void device_debug::set_state(symbol_table &table, void *ref, UINT64 value)
+{
+ device_t *device = reinterpret_cast<device_t *>(table.globalref());
+ device->debug()->m_state->set_state(reinterpret_cast<FPTR>(ref), value);
+}
+
+
+
+//**************************************************************************
+// DEBUG BREAKPOINT
+//**************************************************************************
+
+//-------------------------------------------------
+// breakpoint - constructor
+//-------------------------------------------------
+
+device_debug::breakpoint::breakpoint(symbol_table &symbols, int index, offs_t address, const char *condition, const char *action)
+ : m_next(NULL),
+ m_index(index),
+ m_enabled(true),
+ m_address(address),
+ m_condition(&symbols, (condition != NULL) ? condition : "1"),
+ m_action((action != NULL) ? action : "")
+{
+}
+
+
+//-------------------------------------------------
+// hit - detect a hit
+//-------------------------------------------------
+
+bool device_debug::breakpoint::hit(offs_t pc)
+{
+ // don't hit if disabled
+ if (!m_enabled)
+ return false;
+
+ // must match our address
+ if (m_address != pc)
+ return false;
+
+ // must satisfy the condition
+ if (!m_condition.is_empty())
+ {
+ try
+ {
+ return (m_condition.execute() != 0);
+ }
+ catch (expression_error &)
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+
+//**************************************************************************
+// DEBUG WATCHPOINT
+//**************************************************************************
+
+//-------------------------------------------------
+// watchpoint - constructor
+//-------------------------------------------------
+
+device_debug::watchpoint::watchpoint(symbol_table &symbols, int index, address_space &space, int type, offs_t address, offs_t length, const char *condition, const char *action)
+ : m_next(NULL),
+ m_space(space),
+ m_index(index),
+ m_enabled(true),
+ m_type(type),
+ m_address(space.address_to_byte(address) & space.bytemask()),
+ m_length(space.address_to_byte(length)),
+ m_condition(&symbols, (condition != NULL) ? condition : "1"),
+ m_action((action != NULL) ? action : "")
+{
+}
+
+
+//-------------------------------------------------
+// hit - detect a hit
+//-------------------------------------------------
+
+bool device_debug::watchpoint::hit(int type, offs_t address, int size)
+{
+ // don't hit if disabled
+ if (!m_enabled)
+ return false;
+
+ // must match the type
+ if ((m_type & type) == 0)
+ return false;
+
+ // must match our address
+ if (address + size <= m_address || address >= m_address + m_length)
+ return false;
+
+ // must satisfy the condition
+ if (!m_condition.is_empty())
+ {
+ try
+ {
+ return (m_condition.execute() != 0);
+ }
+ catch (expression_error &)
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+
+//**************************************************************************
+// TRACER
+//**************************************************************************
+
+//-------------------------------------------------
+// tracer - constructor
+//-------------------------------------------------
+
+device_debug::tracer::tracer(device_debug &debug, FILE &file, bool trace_over, const char *action)
+ : m_debug(debug),
+ m_file(file),
+ m_action((action != NULL) ? action : ""),
+ m_loops(0),
+ m_nextdex(0),
+ m_trace_over(trace_over),
+ m_trace_over_target(~0)
+{
+ memset(m_history, 0, sizeof(m_history));
+}
+
+
+//-------------------------------------------------
+// ~tracer - destructor
+//-------------------------------------------------
+
+device_debug::tracer::~tracer()
+{
+ // make sure we close the file if we can
+ fclose(&m_file);
+}
+
+
+//-------------------------------------------------
+// update - log to the tracefile the data for a
+// given instruction
+//-------------------------------------------------
+
+void device_debug::tracer::update(offs_t pc)
+{
+ // are we in trace over mode and in a subroutine?
+ if (m_trace_over && m_trace_over_target != ~0)
+ {
+ if (m_trace_over_target != pc)
+ return;
+ m_trace_over_target = ~0;
+ }
+
+ // check for a loop condition
+ int count = 0;
+ for (int index = 0; index < ARRAY_LENGTH(m_history); index++)
+ if (m_history[index] == pc)
+ count++;
+
+ // if more than 1 hit, just up the loop count and get out
+ if (count > 1)
+ {
+ m_loops++;
+ return;
+ }
+
+ // if we just finished looping, indicate as much
+ if (m_loops != 0)
+ fprintf(&m_file, "\n (loops for %d instructions)\n\n", m_loops);
+ m_loops = 0;
+
+ // execute any trace actions first
+ if (m_action)
+ debug_console_execute_command(m_debug.m_device.machine(), m_action, 0);
+
+ // print the address
+ astring buffer;
+ int logaddrchars = m_debug.logaddrchars();
+ buffer.printf("%0*X: ", logaddrchars, pc);
+
+ // print the disassembly
+ astring dasm;
+ offs_t dasmresult = m_debug.dasm_wrapped(dasm, pc);
+ buffer.cat(dasm);
+
+ // output the result
+ fprintf(&m_file, "%s\n", buffer.cstr());
+
+ // do we need to step the trace over this instruction?
+ if (m_trace_over && (dasmresult & DASMFLAG_SUPPORTED) != 0 && (dasmresult & DASMFLAG_STEP_OVER) != 0)
+ {
+ int extraskip = (dasmresult & DASMFLAG_OVERINSTMASK) >> DASMFLAG_OVERINSTSHIFT;
+ offs_t trace_over_target = pc + (dasmresult & DASMFLAG_LENGTHMASK);
+
+ // if we need to skip additional instructions, advance as requested
+ while (extraskip-- > 0)
+ trace_over_target += m_debug.dasm_wrapped(dasm, trace_over_target) & DASMFLAG_LENGTHMASK;
+
+ m_trace_over_target = trace_over_target;
+ }
+
+ // log this PC
+ m_nextdex = (m_nextdex + 1) % TRACE_LOOPS;
+ m_history[m_nextdex] = pc;
+}
+
+
+//-------------------------------------------------
+// vprintf - generic print to the trace file
+//-------------------------------------------------
+
+void device_debug::tracer::vprintf(const char *format, va_list va)
+{
+ // pass through to the file
+ vfprintf(&m_file, format, va);
+}
+
+
+//-------------------------------------------------
+// flush - flush any pending changes to the trace
+// file
+//-------------------------------------------------
+
+void device_debug::tracer::flush()
+{
+ fflush(&m_file);
+}
+
+
+//-------------------------------------------------
+// dasm_comment - constructor
+//-------------------------------------------------
+
+device_debug::dasm_comment::dasm_comment(const char *text, offs_t address, rgb_t color, UINT32 crc)
+ : m_next(NULL),
+ m_address(address),
+ m_color(color),
+ m_crc(crc),
+ m_text(text)
+{
+}