summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Aaron Giles <aaron@aarongiles.com>2010-07-06 16:12:53 +0000
committer Aaron Giles <aaron@aarongiles.com>2010-07-06 16:12:53 +0000
commitf7ce43f8fb08bf472451292f4dcba16f20da06b4 (patch)
tree001ac0d2f0825d61048258e1e1dcafbda5923f86
parent16f14bc5f9192c523e7f1d7bb47e77cb8a2e82ee (diff)
Cleanup debugger interface some more.
-rw-r--r--src/emu/debug/debugcmd.c10
-rw-r--r--src/emu/debug/debugcpu.c2876
-rw-r--r--src/emu/debug/debugcpu.h449
-rw-r--r--src/emu/debug/dvdisasm.c2
-rw-r--r--src/emu/ui.c14
-rw-r--r--src/osd/sdl/debugosx.m10
-rw-r--r--src/osd/sdl/debugwin.c2
-rw-r--r--src/osd/windows/debugwin.c2
8 files changed, 1741 insertions, 1624 deletions
diff --git a/src/emu/debug/debugcmd.c b/src/emu/debug/debugcmd.c
index a1365b5629e..a21a67bca4c 100644
--- a/src/emu/debug/debugcmd.c
+++ b/src/emu/debug/debugcmd.c
@@ -1280,7 +1280,7 @@ static void execute_bplist(running_machine *machine, int ref, int params, const
debug_console_printf(machine, "Device '%s' breakpoints:\n", device->tag());
/* loop over the breakpoints */
- for (debug_cpu_breakpoint *bp = device->debug()->breakpoint_first(); bp != NULL; bp = bp->next())
+ for (device_debug::breakpoint *bp = device->debug()->breakpoint_first(); bp != NULL; bp = bp->next())
{
buffer.printf("%c%4X @ %s", bp->enabled() ? ' ' : 'D', bp->index(), core_i64_hex_format(bp->address(), device->debug()->logaddrchars()));
if (bp->condition() != NULL)
@@ -1441,7 +1441,7 @@ static void execute_wplist(running_machine *machine, int ref, int params, const
debug_console_printf(machine, "Device '%s' %s space watchpoints:\n", device->tag(), device->debug()->watchpoint_first(spacenum)->space().name);
/* loop over the watchpoints */
- for (debug_cpu_watchpoint *wp = device->debug()->watchpoint_first(spacenum); wp != NULL; wp = wp->next())
+ for (device_debug::watchpoint *wp = device->debug()->watchpoint_first(spacenum); wp != NULL; wp = wp->next())
{
buffer.printf("%c%4X @ %s-%s %s", wp->enabled() ? ' ' : 'D', wp->index(),
core_i64_hex_format(memory_byte_to_address(&wp->space(), wp->address()), wp->space().addrchars),
@@ -2377,13 +2377,13 @@ static void execute_history(running_machine *machine, int ref, int params, const
if (!debug_command_parameter_cpu_space(machine, (params > 0) ? param[0] : NULL, ADDRESS_SPACE_PROGRAM, &space))
return;
- UINT64 count = DEBUG_HISTORY_SIZE;
+ UINT64 count = device_debug::HISTORY_SIZE;
if (!debug_command_parameter_number(machine, param[1], &count))
return;
/* further validation */
- if (count > DEBUG_HISTORY_SIZE)
- count = DEBUG_HISTORY_SIZE;
+ if (count > device_debug::HISTORY_SIZE)
+ count = device_debug::HISTORY_SIZE;
device_debug *debug = space->cpu->debug();
diff --git a/src/emu/debug/debugcpu.c b/src/emu/debug/debugcpu.c
index 553b07cde8a..b04a8b2fc72 100644
--- a/src/emu/debug/debugcpu.c
+++ b/src/emu/debug/debugcpu.c
@@ -4,16 +4,38 @@
Debugger CPU/memory interface engine.
- Copyright Nicola Salmoria and the MAME Team.
- Visit http://mamedev.org for licensing and usage restrictions.
+****************************************************************************
+
+ 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.
-**********************************************************************
-
- Future work:
-
- - enable history to be enabled/disabled to improve performance
-
-*********************************************************************/
+***************************************************************************/
#include "emu.h"
#include "osdepend.h"
@@ -106,12 +128,6 @@ static void set_tempvar(void *globalref, void *ref, UINT64 value);
static UINT64 get_beamx(void *globalref, void *ref);
static UINT64 get_beamy(void *globalref, void *ref);
static UINT64 get_frame(void *globalref, void *ref);
-static UINT64 get_current_pc(void *globalref, void *ref);
-static UINT64 get_cycles(void *globalref, void *ref);
-static UINT64 get_logunmap(void *globalref, void *ref);
-static void set_logunmap(void *globalref, void *ref, UINT64 value);
-static UINT64 get_cpu_reg(void *globalref, void *ref);
-static void set_cpu_reg(void *globalref, void *ref, UINT64 value);
@@ -128,107 +144,6 @@ const express_callbacks debug_expression_callbacks =
-//**************************************************************************
-// DEVICE DEBUG
-//**************************************************************************
-
-//-------------------------------------------------
-// device_debug - constructor
-//-------------------------------------------------
-
-device_debug::device_debug(device_t &device, symbol_table *globalsyms)
- : m_device(device),
- m_exec(NULL),
- m_memory(NULL),
- m_state(NULL),
- m_disasm(NULL),
- m_symtable(symtable_alloc(globalsyms, (void *)&device)),
- m_flags(0),
- 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_bplist(NULL),
- m_hotspots(NULL),
- m_pc_history_index(0),
- m_hotspot_count(0),
- m_hotspot_threshhold(0),
- m_dasm_override(NULL),
- m_instrhook(NULL),
- m_comments(NULL)
-{
- 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 */
- symtable_add_register(m_symtable, "cycles", NULL, get_cycles, NULL);
-
- if (m_memory != NULL)
- {
- if (m_memory->space(AS_PROGRAM) != NULL)
- symtable_add_register(m_symtable, "logunmap", (void *)m_memory->space(AS_PROGRAM), get_logunmap, set_logunmap);
- if (m_memory->space(AS_DATA) != NULL)
- symtable_add_register(m_symtable, "logunmapd", (void *)m_memory->space(AS_DATA), get_logunmap, set_logunmap);
- if (m_memory->space(AS_IO) != NULL)
- symtable_add_register(m_symtable, "logunmapi", (void *)m_memory->space(AS_IO), get_logunmap, set_logunmap);
- }
-
- /* add all registers into it */
- device_state_interface *stateintf;
- if (device.interface(stateintf))
- {
- astring tempstr;
- for (const device_state_entry *entry = stateintf->state_first(); entry != NULL; entry = entry->next())
- symtable_add_register(m_symtable, tempstr.cpy(entry->symbol()).tolower(), (void *)(FPTR)entry->index(), get_cpu_reg, set_cpu_reg);
- }
- }
-
- // 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 && symtable_find(m_symtable, "curpc") == NULL)
- symtable_add_register(m_symtable, "curpc", NULL, get_current_pc, 0);
- }
-}
-
-
-//-------------------------------------------------
-// ~device_debug - constructor
-//-------------------------------------------------
-
-device_debug::~device_debug()
-{
- /* close any tracefiles */
- if (m_trace.file != NULL)
- fclose(m_trace.file);
-
- /* free the symbol table */
- if (m_symtable != NULL)
- symtable_free(m_symtable);
-
- // free breakpoints and watchpoints
- breakpoint_clear_all();
- watchpoint_clear_all();
-}
-
-
/***************************************************************************
INITIALIZATION AND CLEANUP
***************************************************************************/
@@ -417,1214 +332,6 @@ int debug_cpu_translate(const address_space *space, int intention, offs_t *addre
}
-/*-------------------------------------------------
- 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)
-{
- 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)
-{
- const address_space *space = m_memory->space(AS_PROGRAM);
- int bytes = memory_address_to_byte(space, result & DASMFLAG_LENGTHMASK);
- assert(bytes >= m_disasm->min_opcode_bytes());
- assert(bytes <= m_disasm->max_opcode_bytes());
- (void) bytes; /* appease compiler */
-}
-#endif
-
- return result;
-}
-
-
-
-/***************************************************************************
- CORE DEBUGGER HOOKS
-***************************************************************************/
-
-//-------------------------------------------------
-// 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;
-
- // 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->m_debug_view->update_all();
- m_device.machine->m_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 */
- else 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);
-
- /* if we're stopping on a context switch, handle it now */
- if (m_flags & DEBUG_FLAG_STOP_CONTEXT)
- {
- global->execution_state = EXECUTION_STATE_STOPPED;
- reset_transient_flags(*m_device.machine);
- }
-
- /* 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)
-{
- debugcpu_private *global = m_device.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++ % DEBUG_HISTORY_SIZE] = curpc;
-
- /* are we tracing? */
- if (m_flags & DEBUG_FLAG_TRACING_ANY)
- perform_trace();
-
- /* 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))
- {
- m_device.machine->m_debug_view->update_all();
- m_device.machine->m_debug_view->flush_osd_updates();
- debugger_refresh_display(m_device.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 && attotime_compare(timer_get_time(m_device.machine), m_stoptime) >= 0)
- {
- debug_console_printf(m_device.machine, "Stopped at time interval %.1g\n", attotime_to_double(timer_get_time(m_device.machine)));
- 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(m_device.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)
- {
- int firststop = 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 */
- m_device.machine->m_debug_view->update_all();
- debugger_refresh_display(m_device.machine);
-
- /* wait for the debugger; during this time, disable sound output */
- sound_mute(m_device.machine, TRUE);
- while (global->execution_state == EXECUTION_STATE_STOPPED)
- {
- /* flush any pending updates before waiting again */
- m_device.machine->m_debug_view->flush_osd_updates();
-
- /* clear the memory modified flag and wait */
- global->memory_modified = FALSE;
- if (m_device.machine->debug_flags & DEBUG_FLAG_OSD_ENABLED)
- osd_wait_for_debugger(&m_device, firststop);
- else if (m_device.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)
- {
- m_device.machine->m_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 (m_device.machine->scheduled_event_pending())
- global->execution_state = EXECUTION_STATE_RUNNING;
- }
- sound_mute(m_device.machine, 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();
-
- /* 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(const 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(const address_space &space, offs_t address, UINT64 data, UINT64 mem_mask)
-{
- watchpoint_check(space, WATCHPOINT_WRITE, address, data, mem_mask);
-}
-
-
-
-/***************************************************************************
- EXECUTION CONTROL
-***************************************************************************/
-
-//-------------------------------------------------
-// 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;
-}
-
-
-//-------------------------------------------------
-// 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 = attotime_add(timer_get_time(m_device.machine), ATTOTIME_IN_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);
-
- m_flags |= DEBUG_FLAG_STOP_CONTEXT;
- global->execution_state = EXECUTION_STATE_RUNNING;
-}
-
-
-
-/***************************************************************************
- BREAKPOINTS
-***************************************************************************/
-
-//-------------------------------------------------
-// breakpoint_set - set a new breakpoint,
-// returning its index
-//-------------------------------------------------
-
-int device_debug::breakpoint_set(offs_t address, parsed_expression *condition, const char *action)
-{
- // allocate a new one
- debug_cpu_breakpoint *bp = auto_alloc(m_device.machine, debug_cpu_breakpoint(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 (debug_cpu_breakpoint **bp = &m_bplist; *bp != NULL; bp = &(*bp)->m_next)
- if ((*bp)->m_index == index)
- {
- debug_cpu_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 (debug_cpu_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 (debug_cpu_breakpoint *bp = m_bplist; bp != NULL; bp = bp->next())
- breakpoint_enable(bp->index(), enable);
-}
-
-
-
-/***************************************************************************
- WATCHPOINTS
-***************************************************************************/
-
-/*-------------------------------------------------
- watchpoint_set - set a new watchpoint,
- returning its index
--------------------------------------------------*/
-
-int device_debug::watchpoint_set(const address_space &space, int type, offs_t address, offs_t length, parsed_expression *condition, const char *action)
-{
- assert(space.spacenum < ARRAY_LENGTH(m_wplist));
-
- // allocate a new one
- debug_cpu_watchpoint *wp = auto_alloc(m_device.machine, debug_cpu_watchpoint(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 (int spacenum = 0; spacenum < ARRAY_LENGTH(m_wplist); spacenum++)
- for (debug_cpu_watchpoint **wp = &m_wplist[spacenum]; *wp != NULL; wp = &(*wp)->m_next)
- if ((*wp)->m_index == index)
- {
- debug_cpu_watchpoint *deleteme = *wp;
- const 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 (int spacenum = 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 (int spacenum = 0; spacenum < ARRAY_LENGTH(m_wplist); spacenum++)
- for (debug_cpu_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 (int spacenum = 0; spacenum < ARRAY_LENGTH(m_wplist); spacenum++)
- for (debug_cpu_watchpoint *wp = m_wplist[spacenum]; wp != NULL; wp = wp->next())
- watchpoint_enable(wp->index(), enable);
-}
-
-
-
-/***************************************************************************
- MISC DEBUGGER FUNCTIONS
-***************************************************************************/
-
-offs_t device_debug::history_pc(int index) const
-{
- if (index > 0)
- index = 0;
- if (index <= -DEBUG_HISTORY_SIZE)
- index = -DEBUG_HISTORY_SIZE + 1;
- return m_pc_history[(m_pc_history_index + ARRAY_LENGTH(m_pc_history) - 1 + index) % ARRAY_LENGTH(m_pc_history)];
-}
-
-
-/*-------------------------------------------------
- trace - trace execution of a given device
--------------------------------------------------*/
-
-void device_debug::trace(FILE *file, bool trace_over, const char *action)
-{
- /* close existing files and delete expressions */
- if (m_trace.file != NULL)
- fclose(m_trace.file);
- m_trace.file = NULL;
-
- /* open any new files */
- m_trace.file = file;
- m_trace.action.cpy((action != NULL) ? action : "");
- m_trace.trace_over_target = ~0;
-
- /* update flags */
- if (m_trace.file != NULL)
- m_flags |= trace_over ? DEBUG_FLAG_TRACING_OVER : DEBUG_FLAG_TRACING;
- else
- m_flags &= ~DEBUG_FLAG_TRACING_ANY;
-}
-
-
-/*-------------------------------------------------
- trace_printf - output data into the given
- device's tracefile, if tracing
--------------------------------------------------*/
-
-void device_debug::trace_printf(const char *fmt, ...)
-{
- va_list va;
-
- if (m_trace.file)
- {
- va_start(va, fmt);
- vfprintf(m_trace.file, fmt, va);
- va_end(va);
- }
-}
-
-
-/*-------------------------------------------------
- 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;
-}
-
-
-/*-------------------------------------------------
- hotspot_track - enable/disable tracking of
- hotspots
--------------------------------------------------*/
-
-void device_debug::hotspot_track(int numspots, int threshhold)
-{
- /* if we already have tracking devdebug, kill it */
- if (m_hotspots != NULL)
- 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, debug_hotspot_entry, numspots);
- memset(m_hotspots, 0xff, sizeof(*m_hotspots) * numspots);
-
- /* fill in the devdebug */
- m_hotspot_count = numspots;
- m_hotspot_threshhold = threshhold;
- }
-
- if (m_memory != NULL && m_memory->space(AS_PROGRAM) != NULL)
- watchpoint_update_flags(*m_memory->space(AS_PROGRAM));
-}
-
-
-/*-------------------------------------------------
- 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;
-
- /* many of our states require us to be called on each instruction */
- if (global->execution_state == EXECUTION_STATE_STOPPED)
- machine->debug_flags |= DEBUG_FLAG_CALL_HOOK;
- if ((m_flags & (DEBUG_FLAG_HISTORY | DEBUG_FLAG_TRACING_ANY | DEBUG_FLAG_HOOKED |
- DEBUG_FLAG_STEPPING_ANY | DEBUG_FLAG_STOP_PC | DEBUG_FLAG_LIVE_BP)) != 0)
- 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) && attotime_compare(m_endexectime, m_stoptime) <= 0)
- machine->debug_flags |= DEBUG_FLAG_CALL_HOOK;
-}
-
-
-/*-------------------------------------------------
- perform_trace - log to the tracefile the
- data for a given instruction
--------------------------------------------------*/
-
-void device_debug::perform_trace()
-{
- offs_t pc = (m_state != NULL) ? m_state->pc() : 0;
-
- /* are we in trace over mode and in a subroutine? */
- if ((m_flags & DEBUG_FLAG_TRACING_OVER) != 0 && m_trace.trace_over_target != ~0)
- {
- if (m_trace.trace_over_target != pc)
- return;
- m_trace.trace_over_target = ~0;
- }
-
- /* check for a loop condition */
- int count = 0;
- for (int index = 0; index < TRACE_LOOPS; index++)
- if (m_trace.history[index] == pc)
- count++;
-
- /* if no more than 1 hit, process normally */
- if (count <= 1)
- {
- int logaddrchars = (m_memory != NULL && m_memory->space(AS_PROGRAM) != NULL) ? m_memory->space(AS_PROGRAM)->logaddrchars : 8;
-
- /* if we just finished looping, indicate as much */
- if (m_trace.loops != 0)
- fprintf(m_trace.file, "\n (loops for %d instructions)\n\n", m_trace.loops);
- m_trace.loops = 0;
-
- /* execute any trace actions first */
- if (m_trace.action != NULL)
- debug_console_execute_command(m_device.machine, m_trace.action, 0);
-
- /* print the address */
- astring buffer;
- buffer.printf("%0*X: ", logaddrchars, pc);
-
- /* print the disassembly */
- astring dasm;
- offs_t dasmresult = dasm_wrapped(dasm, pc);
- buffer.cat(dasm);
-
- /* output the result */
- fprintf(m_trace.file, "%s\n", buffer.cstr());
-
- /* do we need to step the trace over this instruction? */
- if ((m_flags & DEBUG_FLAG_TRACING_OVER) != 0 && (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 += dasm_wrapped(dasm, trace_over_target) & DASMFLAG_LENGTHMASK;
-
- m_trace.trace_over_target = trace_over_target;
- }
-
- /* log this PC */
- m_trace.nextdex = (m_trace.nextdex + 1) % TRACE_LOOPS;
- m_trace.history[m_trace.nextdex] = pc;
- }
-
- /* else just count the loop */
- else
- m_trace.loops++;
-}
-
-
-/*-------------------------------------------------
- prepare_for_step_overout - prepare things for
- stepping over an instruction
--------------------------------------------------*/
-
-void device_debug::prepare_for_step_overout()
-{
- offs_t pc = (m_state != NULL) ? m_state->pc() : 0;
-
- /* 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 (debug_cpu_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 CPU
--------------------------------------------------*/
-
-void device_debug::breakpoint_check(offs_t pc)
-{
- /* see if we match */
- for (debug_cpu_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.len() != 0)
- 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 CPU's
- watchpoint flags
--------------------------------------------------*/
-
-void device_debug::watchpoint_update_flags(const address_space &space)
-{
- bool enablewrite = false;
- bool enableread = false;
-
- /* if hotspots are enabled, turn on all reads */
- if (m_hotspots != NULL)
- enableread = true;
-
- /* see if there are any enabled breakpoints */
- for (debug_cpu_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 */
- memory_enable_read_watchpoints(&space, enableread);
- memory_enable_write_watchpoints(&space, enablewrite);
-}
-
-
-/*-------------------------------------------------
- watchpoint_check - check the watchpoints
- for a given CPU and address space
--------------------------------------------------*/
-
-void device_debug::watchpoint_check(const address_space &space, int type, offs_t address, UINT64 value_to_write, UINT64 mem_mask)
-{
- debugcpu_private *global = space.machine->debugcpu_data;
- offs_t size = 0;
-
- /* 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. */
- if (mem_mask != 0)
- {
- int bus_size = space.dbits / 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 (debug_cpu_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 != NULL)
- 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.cpu->debug()->m_state != NULL) ? space.cpu->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], memory_byte_to_address(&space, 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], memory_byte_to_address(&space, address), pc);
- debug_console_printf(space.machine, "%s\n", buffer.cstr());
- space.cpu->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(const address_space &space, offs_t address)
-{
- assert(m_state != NULL);
-
- offs_t pc = m_state->pc();
-
- /* see if we have a match in our list */
- int hotindex;
- for (hotindex = 0; hotindex < m_hotspot_count; hotindex++)
- if (m_hotspots[hotindex].access == address && m_hotspots[hotindex].pc == pc && m_hotspots[hotindex].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 */
- debug_hotspot_entry *spot = &m_hotspots[m_hotspot_count - 1];
- if (spot->count > m_hotspot_threshhold)
- debug_console_printf(space.machine, "Hotspot @ %s %08X (PC=%08X) hit %d times (fell off bottom)\n", space.name, spot->access, spot->pc, spot->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].access = address;
- m_hotspots[0].pc = pc;
- m_hotspots[0].space = &space;
- m_hotspots[0].count = 1;
- }
-
- /* if we did find one, increase the count and move it to the top */
- else
- {
- m_hotspots[hotindex].count++;
- if (hotindex != 0)
- {
- debug_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);
-
- const address_space *space = m_memory->space(AS_PROGRAM);
- int maxbytes = m_disasm->max_opcode_bytes();
-
- /* fetch the bytes up to the maximum */
- offs_t pcbyte = memory_address_to_byte(space, pc) & space->bytemask;
- UINT8 opbuf[64], argbuf[64];
- 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);
- }
-
- buffer.expand(200);
- return disassemble(buffer, pc, opbuf, argbuf);
-}
-
-
-
/***************************************************************************
DEBUGGER MEMORY ACCESSORS
***************************************************************************/
@@ -2735,6 +1442,61 @@ static EXPRERR expression_validate(void *param, const char *name, int space)
***************************************************************************/
/*-------------------------------------------------
+ get_beamx - get beam horizontal position
+-------------------------------------------------*/
+
+static UINT64 get_beamx(void *globalref, 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(void *globalref, 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(void *globalref, void *ref)
+{
+ screen_device *screen = reinterpret_cast<screen_device *>(ref);
+ return (screen != NULL) ? screen->frame_number() : 0;
+}
+
+
+/*-------------------------------------------------
+ get_tempvar - getter callback for the
+ 'tempX' symbols
+-------------------------------------------------*/
+
+static UINT64 get_tempvar(void *globalref, void *ref)
+{
+ return *(UINT64 *)ref;
+}
+
+
+/*-------------------------------------------------
+ set_tempvar - setter callback for the
+ 'tempX' symbols
+-------------------------------------------------*/
+
+static void set_tempvar(void *globalref, void *ref, UINT64 value)
+{
+ *(UINT64 *)ref = value;
+}
+
+
+/*-------------------------------------------------
get_wpaddr - getter callback for the
'wpaddr' symbol
-------------------------------------------------*/
@@ -2780,136 +1542,1292 @@ static UINT64 get_cpunum(void *globalref, void *ref)
}
-/*-------------------------------------------------
- get_tempvar - getter callback for the
- 'tempX' symbols
--------------------------------------------------*/
-static UINT64 get_tempvar(void *globalref, void *ref)
+//**************************************************************************
+// DEVICE DEBUG
+//**************************************************************************
+
+//-------------------------------------------------
+// device_debug - constructor
+//-------------------------------------------------
+
+device_debug::device_debug(device_t &device, symbol_table *globalsyms)
+ : m_device(device),
+ m_exec(NULL),
+ m_memory(NULL),
+ m_state(NULL),
+ m_disasm(NULL),
+ m_flags(0),
+ m_symtable(symtable_alloc(globalsyms, (void *)&device)),
+ 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),
+ m_comments(NULL)
{
- return *(UINT64 *)ref;
+ 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)
+ symtable_add_register(m_symtable, "cycles", NULL, get_cycles, NULL);
+
+ // add entries to enable/disable unmap reporting for each space
+ if (m_memory != NULL)
+ {
+ if (m_memory->space(AS_PROGRAM) != NULL)
+ symtable_add_register(m_symtable, "logunmap", (void *)m_memory->space(AS_PROGRAM), get_logunmap, set_logunmap);
+ if (m_memory->space(AS_DATA) != NULL)
+ symtable_add_register(m_symtable, "logunmapd", (void *)m_memory->space(AS_DATA), get_logunmap, set_logunmap);
+ if (m_memory->space(AS_IO) != NULL)
+ symtable_add_register(m_symtable, "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())
+ symtable_add_register(m_symtable, tempstr.cpy(entry->symbol()).tolower(), (void *)(FPTR)entry->index(), get_cpu_reg, 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 && symtable_find(m_symtable, "curpc") == NULL)
+ symtable_add_register(m_symtable, "curpc", NULL, get_current_pc, 0);
+ }
}
-/*-------------------------------------------------
- set_tempvar - setter callback for the
- 'tempX' symbols
--------------------------------------------------*/
+//-------------------------------------------------
+// ~device_debug - constructor
+//-------------------------------------------------
-static void set_tempvar(void *globalref, void *ref, UINT64 value)
+device_debug::~device_debug()
{
- *(UINT64 *)ref = value;
+ // free the symbol table
+ if (m_symtable != NULL)
+ symtable_free(m_symtable);
+
+ // free breakpoints and watchpoints
+ breakpoint_clear_all();
+ watchpoint_clear_all();
}
-/*-------------------------------------------------
- get_beamx - get beam horizontal position
--------------------------------------------------*/
+//-------------------------------------------------
+// start_hook - the scheduler calls this hook
+// before beginning execution for the given device
+//-------------------------------------------------
-static UINT64 get_beamx(void *globalref, void *ref)
+void device_debug::start_hook(attotime endtime)
{
- screen_device *screen = reinterpret_cast<screen_device *>(ref);
- return (screen != NULL) ? screen->hpos() : 0;
+ 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;
+
+ // 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->m_debug_view->update_all();
+ m_device.machine->m_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
+ else 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();
}
-/*-------------------------------------------------
- get_beamy - get beam vertical position
--------------------------------------------------*/
+//-------------------------------------------------
+// stop_hook - the scheduler calls this hook when
+// ending execution for the given device
+//-------------------------------------------------
-static UINT64 get_beamy(void *globalref, void *ref)
+void device_debug::stop_hook()
{
- screen_device *screen = reinterpret_cast<screen_device *>(ref);
- return (screen != NULL) ? screen->vpos() : 0;
+ debugcpu_private *global = m_device.machine->debugcpu_data;
+
+ assert(global->livecpu == &m_device);
+
+ // if we're stopping on a context switch, handle it now
+ if (m_flags & DEBUG_FLAG_STOP_CONTEXT)
+ {
+ global->execution_state = EXECUTION_STATE_STOPPED;
+ reset_transient_flags(*m_device.machine);
+ }
+
+ // clear the live CPU
+ global->livecpu = NULL;
}
-/*-------------------------------------------------
- get_frame - get current frame number
--------------------------------------------------*/
+//-------------------------------------------------
+// interrupt_hook - called when an interrupt is
+// acknowledged
+//-------------------------------------------------
-static UINT64 get_frame(void *globalref, void *ref)
+void device_debug::interrupt_hook(int irqline)
{
- screen_device *screen = reinterpret_cast<screen_device *>(ref);
- return (screen != NULL) ? screen->frame_number() : 0;
+ 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();
+ }
}
-/*-------------------------------------------------
- get_current_pc - getter callback for a CPU's
- current instruction pointer
--------------------------------------------------*/
+//-------------------------------------------------
+// exception_hook - called when an exception is
+// generated
+//-------------------------------------------------
-static UINT64 get_current_pc(void *globalref, void *ref)
+void device_debug::exception_hook(int exception)
{
- cpu_device *device = (cpu_device *)globalref;
- return cpu_get_pc(device);
+ 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();
+ }
}
-/*-------------------------------------------------
- get_cycles - getter callback for the
- 'cycles' symbol
--------------------------------------------------*/
+//-------------------------------------------------
+// instruction_hook - called by the CPU cores
+// before executing each instruction
+//-------------------------------------------------
-static UINT64 get_cycles(void *globalref, void *ref)
+void device_debug::instruction_hook(offs_t curpc)
{
- cpu_device *device = (cpu_device *)globalref;
- return device->cycles_remaining();
+ debugcpu_private *global = m_device.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))
+ {
+ m_device.machine->m_debug_view->update_all();
+ m_device.machine->m_debug_view->flush_osd_updates();
+ debugger_refresh_display(m_device.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 && attotime_compare(timer_get_time(m_device.machine), m_stoptime) >= 0)
+ {
+ debug_console_printf(m_device.machine, "Stopped at time interval %.1g\n", attotime_to_double(timer_get_time(m_device.machine)));
+ 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(m_device.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)
+ {
+ int firststop = 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
+ m_device.machine->m_debug_view->update_all();
+ debugger_refresh_display(m_device.machine);
+
+ // wait for the debugger; during this time, disable sound output
+ sound_mute(m_device.machine, TRUE);
+ while (global->execution_state == EXECUTION_STATE_STOPPED)
+ {
+ // flush any pending updates before waiting again
+ m_device.machine->m_debug_view->flush_osd_updates();
+
+ // clear the memory modified flag and wait
+ global->memory_modified = FALSE;
+ if (m_device.machine->debug_flags & DEBUG_FLAG_OSD_ENABLED)
+ osd_wait_for_debugger(&m_device, firststop);
+ else if (m_device.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)
+ {
+ m_device.machine->m_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 (m_device.machine->scheduled_event_pending())
+ global->execution_state = EXECUTION_STATE_RUNNING;
+ }
+ sound_mute(m_device.machine, 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;
}
-/*-------------------------------------------------
- get_logunmap - getter callback for the logumap
- symbols
--------------------------------------------------*/
+//-------------------------------------------------
+// memory_read_hook - the memory system calls
+// this hook when watchpoints are enabled and a
+// memory read happens
+//-------------------------------------------------
-static UINT64 get_logunmap(void *globalref, void *ref)
+void device_debug::memory_read_hook(const address_space &space, offs_t address, UINT64 mem_mask)
{
- const address_space *space = (const address_space *)ref;
- return (space != NULL) ? memory_get_log_unmap(space) : TRUE;
+ // check watchpoints
+ watchpoint_check(space, WATCHPOINT_READ, address, 0, mem_mask);
+
+ // check hotspots
+ if (m_hotspots != NULL)
+ hotspot_check(space, address);
}
-/*-------------------------------------------------
- set_logunmap - setter callback for the logumap
- symbols
--------------------------------------------------*/
+//-------------------------------------------------
+// memory_write_hook - the memory system calls
+// this hook when watchpoints are enabled and a
+// memory write happens
+//-------------------------------------------------
-static void set_logunmap(void *globalref, void *ref, UINT64 value)
+void device_debug::memory_write_hook(const address_space &space, offs_t address, UINT64 data, UINT64 mem_mask)
{
- const address_space *space = (const address_space *)ref;
- if (space != NULL)
- memory_set_log_unmap(space, value ? 1 : 0);
+ watchpoint_check(space, WATCHPOINT_WRITE, address, data, mem_mask);
}
-/*-------------------------------------------------
- get_cpu_reg - getter callback for a CPU's
- register symbols
--------------------------------------------------*/
+//-------------------------------------------------
+// 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)
+{
+ 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);
-static UINT64 get_cpu_reg(void *globalref, void *ref)
+ // make sure we get good results
+ assert((result & DASMFLAG_LENGTHMASK) != 0);
+#ifdef MAME_DEBUG
+if (m_memory != NULL && m_disasm != NULL)
{
- cpu_device *device = (cpu_device *)globalref;
- return cpu_get_reg(device, (FPTR)ref);
+ const address_space *space = m_memory->space(AS_PROGRAM);
+ int bytes = memory_address_to_byte(space, result & DASMFLAG_LENGTHMASK);
+ assert(bytes >= m_disasm->min_opcode_bytes());
+ assert(bytes <= m_disasm->max_opcode_bytes());
+ (void) bytes; // appease compiler
}
+#endif
+ return result;
+}
-/*-------------------------------------------------
- set_cpu_reg - setter callback for a CPU's
- register symbols
--------------------------------------------------*/
-static void set_cpu_reg(void *globalref, void *ref, UINT64 value)
+//-------------------------------------------------
+// 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 = attotime_add(timer_get_time(m_device.machine), ATTOTIME_IN_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);
+
+ m_flags |= DEBUG_FLAG_STOP_CONTEXT;
+ 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, parsed_expression *condition, const char *action)
+{
+ // allocate a new one
+ breakpoint *bp = auto_alloc(m_device.machine, breakpoint(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(const address_space &space, int type, offs_t address, offs_t length, parsed_expression *condition, const char *action)
+{
+ assert(space.spacenum < ARRAY_LENGTH(m_wplist));
+
+ // allocate a new one
+ watchpoint *wp = auto_alloc(m_device.machine, watchpoint(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 (int spacenum = 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;
+ const 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 (int spacenum = 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 (int spacenum = 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 (int spacenum = 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)];
+}
+
+
+//-------------------------------------------------
+// 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) && attotime_compare(m_endexectime, m_stoptime) <= 0)
+ 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()
{
- cpu_device *device = (cpu_device *)globalref;
- device->set_state((FPTR)ref, value);
+ // 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
+//-------------------------------------------------
-debug_cpu_breakpoint::debug_cpu_breakpoint(int index, offs_t address, parsed_expression *condition, const char *action)
+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.len() != 0)
+ 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(const 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
+ memory_enable_read_watchpoints(&space, enableread);
+ memory_enable_write_watchpoints(&space, enablewrite);
+}
+
+
+//-------------------------------------------------
+// watchpoint_check - check the watchpoints
+// for a given CPU and address space
+//-------------------------------------------------
+
+void device_debug::watchpoint_check(const 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.dbits / 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 != NULL)
+ 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.cpu->debug()->m_state != NULL) ? space.cpu->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], memory_byte_to_address(&space, 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], memory_byte_to_address(&space, address), pc);
+ debug_console_printf(space.machine, "%s\n", buffer.cstr());
+ space.cpu->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(const 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
+ const address_space *space = m_memory->space(AS_PROGRAM);
+ offs_t pcbyte = memory_address_to_byte(space, 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, pc, opbuf, argbuf);
+}
+
+
+//-------------------------------------------------
+// get_current_pc - getter callback for a device's
+// current instruction pointer
+//-------------------------------------------------
+
+UINT64 device_debug::get_current_pc(void *globalref, void *ref)
+{
+ device_t *device = reinterpret_cast<device_t *>(globalref);
+ return device->debug()->pc();
+}
+
+
+//-------------------------------------------------
+// get_cycles - getter callback for the
+// 'cycles' symbol
+//-------------------------------------------------
+
+UINT64 device_debug::get_cycles(void *globalref, void *ref)
+{
+ device_t *device = reinterpret_cast<device_t *>(globalref);
+ return device->debug()->m_exec->cycles_remaining();
+}
+
+
+//-------------------------------------------------
+// get_logunmap - getter callback for the logumap
+// symbols
+//-------------------------------------------------
+
+UINT64 device_debug::get_logunmap(void *globalref, void *ref)
+{
+ const address_space *space = reinterpret_cast<const address_space *>(ref);
+ return memory_get_log_unmap(space);
+}
+
+
+//-------------------------------------------------
+// set_logunmap - setter callback for the logumap
+// symbols
+//-------------------------------------------------
+
+void device_debug::set_logunmap(void *globalref, void *ref, UINT64 value)
+{
+ const address_space *space = reinterpret_cast<const address_space *>(ref);
+ memory_set_log_unmap(space, value ? 1 : 0);
+}
+
+
+//-------------------------------------------------
+// get_state - getter callback for a device's
+// state symbols
+//-------------------------------------------------
+
+UINT64 device_debug::get_cpu_reg(void *globalref, void *ref)
+{
+ device_t *device = reinterpret_cast<device_t *>(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(void *globalref, void *ref, UINT64 value)
+{
+ device_t *device = reinterpret_cast<device_t *>(globalref);
+ device->debug()->m_state->set_state(reinterpret_cast<FPTR>(ref), value);
+}
+
+
+
+//**************************************************************************
+// DEBUG BREAKPOINT
+//**************************************************************************
+
+//-------------------------------------------------
+// breakpoint - constructor
+//-------------------------------------------------
+
+device_debug::breakpoint::breakpoint(int index, offs_t address, parsed_expression *condition, const char *action)
: m_next(NULL),
m_index(index),
m_enabled(true),
@@ -2920,14 +2838,22 @@ debug_cpu_breakpoint::debug_cpu_breakpoint(int index, offs_t address, parsed_exp
}
-debug_cpu_breakpoint::~debug_cpu_breakpoint()
+//-------------------------------------------------
+// ~breakpoint - destructor
+//-------------------------------------------------
+
+device_debug::breakpoint::~breakpoint()
{
if (m_condition != NULL)
expression_free(m_condition);
}
-bool debug_cpu_breakpoint::hit(offs_t pc)
+//-------------------------------------------------
+// hit - detect a hit
+//-------------------------------------------------
+
+bool device_debug::breakpoint::hit(offs_t pc)
{
// don't hit if disabled
if (!m_enabled)
@@ -2946,7 +2872,16 @@ bool debug_cpu_breakpoint::hit(offs_t pc)
}
-debug_cpu_watchpoint::debug_cpu_watchpoint(int index, const address_space &space, int type, offs_t address, offs_t length, parsed_expression *condition, const char *action)
+
+//**************************************************************************
+// DEBUG WATCHPOINT
+//**************************************************************************
+
+//-------------------------------------------------
+// watchpoint - constructor
+//-------------------------------------------------
+
+device_debug::watchpoint::watchpoint(int index, const address_space &space, int type, offs_t address, offs_t length, parsed_expression *condition, const char *action)
: m_next(NULL),
m_space(space),
m_index(index),
@@ -2960,14 +2895,22 @@ debug_cpu_watchpoint::debug_cpu_watchpoint(int index, const address_space &space
}
-debug_cpu_watchpoint::~debug_cpu_watchpoint()
+//-------------------------------------------------
+// ~watchpoint - destructor
+//-------------------------------------------------
+
+device_debug::watchpoint::~watchpoint()
{
if (m_condition != NULL)
expression_free(m_condition);
}
-bool debug_cpu_watchpoint::hit(int type, offs_t address, int size)
+//-------------------------------------------------
+// hit - detect a hit
+//-------------------------------------------------
+
+bool device_debug::watchpoint::hit(int type, offs_t address, int size)
{
// don't hit if disabled
if (!m_enabled)
@@ -2990,11 +2933,126 @@ bool debug_cpu_watchpoint::hit(int type, offs_t address, int size)
}
-debug_trace_info::debug_trace_info()
- : file(NULL),
- loops(0),
- nextdex(0),
- trace_over_target(0)
+
+//**************************************************************************
+// 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()
{
- memset(history, 0, sizeof(history));
+ // 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 != NULL)
+ 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);
+}
+
diff --git a/src/emu/debug/debugcpu.h b/src/emu/debug/debugcpu.h
index 501af3e31b2..b412711bb6e 100644
--- a/src/emu/debug/debugcpu.h
+++ b/src/emu/debug/debugcpu.h
@@ -4,10 +4,38 @@
Debugger CPU/memory interface engine.
- Copyright Nicola Salmoria and the MAME Team.
- Visit http://mamedev.org for licensing and usage restrictions.
+****************************************************************************
+
+ 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.
-*********************************************************************/
+***************************************************************************/
#pragma once
@@ -17,85 +45,24 @@
#include "express.h"
-/***************************************************************************
- CONSTANTS
-***************************************************************************/
+//**************************************************************************
+// CONSTANTS
+//**************************************************************************
-#define TRACE_LOOPS 64
-#define DEBUG_HISTORY_SIZE 256
-
-#define WATCHPOINT_READ 1
-#define WATCHPOINT_WRITE 2
-#define WATCHPOINT_READWRITE (WATCHPOINT_READ | WATCHPOINT_WRITE)
-
-#define DEBUG_FLAG_OBSERVING 0x00000001 /* observing this CPU */
-#define DEBUG_FLAG_HISTORY 0x00000002 /* tracking this CPU's history */
-#define DEBUG_FLAG_TRACING 0x00000004 /* tracing this CPU */
-#define DEBUG_FLAG_TRACING_OVER 0x00000008 /* tracing this CPU with step over behavior */
-#define DEBUG_FLAG_HOOKED 0x00000010 /* per-instruction callback hook */
-#define DEBUG_FLAG_STEPPING 0x00000020 /* CPU is single stepping */
-#define DEBUG_FLAG_STEPPING_OVER 0x00000040 /* CPU is stepping over a function */
-#define DEBUG_FLAG_STEPPING_OUT 0x00000080 /* CPU is stepping out of a function */
-#define DEBUG_FLAG_STOP_PC 0x00000100 /* there is a pending stop at cpu->breakpc */
-#define DEBUG_FLAG_STOP_CONTEXT 0x00000200 /* there is a pending stop on next context switch */
-#define DEBUG_FLAG_STOP_INTERRUPT 0x00000400 /* there is a pending stop on the next interrupt */
-#define DEBUG_FLAG_STOP_EXCEPTION 0x00000800 /* there is a pending stop on the next exception */
-#define DEBUG_FLAG_STOP_VBLANK 0x00001000 /* there is a pending stop on the next VBLANK */
-#define DEBUG_FLAG_STOP_TIME 0x00002000 /* there is a pending stop at cpu->stoptime */
-#define DEBUG_FLAG_LIVE_BP 0x00010000 /* there are live breakpoints for this CPU */
-
-#define DEBUG_FLAG_STEPPING_ANY (DEBUG_FLAG_STEPPING | \
- DEBUG_FLAG_STEPPING_OVER | \
- DEBUG_FLAG_STEPPING_OUT)
-
-#define DEBUG_FLAG_TRACING_ANY (DEBUG_FLAG_TRACING | \
- DEBUG_FLAG_TRACING_OVER)
-
-#define DEBUG_FLAG_TRANSIENT (DEBUG_FLAG_STEPPING_ANY | \
- DEBUG_FLAG_STOP_PC | \
- DEBUG_FLAG_STOP_CONTEXT | \
- DEBUG_FLAG_STOP_INTERRUPT | \
- DEBUG_FLAG_STOP_EXCEPTION | \
- DEBUG_FLAG_STOP_VBLANK | \
- DEBUG_FLAG_STOP_TIME)
-
-
-
-/***************************************************************************
- TYPE DEFINITIONS
-***************************************************************************/
+const UINT8 WATCHPOINT_READ = 1;
+const UINT8 WATCHPOINT_WRITE = 2;
+const UINT8 WATCHPOINT_READWRITE = WATCHPOINT_READ | WATCHPOINT_WRITE;
-typedef int (*debug_instruction_hook_func)(device_t &device, offs_t curpc);
-class debug_cpu_breakpoint;
-class debug_cpu_watchpoint;
-class debug_cpu_comment_group;
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
-
-class debug_trace_info
-{
-public:
- debug_trace_info();
-
- FILE * file; /* tracing file for this CPU */
- astring action; /* action to perform during a trace */
- offs_t history[TRACE_LOOPS]; /* history of recent PCs */
- int loops; /* number of instructions in a loop */
- int nextdex; /* next index */
- offs_t trace_over_target; /* target for tracing over
- (0 = not tracing over,
- ~0 = not currently tracing over) */
-};
+typedef int (*debug_instruction_hook_func)(device_t &device, offs_t curpc);
-struct debug_hotspot_entry
-{
- offs_t access; /* access address */
- offs_t pc; /* PC of the access */
- const address_space *space; /* space where the access occurred */
- UINT32 count; /* number of hits */
-};
+class debug_cpu_comment_group;
class device_debug
@@ -103,18 +70,87 @@ class device_debug
typedef offs_t (*dasm_override_func)(device_t &device, char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram, int options);
public:
+ // breakpoint class
+ class breakpoint
+ {
+ friend class device_debug;
+
+ public:
+ // construction/destruction
+ breakpoint(int index, offs_t address, parsed_expression *condition = NULL, const char *action = NULL);
+ ~breakpoint();
+
+ // getters
+ breakpoint *next() const { return m_next; }
+ int index() const { return m_index; }
+ bool enabled() const { return m_enabled; }
+ offs_t address() const { return m_address; }
+ const char *condition() const { return (m_condition != NULL) ? expression_original_string(m_condition) : NULL; }
+ const char *action() const { return m_action; }
+
+ private:
+ // internals
+ bool hit(offs_t pc);
+
+ breakpoint * m_next; // next in the list
+ int m_index; // user reported index
+ UINT8 m_enabled; // enabled?
+ offs_t m_address; // execution address
+ parsed_expression * m_condition; // condition
+ astring m_action; // action
+ };
+
+ // watchpoint class
+ class watchpoint
+ {
+ friend class device_debug;
+
+ public:
+ // construction/destruction
+ watchpoint(int index, const address_space &space, int type, offs_t address, offs_t length, parsed_expression *condition = NULL, const char *action = NULL);
+ ~watchpoint();
+
+ // getters
+ watchpoint *next() const { return m_next; }
+ const address_space &space() const { return m_space; }
+ int index() const { return m_index; }
+ int type() const { return m_type; }
+ bool enabled() const { return m_enabled; }
+ offs_t address() const { return m_address; }
+ offs_t length() const { return m_length; }
+ const char *condition() const { return (m_condition != NULL) ? expression_original_string(m_condition) : NULL; }
+ const char *action() const { return m_action; }
+
+ private:
+ // internals
+ bool hit(int type, offs_t address, int size);
+
+ watchpoint * m_next; // next in the list
+ const address_space &m_space; // address space
+ int m_index; // user reported index
+ bool m_enabled; // enabled?
+ UINT8 m_type; // type (read/write)
+ offs_t m_address; // start address
+ offs_t m_length; // length of watch area
+ parsed_expression * m_condition; // condition
+ astring m_action; // action
+ };
+
+public:
+ // construction/destruction
device_debug(device_t &device, symbol_table *globalsyms);
~device_debug();
+ // getters
symbol_table *symtable() const { return m_symtable; }
-
- int logaddrchars(int spacenum = AS_PROGRAM) { return (m_memory != NULL && m_memory->space(spacenum) != NULL) ? m_memory->space(spacenum)->logaddrchars : 8; }
-
- offs_t disassemble(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram);
- void set_dasm_override(dasm_override_func dasm_override) { m_dasm_override = dasm_override; }
+
+ // commonly-used pass-throughs
+ offs_t pc() const { return (m_state != NULL) ? m_state->pc() : 0; }
+ int logaddrchars(int spacenum = AS_PROGRAM) const { return (m_memory != NULL && m_memory->space(spacenum) != NULL) ? m_memory->space(spacenum)->logaddrchars : 8; }
int min_opcode_bytes() const { return (m_disasm != NULL) ? m_disasm->max_opcode_bytes() : 1; }
int max_opcode_bytes() const { return (m_disasm != NULL) ? m_disasm->max_opcode_bytes() : 1; }
-
+
+ // hooks used by the rest of the system
void start_hook(attotime endtime);
void stop_hook();
void interrupt_hook(int irqline);
@@ -123,167 +159,197 @@ public:
void memory_read_hook(const address_space &space, offs_t address, UINT64 mem_mask);
void memory_write_hook(const address_space &space, offs_t address, UINT64 data, UINT64 mem_mask);
- void halt_on_next_instruction(const char *fmt, ...);
+ // hooks into our operations
+ void set_instruction_hook(debug_instruction_hook_func hook);
+ void set_dasm_override(dasm_override_func dasm_override) { m_dasm_override = dasm_override; }
+
+ // disassembly
+ offs_t disassemble(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram);
+
+ // debugger focus
void ignore(bool ignore = true);
bool observing() const { return ((m_flags & DEBUG_FLAG_OBSERVING) != 0); }
+ // single stepping
void single_step(int numsteps = 1);
void single_step_over(int numsteps = 1);
void single_step_out();
+ // execution
void go(offs_t targetpc = ~0);
void go_vblank();
void go_interrupt(int irqline = -1);
void go_exception(int exception);
void go_milliseconds(UINT64 milliseconds);
void go_next_device();
+ void halt_on_next_instruction(const char *fmt, ...);
- debug_cpu_breakpoint *breakpoint_first() const { return m_bplist; }
+ // breakpoints
+ breakpoint *breakpoint_first() const { return m_bplist; }
int breakpoint_set(offs_t address, parsed_expression *condition = NULL, const char *action = NULL);
bool breakpoint_clear(int index);
void breakpoint_clear_all();
bool breakpoint_enable(int index, bool enable = true);
void breakpoint_enable_all(bool enable = true);
- debug_cpu_watchpoint *watchpoint_first(int spacenum) const { return m_wplist[spacenum]; }
+ // watchpoints
+ watchpoint *watchpoint_first(int spacenum) const { return m_wplist[spacenum]; }
int watchpoint_set(const address_space &space, int type, offs_t address, offs_t length, parsed_expression *condition, const char *action);
bool watchpoint_clear(int wpnum);
void watchpoint_clear_all();
bool watchpoint_enable(int index, bool enable = true);
void watchpoint_enable_all(bool enable = true);
+ // hotspots
bool hotspot_tracking_enabled() const { return (m_hotspots != NULL); }
void hotspot_track(int numspots, int threshhold);
-
+
+ // history
offs_t history_pc(int index) const;
+ // tracing
void trace(FILE *file, bool trace_over, const char *action);
void trace_printf(const char *fmt, ...);
- void trace_flush() { if (m_trace.file != NULL) fflush(m_trace.file); }
-
- void set_instruction_hook(debug_instruction_hook_func hook);
+ void trace_flush() { if (m_trace != NULL) m_trace->flush(); }
void reset_transient_flag() { m_flags &= ~DEBUG_FLAG_TRANSIENT; }
+ static const int HISTORY_SIZE = 256;
+
private:
+ // internal helpers
void compute_debug_flags();
+ void prepare_for_step_overout(offs_t pc);
+ UINT32 dasm_wrapped(astring &buffer, offs_t pc);
- void perform_trace();
- void prepare_for_step_overout();
-
+ // breakpoint and watchpoint helpers
void breakpoint_update_flags();
void breakpoint_check(offs_t pc);
-
void watchpoint_update_flags(const address_space &space);
void watchpoint_check(const address_space &space, int type, offs_t address, UINT64 value_to_write, UINT64 mem_mask);
-
void hotspot_check(const address_space &space, offs_t address);
- UINT32 dasm_wrapped(astring &buffer, offs_t pc);
-
- device_t & m_device;
- device_execute_interface *m_exec;
- device_memory_interface *m_memory;
- device_state_interface *m_state;
- device_disasm_interface *m_disasm;
-
- symbol_table * m_symtable; /* symbol table for expression evaluation */
- UINT32 m_flags; /* debugging flags for this CPU */
- UINT8 m_opwidth; /* width of an opcode */
- offs_t m_stepaddr; /* step target address for DEBUG_FLAG_STEPPING_OVER */
- int m_stepsleft; /* number of steps left until done */
- offs_t m_stopaddr; /* stop address for DEBUG_FLAG_STOP_PC */
- attotime m_stoptime; /* stop time for DEBUG_FLAG_STOP_TIME */
- int m_stopirq; /* stop IRQ number for DEBUG_FLAG_STOP_INTERRUPT */
- int m_stopexception; /* stop exception number for DEBUG_FLAG_STOP_EXCEPTION */
- attotime m_endexectime; /* ending time of the current execution */
- debug_trace_info m_trace; /* trace info */
- debug_cpu_breakpoint * m_bplist; /* list of breakpoints */
- debug_hotspot_entry * m_hotspots; /* hotspot list */
- offs_t m_pc_history[DEBUG_HISTORY_SIZE]; /* history of recent PCs */
- UINT32 m_pc_history_index; /* current history index */
- int m_hotspot_count; /* number of hotspots */
- int m_hotspot_threshhold; /* threshhold for the number of hits to print */
- dasm_override_func m_dasm_override; /* pointer to provided override function */
- debug_instruction_hook_func m_instrhook; /* per-instruction callback hook */
- debug_cpu_watchpoint * m_wplist[ADDRESS_SPACES]; /* watchpoint lists for each address space */
-
-public: // until comments get folded in
- debug_cpu_comment_group *m_comments; /* disassembly comments */
-};
-
-class debug_cpu_breakpoint
-{
- friend class device_debug;
-
-public:
- debug_cpu_breakpoint(int index, offs_t address, parsed_expression *condition = NULL, const char *action = NULL);
- ~debug_cpu_breakpoint();
+ // symbol get/set callbacks
+ static UINT64 get_current_pc(void *globalref, void *ref);
+ static UINT64 get_cycles(void *globalref, void *ref);
+ static UINT64 get_logunmap(void *globalref, void *ref);
+ static void set_logunmap(void *globalref, void *ref, UINT64 value);
+ static UINT64 get_cpu_reg(void *globalref, void *ref);
+ static void set_state(void *globalref, void *ref, UINT64 value);
- debug_cpu_breakpoint *next() const { return m_next; }
- int index() const { return m_index; }
- bool enabled() const { return m_enabled; }
- offs_t address() const { return m_address; }
- const char *condition() const { return (m_condition != NULL) ? expression_original_string(m_condition) : NULL; }
- const char *action() const { return m_action; }
+ // basic device information
+ device_t & m_device; // device we are attached to
+ device_execute_interface *m_exec; // execute interface, if present
+ device_memory_interface *m_memory; // memory interface, if present
+ device_state_interface *m_state; // state interface, if present
+ device_disasm_interface *m_disasm; // disasm interface, if present
+
+ // global state
+ UINT32 m_flags; // debugging flags for this CPU
+ symbol_table * m_symtable; // symbol table for expression evaluation
+ debug_instruction_hook_func m_instrhook; // per-instruction callback hook
+
+ // disassembly
+ dasm_override_func m_dasm_override; // pointer to provided override function
+ UINT8 m_opwidth; // width of an opcode
+
+ // stepping information
+ offs_t m_stepaddr; // step target address for DEBUG_FLAG_STEPPING_OVER
+ int m_stepsleft; // number of steps left until done
+
+ // execution information
+ offs_t m_stopaddr; // stop address for DEBUG_FLAG_STOP_PC
+ attotime m_stoptime; // stop time for DEBUG_FLAG_STOP_TIME
+ int m_stopirq; // stop IRQ number for DEBUG_FLAG_STOP_INTERRUPT
+ int m_stopexception; // stop exception number for DEBUG_FLAG_STOP_EXCEPTION
+ attotime m_endexectime; // ending time of the current execution
+
+ // history
+ offs_t m_pc_history[HISTORY_SIZE]; // history of recent PCs
+ UINT32 m_pc_history_index; // current history index
+
+ // breakpoints and watchpoints
+ breakpoint * m_bplist; // list of breakpoints
+ watchpoint * m_wplist[ADDRESS_SPACES]; // watchpoint lists for each address space
+
+ // tracing
+ class tracer
+ {
+ public:
+ tracer(device_debug &debug, FILE &file, bool trace_over, const char *action);
+ ~tracer();
+
+ void update(offs_t pc);
+ void vprintf(const char *format, va_list va);
+ void flush();
-private:
- bool hit(offs_t pc);
-
- debug_cpu_breakpoint *m_next; /* next in the list */
- int m_index; /* user reported index */
- UINT8 m_enabled; /* enabled? */
- offs_t m_address; /* execution address */
- parsed_expression *m_condition; /* condition */
- astring m_action; /* action */
-};
-
-
-class debug_cpu_watchpoint
-{
- friend class device_debug;
+ private:
+ static const int TRACE_LOOPS = 64;
+
+ device_debug & m_debug; // reference to our owner
+ FILE & m_file; // tracing file for this CPU
+ astring m_action; // action to perform during a trace
+ offs_t m_history[TRACE_LOOPS]; // history of recent PCs
+ int m_loops; // number of instructions in a loop
+ int m_nextdex; // next index
+ bool m_trace_over; // true if we're tracing over
+ offs_t m_trace_over_target; // target for tracing over
+ // (0 = not tracing over,
+ // ~0 = not currently tracing over)
+ };
+ tracer * m_trace; // tracer state
+
+ // hotspots
+ struct hotspot_entry
+ {
+ offs_t m_access; // access address
+ offs_t m_pc; // PC of the access
+ const address_space *m_space; // space where the access occurred
+ UINT32 m_count; // number of hits
+ };
+ hotspot_entry * m_hotspots; // hotspot list
+ int m_hotspot_count; // number of hotspots
+ int m_hotspot_threshhold; // threshhold for the number of hits to print
+
+ // internal flag values
+ static const UINT32 DEBUG_FLAG_OBSERVING = 0x00000001; // observing this CPU
+ static const UINT32 DEBUG_FLAG_HISTORY = 0x00000002; // tracking this CPU's history
+ static const UINT32 DEBUG_FLAG_TRACING = 0x00000004; // tracing this CPU
+ static const UINT32 DEBUG_FLAG_TRACING_OVER = 0x00000008; // tracing this CPU with step over behavior
+ static const UINT32 DEBUG_FLAG_HOOKED = 0x00000010; // per-instruction callback hook
+ static const UINT32 DEBUG_FLAG_STEPPING = 0x00000020; // CPU is single stepping
+ static const UINT32 DEBUG_FLAG_STEPPING_OVER = 0x00000040; // CPU is stepping over a function
+ static const UINT32 DEBUG_FLAG_STEPPING_OUT = 0x00000080; // CPU is stepping out of a function
+ static const UINT32 DEBUG_FLAG_STOP_PC = 0x00000100; // there is a pending stop at cpu->breakpc
+ static const UINT32 DEBUG_FLAG_STOP_CONTEXT = 0x00000200; // there is a pending stop on next context switch
+ static const UINT32 DEBUG_FLAG_STOP_INTERRUPT = 0x00000400; // there is a pending stop on the next interrupt
+ static const UINT32 DEBUG_FLAG_STOP_EXCEPTION = 0x00000800; // there is a pending stop on the next exception
+ static const UINT32 DEBUG_FLAG_STOP_VBLANK = 0x00001000; // there is a pending stop on the next VBLANK
+ static const UINT32 DEBUG_FLAG_STOP_TIME = 0x00002000; // there is a pending stop at cpu->stoptime
+ static const UINT32 DEBUG_FLAG_LIVE_BP = 0x00010000; // there are live breakpoints for this CPU
+
+ static const UINT32 DEBUG_FLAG_STEPPING_ANY = DEBUG_FLAG_STEPPING | DEBUG_FLAG_STEPPING_OVER | DEBUG_FLAG_STEPPING_OUT;
+ static const UINT32 DEBUG_FLAG_TRACING_ANY = DEBUG_FLAG_TRACING | DEBUG_FLAG_TRACING_OVER;
+ static const UINT32 DEBUG_FLAG_TRANSIENT = DEBUG_FLAG_STEPPING_ANY | DEBUG_FLAG_STOP_PC | DEBUG_FLAG_STOP_CONTEXT |
+ DEBUG_FLAG_STOP_INTERRUPT | DEBUG_FLAG_STOP_EXCEPTION | DEBUG_FLAG_STOP_VBLANK | DEBUG_FLAG_STOP_TIME;
-public:
- debug_cpu_watchpoint(int index, const address_space &space, int type, offs_t address, offs_t length, parsed_expression *condition = NULL, const char *action = NULL);
- ~debug_cpu_watchpoint();
-
- debug_cpu_watchpoint *next() const { return m_next; }
- const address_space &space() const { return m_space; }
- int index() const { return m_index; }
- int type() const { return m_type; }
- bool enabled() const { return m_enabled; }
- offs_t address() const { return m_address; }
- offs_t length() const { return m_length; }
- const char *condition() const { return (m_condition != NULL) ? expression_original_string(m_condition) : NULL; }
- const char *action() const { return m_action; }
-
-private:
- bool hit(int type, offs_t address, int size);
-
- debug_cpu_watchpoint *m_next; /* next in the list */
- const address_space &m_space; // address space
- int m_index; /* user reported index */
- bool m_enabled; /* enabled? */
- UINT8 m_type; /* type (read/write) */
- offs_t m_address; /* start address */
- offs_t m_length; /* length of watch area */
- parsed_expression *m_condition; /* condition */
- astring m_action; /* action */
+public: // until comments get folded in
+ debug_cpu_comment_group *m_comments; // disassembly comments
};
-/***************************************************************************
- GLOBAL VARIABLES
-***************************************************************************/
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
extern const express_callbacks debug_expression_callbacks;
-/***************************************************************************
- FUNCTION PROTOTYPES
-***************************************************************************/
+//**************************************************************************
+// FUNCTION PROTOTYPES
+//**************************************************************************
/* ----- initialization and cleanup ----- */
@@ -318,13 +384,6 @@ symbol_table *debug_cpu_get_visible_symtable(running_machine *machine);
-/* ----- memory and disassembly helpers ----- */
-
-/* return the physical address corresponding to the given logical address */
-int debug_cpu_translate(const address_space *space, int intention, offs_t *address);
-
-
-
/* ----- misc debugger functions ----- */
/* specifies a debug command script to execute */
@@ -334,6 +393,9 @@ void debug_cpu_source_script(running_machine *machine, const char *file);
/* ----- debugger memory accessors ----- */
+/* return the physical address corresponding to the given logical address */
+int debug_cpu_translate(const address_space *space, int intention, offs_t *address);
+
/* return a byte from the the specified memory space */
UINT8 debug_read_byte(const address_space *space, offs_t address, int apply_translation);
@@ -367,4 +429,5 @@ void debug_write_memory(const address_space *space, offs_t address, UINT64 data,
/* read 1,2,4 or 8 bytes at the given offset from opcode space */
UINT64 debug_read_opcode(const address_space *space, offs_t offset, int size, int arg);
+
#endif
diff --git a/src/emu/debug/dvdisasm.c b/src/emu/debug/dvdisasm.c
index d52020603f5..6840b2724c7 100644
--- a/src/emu/debug/dvdisasm.c
+++ b/src/emu/debug/dvdisasm.c
@@ -578,7 +578,7 @@ recompute:
// if we're on a line with a breakpoint, tag it changed
else
{
- for (debug_cpu_breakpoint *bp = source.m_device.debug()->breakpoint_first(); bp != NULL; bp = bp->next())
+ for (device_debug::breakpoint *bp = source.m_device.debug()->breakpoint_first(); bp != NULL; bp = bp->next())
if (m_byteaddress[effrow] == (memory_address_to_byte(source.m_space, bp->address()) & source.m_space->logbytemask))
attrib = DCA_CHANGED;
}
diff --git a/src/emu/ui.c b/src/emu/ui.c
index 1366554e2a7..22f412daefd 100644
--- a/src/emu/ui.c
+++ b/src/emu/ui.c
@@ -1005,24 +1005,20 @@ astring &game_info_astring(running_machine *machine, astring &string)
/* loop over all CPUs */
device_execute_interface *exec;
- int count = 1;
for (bool gotone = machine->m_devicelist.first(exec); gotone; gotone = exec->next(exec))
{
- // skip over any we already claimed
- if (--count != 0)
- continue;
-
/* get cpu specific clock that takes internal multiplier/dividers into account */
int clock = exec->device().clock();
/* count how many identical CPUs we have */
- count = 1;
+ int count = 1;
device_execute_interface *scan;
for (bool gotone = exec->next(scan); gotone; gotone = scan->next(scan))
{
if (exec->device().type() != scan->device().type() || exec->device().clock() != scan->device().clock())
break;
count++;
+ exec = scan;
}
/* if more than one, prepend a #x in front of the CPU name */
@@ -1047,9 +1043,9 @@ astring &game_info_astring(running_machine *machine, astring &string)
found_sound = TRUE;
/* count how many identical sound chips we have */
- count = 1;
- device_sound_interface *scan = sound;
- for (bool gotanother = scan->next(scan); gotanother; gotanother = scan->next(scan))
+ int count = 1;
+ device_sound_interface *scan;
+ for (bool gotanother = sound->next(scan); gotanother; gotanother = scan->next(scan))
{
if (sound->device().type() != scan->device().type() || sound->device().clock() != scan->device().clock())
break;
diff --git a/src/osd/sdl/debugosx.m b/src/osd/sdl/debugosx.m
index 155e137ea98..ec8130f3022 100644
--- a/src/osd/sdl/debugosx.m
+++ b/src/osd/sdl/debugosx.m
@@ -872,9 +872,9 @@ void console_create_window(running_machine *machine)
@implementation MAMEDisassemblyView
-- (debug_cpu_breakpoint *)findBreakpointAtAddress:(offs_t)address inAddressSpace:(const address_space *)space {
+- (device_debug::breakpoint *)findBreakpointAtAddress:(offs_t)address inAddressSpace:(const address_space *)space {
cpu_debug_data *cpuinfo = cpu_get_debug_data(space->cpu);
- debug_cpu_breakpoint *bp;
+ device_debug::breakpoint *bp;
for (bp = cpuinfo->bplist; (bp != NULL) && (address != bp->address); bp = bp->next) {}
return bp;
}
@@ -946,7 +946,7 @@ void console_create_window(running_machine *machine)
SEL action = [item action];
BOOL inContextMenu = ([item menu] == [self menu]);
BOOL haveCursor = NO, isCurrent = NO;
- debug_cpu_breakpoint *breakpoint = NULL;
+ device_debug::breakpoint *breakpoint = NULL;
if (view->cursor_visible()) {
if (debug_cpu_get_visible_cpu(machine) == view->source()->device()) {
@@ -1078,7 +1078,7 @@ void console_create_window(running_machine *machine)
const address_space *space = downcast<const debug_view_disasm_source *>(view->source())->space();
if (!useConsole || (debug_cpu_get_visible_cpu(machine) == space->cpu)) {
offs_t address = downcast<debug_view_disasm *>(view)->selected_address();
- debug_cpu_breakpoint *bp = [self findBreakpointAtAddress:address inAddressSpace:space];
+ device_debug::breakpoint *bp = [self findBreakpointAtAddress:address inAddressSpace:space];
// if it doesn't exist, add a new one
if (useConsole) {
@@ -1104,7 +1104,7 @@ void console_create_window(running_machine *machine)
const address_space *space = downcast<const debug_view_disasm_source *>(view->source())->space();
if (!useConsole || (debug_cpu_get_visible_cpu(machine) == space->cpu)) {
offs_t address = downcast<debug_view_disasm *>(view)->selected_address();
- debug_cpu_breakpoint *bp = [self findBreakpointAtAddress:address inAddressSpace:space];
+ device_debug::breakpoint *bp = [self findBreakpointAtAddress:address inAddressSpace:space];
if (bp != NULL) {
NSString *command;
diff --git a/src/osd/sdl/debugwin.c b/src/osd/sdl/debugwin.c
index 883575aa360..8585371319b 100644
--- a/src/osd/sdl/debugwin.c
+++ b/src/osd/sdl/debugwin.c
@@ -1011,7 +1011,7 @@ on_set_breakpoint_at_cursor_activate(GtkWidget *win)
{
offs_t address = downcast<debug_view_disasm *>(disasm->view)->selected_address();
device_debug *cpuinfo = disasm->view->source()->device()->debug();
- debug_cpu_breakpoint *bp;
+ device_debug::breakpoint *bp;
INT32 bpindex = -1;
/* first find an existing breakpoint at this address */
diff --git a/src/osd/windows/debugwin.c b/src/osd/windows/debugwin.c
index 7e15e47e2c7..23dce31ac0b 100644
--- a/src/osd/windows/debugwin.c
+++ b/src/osd/windows/debugwin.c
@@ -2255,7 +2255,7 @@ static int disasm_handle_command(debugwin_info *info, WPARAM wparam, LPARAM lpar
INT32 bpindex = -1;
/* first find an existing breakpoint at this address */
- for (debug_cpu_breakpoint *bp = debug->breakpoint_first(); bp != NULL; bp = bp->next())
+ for (device_debug::breakpoint *bp = debug->breakpoint_first(); bp != NULL; bp = bp->next())
if (address == bp->address())
{
bpindex = bp->index();