mame/src/emu/debug/debugcpu.c
Aaron Giles 4a80c53a8d [from AtariAce]
Hi mamedev,

This patch continues deglobalifying the MAME core, this time targeting
sound.c.  The first two patches adds running_machine to apis in
sound.h that lack it (the first patch is generated by the perl script,
the second patch fixes some cases it didn't handle well).  The last
patch then removes the globals in the traditional way.

~aa
2009-08-30 05:42:33 +00:00

3084 lines
94 KiB
C

/*********************************************************************
debugcpu.c
Debugger CPU/memory interface engine.
Copyright Nicola Salmoria and the MAME Team.
Visit http://mamedev.org for licensing and usage restrictions.
**********************************************************************
Future work:
- enable history to be enabled/disabled to improve performance
*********************************************************************/
#include "osdepend.h"
#include "driver.h"
#include "debugcpu.h"
#include "debugcmd.h"
#include "debugcmt.h"
#include "debugcon.h"
#include "express.h"
#include "debugvw.h"
#include "debugger.h"
#include "uiinput.h"
#include "machine/eeprom.h"
#include <ctype.h>
/***************************************************************************
CONSTANTS
***************************************************************************/
#define NUM_TEMP_VARIABLES 10
enum
{
EXECUTION_STATE_STOPPED,
EXECUTION_STATE_RUNNING
};
/***************************************************************************
TYPE DEFINITIONS
***************************************************************************/
/* in mame.h: typedef struct _debugcpu_private debugcpu_private; */
struct _debugcpu_private
{
const device_config *livecpu;
const device_config *visiblecpu;
const device_config *breakcpu;
FILE * source_file; /* script source file */
symbol_table * symtable; /* global symbol table */
UINT8 within_instruction_hook;
UINT8 vblank_occurred;
UINT8 memory_modified;
UINT8 debugger_access;
int execution_state;
UINT32 bpindex;
UINT32 wpindex;
UINT64 wpdata;
UINT64 wpaddr;
UINT64 tempvar[NUM_TEMP_VARIABLES];
osd_ticks_t last_periodic_update_time;
};
/***************************************************************************
FUNCTION PROTOTYPES
***************************************************************************/
/* internal helpers */
static void debug_cpu_exit(running_machine *machine);
static void on_vblank(const device_config *device, void *param, int vblank_state);
static void reset_transient_flags(running_machine *machine);
static void compute_debug_flags(const device_config *device);
static void perform_trace(cpu_debug_data *info);
static void prepare_for_step_overout(cpu_debug_data *info);
static void process_source_file(running_machine *machine);
static void breakpoint_update_flags(cpu_debug_data *info);
static void breakpoint_check(running_machine *machine, cpu_debug_data *info, offs_t pc);
static void watchpoint_update_flags(const address_space *space);
static void watchpoint_check(const address_space *space, int type, offs_t address, UINT64 value_to_write, UINT64 mem_mask);
static void check_hotspots(const address_space *space, offs_t address);
static UINT32 dasm_wrapped(const device_config *device, char *buffer, offs_t pc);
/* expression handlers */
static UINT64 expression_read_memory(void *param, const char *name, int space, UINT32 address, int size);
static UINT64 expression_read_address_space(const address_space *space, offs_t address, int size);
static UINT64 expression_read_program_direct(const address_space *space, int opcode, offs_t address, int size);
static UINT64 expression_read_memory_region(running_machine *machine, const char *rgntag, offs_t address, int size);
static UINT64 expression_read_eeprom(running_machine *machine, offs_t address, int size);
static void expression_write_memory(void *param, const char *name, int space, UINT32 address, int size, UINT64 data);
static void expression_write_address_space(const address_space *space, offs_t address, int size, UINT64 data);
static void expression_write_program_direct(const address_space *space, int opcode, offs_t address, int size, UINT64 data);
static void expression_write_memory_region(running_machine *machine, const char *rgntag, offs_t address, int size, UINT64 data);
static void expression_write_eeprom(running_machine *machine, offs_t address, int size, UINT64 data);
static EXPRERR expression_validate(void *param, const char *name, int space);
/* variable getters/setters */
static UINT64 get_wpaddr(void *globalref, void *ref);
static UINT64 get_wpdata(void *globalref, void *ref);
static UINT64 get_cpunum(void *globalref, void *ref);
static UINT64 get_tempvar(void *globalref, void *ref);
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);
/***************************************************************************
GLOBAL CONSTANTS
***************************************************************************/
const express_callbacks debug_expression_callbacks =
{
expression_read_memory,
expression_write_memory,
expression_validate
};
/***************************************************************************
INITIALIZATION AND CLEANUP
***************************************************************************/
/*-------------------------------------------------
debug_cpu_init - initialize the CPU
information for debugging
-------------------------------------------------*/
void debug_cpu_init(running_machine *machine)
{
const device_config *first_screen = video_screen_first(machine->config);
debugcpu_private *global;
const device_config *cpu;
int regnum;
/* allocate and reset globals */
machine->debugcpu_data = global = auto_alloc_clear(machine, debugcpu_private);
global->execution_state = EXECUTION_STATE_STOPPED;
global->bpindex = 1;
global->wpindex = 1;
/* create a global symbol table */
global->symtable = symtable_alloc(NULL, machine);
/* add "wpaddr", "wpdata", "cycles", "cpunum", "logunmap" to the global symbol table */
symtable_add_register(global->symtable, "wpaddr", NULL, get_wpaddr, NULL);
symtable_add_register(global->symtable, "wpdata", NULL, get_wpdata, NULL);
symtable_add_register(global->symtable, "cpunum", NULL, get_cpunum, NULL);
symtable_add_register(global->symtable, "beamx", (void *)first_screen, get_beamx, NULL);
symtable_add_register(global->symtable, "beamy", (void *)first_screen, get_beamy, NULL);
symtable_add_register(global->symtable, "frame", (void *)first_screen, get_frame, NULL);
/* add the temporary variables to the global symbol table */
for (regnum = 0; regnum < NUM_TEMP_VARIABLES; regnum++)
{
char symname[10];
sprintf(symname, "temp%d", regnum);
symtable_add_register(global->symtable, symname, &global->tempvar[regnum], get_tempvar, set_tempvar);
}
/* loop over CPUs and build up their info */
for (cpu = machine->cpu[0]; cpu != NULL; cpu = cpu->typenext)
{
cpu_class_header *classheader = cpu_get_class_header(cpu);
cpu_debug_data *info;
/* allocate some information */
info = auto_alloc_clear(machine, cpu_debug_data);
classheader->debug = info;
/* reset the PC data */
info->flags = DEBUG_FLAG_OBSERVING | DEBUG_FLAG_HISTORY;
info->device = cpu;
info->opwidth = cpu_get_min_opcode_bytes(info->device);
/* fetch the memory accessors */
info->read = (cpu_read_func)device_get_info_fct(info->device, CPUINFO_FCT_READ);
info->write = (cpu_write_func)device_get_info_fct(info->device, CPUINFO_FCT_WRITE);
info->readop = (cpu_readop_func)device_get_info_fct(info->device, CPUINFO_FCT_READOP);
info->translate = (cpu_translate_func)device_get_info_fct(info->device, CPUINFO_FCT_TRANSLATE);
info->disassemble = (cpu_disassemble_func)device_get_info_fct(info->device, CPUINFO_FCT_DISASSEMBLE);
/* allocate a symbol table */
info->symtable = symtable_alloc(global->symtable, (void *)cpu);
/* add a global symbol for the current instruction pointer */
symtable_add_register(info->symtable, "cycles", NULL, get_cycles, NULL);
if (cpu->space[ADDRESS_SPACE_PROGRAM] != NULL)
symtable_add_register(info->symtable, "logunmap", (void *)cpu->space[ADDRESS_SPACE_PROGRAM], get_logunmap, set_logunmap);
if (cpu->space[ADDRESS_SPACE_DATA] != NULL)
symtable_add_register(info->symtable, "logunmapd", (void *)cpu->space[ADDRESS_SPACE_DATA], get_logunmap, set_logunmap);
if (cpu->space[ADDRESS_SPACE_IO] != NULL)
symtable_add_register(info->symtable, "logunmapi", (void *)cpu->space[ADDRESS_SPACE_IO], get_logunmap, set_logunmap);
/* add all registers into it */
for (regnum = 0; regnum < MAX_REGS; regnum++)
{
const char *str = cpu_get_reg_string(info->device, regnum);
const char *colon;
char symname[256];
int charnum;
/* skip if we don't get a valid string, or one without a colon */
if (str == NULL)
continue;
if (str[0] == '~')
str++;
colon = strchr(str, ':');
if (colon == NULL)
continue;
/* strip all spaces from the name and convert to lowercase */
for (charnum = 0; charnum < sizeof(symname) - 1 && str < colon; str++)
if (!isspace((UINT8)*str))
symname[charnum++] = tolower((UINT8)*str);
symname[charnum] = 0;
/* add the symbol to the table */
symtable_add_register(info->symtable, symname, (void *)(FPTR)regnum, get_cpu_reg, set_cpu_reg);
}
/* if no curpc, add one */
if (symtable_find(info->symtable, "curpc") == NULL)
symtable_add_register(info->symtable, "curpc", NULL, get_current_pc, 0);
}
/* first CPU is visible by default */
global->visiblecpu = machine->cpu[0];
/* add callback for breaking on VBLANK */
if (machine->primary_screen != NULL)
video_screen_register_vblank_callback(machine->primary_screen, on_vblank, NULL);
add_exit_callback(machine, debug_cpu_exit);
}
/*-------------------------------------------------
debug_cpu_flush_traces - flushes all traces;
this is useful if a trace is going on when we
fatalerror
-------------------------------------------------*/
void debug_cpu_flush_traces(running_machine *machine)
{
const device_config *cpu;
for (cpu = machine->cpu[0]; cpu != NULL; cpu = cpu->typenext)
{
cpu_debug_data *info = cpu_get_debug_data(cpu);
/* this can be called on exit even when no debugging is enabled, so
make sure the info is valid before proceeding */
if (info != NULL && info->trace.file != NULL)
fflush(info->trace.file);
}
}
/***************************************************************************
DEBUGGING STATUS AND INFORMATION
***************************************************************************/
/*-------------------------------------------------
cpu_get_visible_cpu - return the visible CPU
device (the one that commands should apply to)
-------------------------------------------------*/
const device_config *debug_cpu_get_visible_cpu(running_machine *machine)
{
return machine->debugcpu_data->visiblecpu;
}
/*-------------------------------------------------
debug_cpu_within_instruction_hook - true if
the debugger is currently live
-------------------------------------------------*/
int debug_cpu_within_instruction_hook(running_machine *machine)
{
return machine->debugcpu_data->within_instruction_hook;
}
/*-------------------------------------------------
debug_cpu_is_stopped - return TRUE if the
current execution state is stopped
-------------------------------------------------*/
int debug_cpu_is_stopped(running_machine *machine)
{
debugcpu_private *global = machine->debugcpu_data;
return (global != NULL) ? (global->execution_state == EXECUTION_STATE_STOPPED) : FALSE;
}
/***************************************************************************
SYMBOL TABLE INTERFACES
***************************************************************************/
/*-------------------------------------------------
debug_cpu_get_global_symtable - return the
global symbol table
-------------------------------------------------*/
symbol_table *debug_cpu_get_global_symtable(running_machine *machine)
{
return machine->debugcpu_data->symtable;
}
/*-------------------------------------------------
debug_cpu_get_visible_symtable - return the
locally-visible symbol table
-------------------------------------------------*/
symbol_table *debug_cpu_get_visible_symtable(running_machine *machine)
{
return cpu_get_debug_data(machine->debugcpu_data->visiblecpu)->symtable;
}
/*-------------------------------------------------
debug_cpu_get_symtable - return a specific
CPU's symbol table
-------------------------------------------------*/
symbol_table *debug_cpu_get_symtable(const device_config *device)
{
return cpu_get_debug_data(device)->symtable;
}
/***************************************************************************
MEMORY AND DISASSEMBLY HELPERS
***************************************************************************/
/*-------------------------------------------------
debug_cpu_translate - return the physical
address corresponding to the given logical
address
-------------------------------------------------*/
int debug_cpu_translate(const address_space *space, int intention, offs_t *address)
{
cpu_debug_data *info = cpu_get_debug_data(space->cpu);
if (info->translate != NULL)
return (*info->translate)(space->cpu, space->spacenum, intention, address);
else
return TRUE;
}
/*-------------------------------------------------
debug_cpu_disassemble - disassemble a line at
a given PC on a given CPU
-------------------------------------------------*/
offs_t debug_cpu_disassemble(const device_config *device, char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram)
{
cpu_debug_data *info = cpu_get_debug_data(device);
offs_t result = 0;
/* check for disassembler override */
if (info->dasm_override != NULL)
result = (*info->dasm_override)(device, buffer, pc, oprom, opram);
/* if we have a disassembler, run it */
if (result == 0 && info->disassemble != NULL)
result = (*info->disassemble)(device, buffer, pc, oprom, opram);
/* if we still have nothing, output vanilla bytes */
if (result == 0)
{
result = cpu_get_min_opcode_bytes(device);
switch (result)
{
case 1:
default:
sprintf(buffer, "$%02X", *(UINT8 *)oprom);
break;
case 2:
sprintf(buffer, "$%04X", *(UINT16 *)oprom);
break;
case 4:
sprintf(buffer, "$%08X", *(UINT32 *)oprom);
break;
case 8:
sprintf(buffer, "$%08X%08X", (UINT32)(*(UINT64 *)oprom >> 32), (UINT32)(*(UINT64 *)oprom >> 0));
break;
}
}
/* make sure we get good results */
assert((result & DASMFLAG_LENGTHMASK) != 0);
#ifdef MAME_DEBUG
{
const address_space *space = cpu_get_address_space(device, ADDRESS_SPACE_PROGRAM);
int bytes = memory_address_to_byte(space, result & DASMFLAG_LENGTHMASK);
assert(bytes >= cpu_get_min_opcode_bytes(device));
assert(bytes <= cpu_get_max_opcode_bytes(device));
(void) bytes; /* appease compiler */
}
#endif
return result;
}
/*-------------------------------------------------
debug_cpu_set_dasm_override - set an override
handler for disassembly
-------------------------------------------------*/
void debug_cpu_set_dasm_override(const device_config *device, cpu_disassemble_func dasm_override)
{
cpu_debug_data *info = cpu_get_debug_data(device);
if (info != NULL)
info->dasm_override = dasm_override;
}
/***************************************************************************
CORE DEBUGGER HOOKS
***************************************************************************/
/*-------------------------------------------------
debug_cpu_start_hook - the CPU execution
system calls this hook before beginning
execution for the given CPU
-------------------------------------------------*/
void debug_cpu_start_hook(const device_config *device, attotime endtime)
{
debugcpu_private *global = device->machine->debugcpu_data;
cpu_debug_data *info = cpu_get_debug_data(device);
assert((device->machine->debug_flags & DEBUG_FLAG_ENABLED) != 0);
/* stash a pointer to the current live CPU */
assert(global->livecpu == NULL);
global->livecpu = device;
/* update the target execution end time */
info->endexectime = endtime;
/* if we're running, do some periodic updating */
if (global->execution_state != EXECUTION_STATE_STOPPED)
{
/* check for periodic updates */
if (device == global->visiblecpu && osd_ticks() > global->last_periodic_update_time + osd_ticks_per_second()/4)
{
debug_view_update_all(device->machine);
debug_view_flush_updates(device->machine);
global->last_periodic_update_time = osd_ticks();
}
/* check for pending breaks */
else if (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 ((info->flags & DEBUG_FLAG_STOP_VBLANK) != 0)
{
global->execution_state = EXECUTION_STATE_STOPPED;
debug_console_printf(device->machine, "Stopped at VBLANK\n");
}
/* check for debug keypresses */
else if (ui_input_pressed(device->machine, IPT_UI_DEBUG_BREAK))
debug_cpu_halt_on_next_instruction(global->visiblecpu, "User-initiated break\n");
}
}
/* recompute the debugging mode */
compute_debug_flags(device);
}
/*-------------------------------------------------
debug_cpu_stop_hook - the CPU execution
system calls this hook when ending execution
for the given CPU
-------------------------------------------------*/
void debug_cpu_stop_hook(const device_config *device)
{
debugcpu_private *global = device->machine->debugcpu_data;
cpu_debug_data *info = cpu_get_debug_data(device);
assert(global->livecpu == device);
/* if we're stopping on a context switch, handle it now */
if (info->flags & DEBUG_FLAG_STOP_CONTEXT)
{
global->execution_state = EXECUTION_STATE_STOPPED;
reset_transient_flags(device->machine);
}
/* clear the live CPU */
global->livecpu = NULL;
}
/*-------------------------------------------------
debug_cpu_interrupt_hook - called when an
interrupt is acknowledged
-------------------------------------------------*/
void debug_cpu_interrupt_hook(const device_config *device, int irqline)
{
debugcpu_private *global = device->machine->debugcpu_data;
cpu_debug_data *info = cpu_get_debug_data(device);
/* see if this matches a pending interrupt request */
if (info != NULL && (info->flags & DEBUG_FLAG_STOP_INTERRUPT) != 0 && (info->stopirq == -1 || info->stopirq == irqline))
{
global->execution_state = EXECUTION_STATE_STOPPED;
debug_console_printf(device->machine, "Stopped on interrupt (CPU '%s', IRQ %d)\n", device->tag, irqline);
compute_debug_flags(device);
}
}
/*-------------------------------------------------
debug_cpu_exception_hook - called when an
exception is generated
-------------------------------------------------*/
void debug_cpu_exception_hook(const device_config *device, int exception)
{
debugcpu_private *global = device->machine->debugcpu_data;
cpu_debug_data *info = cpu_get_debug_data(device);
/* see if this matches a pending interrupt request */
if ((info->flags & DEBUG_FLAG_STOP_EXCEPTION) != 0 && (info->stopexception == -1 || info->stopexception == exception))
{
global->execution_state = EXECUTION_STATE_STOPPED;
debug_console_printf(device->machine, "Stopped on exception (CPU '%s', exception %d)\n", device->tag, exception);
compute_debug_flags(device);
}
}
/*-------------------------------------------------
debug_cpu_instruction_hook - called by the
CPU cores before executing each instruction
-------------------------------------------------*/
void debug_cpu_instruction_hook(const device_config *device, offs_t curpc)
{
debugcpu_private *global = device->machine->debugcpu_data;
cpu_debug_data *info = cpu_get_debug_data(device);
/* note that we are in the debugger code */
global->within_instruction_hook = TRUE;
/* update the history */
info->pc_history[info->pc_history_index++ % DEBUG_HISTORY_SIZE] = curpc;
/* are we tracing? */
if (info->flags & DEBUG_FLAG_TRACING_ANY)
perform_trace(info);
/* per-instruction hook? */
if (global->execution_state != EXECUTION_STATE_STOPPED && (info->flags & DEBUG_FLAG_HOOKED) != 0 && (*info->instrhook)(device, curpc))
global->execution_state = EXECUTION_STATE_STOPPED;
/* handle single stepping */
if (global->execution_state != EXECUTION_STATE_STOPPED && (info->flags & DEBUG_FLAG_STEPPING_ANY) != 0)
{
/* is this an actual step? */
if (info->stepaddr == ~0 || curpc == info->stepaddr)
{
/* decrement the count and reset the breakpoint */
info->stepsleft--;
info->stepaddr = ~0;
/* if we hit 0, stop */
if (info->stepsleft == 0)
global->execution_state = EXECUTION_STATE_STOPPED;
/* update every 100 steps until we are within 200 of the end */
else if ((info->flags & DEBUG_FLAG_STEPPING_OUT) == 0 && (info->stepsleft < 200 || info->stepsleft % 100 == 0))
{
debug_view_update_all(device->machine);
debug_view_flush_updates(device->machine);
debugger_refresh_display(device->machine);
}
}
}
/* handle breakpoints */
if (global->execution_state != EXECUTION_STATE_STOPPED && (info->flags & (DEBUG_FLAG_STOP_TIME | DEBUG_FLAG_STOP_PC | DEBUG_FLAG_LIVE_BP)) != 0)
{
/* see if we hit a target time */
if ((info->flags & DEBUG_FLAG_STOP_TIME) != 0 && attotime_compare(timer_get_time(device->machine), info->stoptime) >= 0)
{
debug_console_printf(device->machine, "Stopped at time interval %.1g\n", attotime_to_double(timer_get_time(device->machine)));
global->execution_state = EXECUTION_STATE_STOPPED;
}
/* check the temp running breakpoint and break if we hit it */
else if ((info->flags & DEBUG_FLAG_STOP_PC) != 0 && info->stopaddr == curpc)
{
debug_console_printf(device->machine, "Stopped at temporary breakpoint %X on CPU '%s'\n", info->stopaddr, device->tag);
global->execution_state = EXECUTION_STATE_STOPPED;
}
/* check for execution breakpoints */
else if ((info->flags & DEBUG_FLAG_LIVE_BP) != 0)
breakpoint_check(device->machine, info, 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(device->machine);
global->breakcpu = NULL;
/* remember the last visible CPU in the debugger */
global->visiblecpu = device;
/* update all views */
debug_view_update_all(device->machine);
debugger_refresh_display(device->machine);
/* wait for the debugger; during this time, disable sound output */
sound_mute(device->machine, TRUE);
while (global->execution_state == EXECUTION_STATE_STOPPED)
{
/* flush any pending updates before waiting again */
debug_view_flush_updates(device->machine);
/* clear the memory modified flag and wait */
global->memory_modified = FALSE;
osd_wait_for_debugger(device, firststop);
firststop = FALSE;
/* if something modified memory, update the screen */
if (global->memory_modified)
{
debug_view_update_type(device->machine, DVT_DISASSEMBLY);
debugger_refresh_display(device->machine);
}
/* check for commands in the source file */
process_source_file(device->machine);
/* if an event got scheduled, resume */
if (mame_is_scheduled_event_pending(device->machine))
global->execution_state = EXECUTION_STATE_RUNNING;
}
sound_mute(device->machine, FALSE);
/* remember the last visible CPU in the debugger */
global->visiblecpu = device;
}
/* handle step out/over on the instruction we are about to execute */
if ((info->flags & (DEBUG_FLAG_STEPPING_OVER | DEBUG_FLAG_STEPPING_OUT)) != 0 && info->stepaddr == ~0)
prepare_for_step_overout(info);
/* no longer in debugger code */
global->within_instruction_hook = FALSE;
}
/*-------------------------------------------------
debug_cpu_memory_read_hook - the memory system
calls this hook when watchpoints are enabled
and a memory read happens
-------------------------------------------------*/
void debug_cpu_memory_read_hook(const address_space *space, offs_t address, UINT64 mem_mask)
{
cpu_debug_data *info = cpu_get_debug_data(space->cpu);
/* check watchpoints */
watchpoint_check(space, WATCHPOINT_READ, address, 0, mem_mask);
/* check hotspots */
if (info->hotspots != NULL)
check_hotspots(space, address);
}
/*-------------------------------------------------
debug_cpu_memory_write_hook - the memory
system calls this hook when watchpoints are
enabled and a memory write happens
-------------------------------------------------*/
void debug_cpu_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
***************************************************************************/
/*-------------------------------------------------
debug_cpu_halt_on_next_instruction - halt in
the debugger on the next instruction
-------------------------------------------------*/
void debug_cpu_halt_on_next_instruction(const device_config *device, const char *fmt, ...)
{
debugcpu_private *global = device->machine->debugcpu_data;
cpu_debug_data *info = cpu_get_debug_data(device);
va_list arg;
/* if something is pending on this CPU already, ignore this request */
if (info != NULL && device == global->breakcpu)
return;
/* output the message to the console */
va_start(arg, fmt);
debug_console_vprintf(device->machine, fmt, arg);
va_end(arg);
/* if we are live, stop now, otherwise note that we want to break there */
if (device == global->livecpu)
{
global->execution_state = EXECUTION_STATE_STOPPED;
if (global->livecpu != NULL)
compute_debug_flags(global->livecpu);
}
else
global->breakcpu = device;
}
/*-------------------------------------------------
debug_cpu_ignore_cpu - ignore/observe a given
CPU
-------------------------------------------------*/
void debug_cpu_ignore_cpu(const device_config *device, int ignore)
{
debugcpu_private *global = device->machine->debugcpu_data;
cpu_debug_data *info = cpu_get_debug_data(device);
if (ignore)
info->flags &= ~DEBUG_FLAG_OBSERVING;
else
info->flags |= DEBUG_FLAG_OBSERVING;
if (device == global->livecpu && ignore)
debug_cpu_next_cpu(device->machine);
}
/*-------------------------------------------------
debug_cpu_single_step - single step the visible
CPU past the requested number of instructions
-------------------------------------------------*/
void debug_cpu_single_step(running_machine *machine, int numsteps)
{
debugcpu_private *global = machine->debugcpu_data;
cpu_debug_data *info = cpu_get_debug_data(global->visiblecpu);
assert(info != NULL);
info->stepsleft = numsteps;
info->stepaddr = ~0;
info->flags |= DEBUG_FLAG_STEPPING;
global->execution_state = EXECUTION_STATE_RUNNING;
}
/*-------------------------------------------------
debug_cpu_single_step_over - single step the
visible over the requested number of
instructions
-------------------------------------------------*/
void debug_cpu_single_step_over(running_machine *machine, int numsteps)
{
debugcpu_private *global = machine->debugcpu_data;
cpu_debug_data *info = cpu_get_debug_data(global->visiblecpu);
assert(info != NULL);
info->stepsleft = numsteps;
info->stepaddr = ~0;
info->flags |= DEBUG_FLAG_STEPPING_OVER;
global->execution_state = EXECUTION_STATE_RUNNING;
}
/*-------------------------------------------------
debug_cpu_single_step_out - single step the
visible CPU out of the current function
-------------------------------------------------*/
void debug_cpu_single_step_out(running_machine *machine)
{
debugcpu_private *global = machine->debugcpu_data;
cpu_debug_data *info = cpu_get_debug_data(global->visiblecpu);
assert(info != NULL);
info->stepsleft = 100;
info->stepaddr = ~0;
info->flags |= DEBUG_FLAG_STEPPING_OUT;
global->execution_state = EXECUTION_STATE_RUNNING;
}
/*-------------------------------------------------
debug_cpu_go - execute the visible CPU until
it hits the given address
-------------------------------------------------*/
void debug_cpu_go(running_machine *machine, offs_t targetpc)
{
debugcpu_private *global = machine->debugcpu_data;
cpu_debug_data *info = cpu_get_debug_data(global->visiblecpu);
assert(info != NULL);
info->stopaddr = targetpc;
info->flags |= DEBUG_FLAG_STOP_PC;
global->execution_state = EXECUTION_STATE_RUNNING;
}
/*-------------------------------------------------
debug_cpu_go_vblank - execute until the next
VBLANK
-------------------------------------------------*/
void debug_cpu_go_vblank(running_machine *machine)
{
debugcpu_private *global = machine->debugcpu_data;
cpu_debug_data *info = cpu_get_debug_data(global->visiblecpu);
assert(info != NULL);
global->vblank_occurred = FALSE;
info->flags |= DEBUG_FLAG_STOP_VBLANK;
global->execution_state = EXECUTION_STATE_RUNNING;
}
/*-------------------------------------------------
debug_cpu_go_interrupt - execute until the
specified interrupt fires on the visible CPU
-------------------------------------------------*/
void debug_cpu_go_interrupt(running_machine *machine, int irqline)
{
debugcpu_private *global = machine->debugcpu_data;
cpu_debug_data *info = cpu_get_debug_data(global->visiblecpu);
assert(info != NULL);
info->stopirq = irqline;
info->flags |= DEBUG_FLAG_STOP_INTERRUPT;
global->execution_state = EXECUTION_STATE_RUNNING;
}
/*-------------------------------------------------
debug_cpu_go_exception - execute until the
specified exception fires on the visible CPU
-------------------------------------------------*/
void debug_cpu_go_exception(running_machine *machine, int exception)
{
debugcpu_private *global = machine->debugcpu_data;
cpu_debug_data *info = cpu_get_debug_data(global->visiblecpu);
assert(info != NULL);
info->stopexception = exception;
info->flags |= DEBUG_FLAG_STOP_EXCEPTION;
global->execution_state = EXECUTION_STATE_RUNNING;
}
/*-------------------------------------------------
debug_cpu_go_milliseconds - execute until the
specified delay elapses
-------------------------------------------------*/
void debug_cpu_go_milliseconds(running_machine *machine, UINT64 milliseconds)
{
debugcpu_private *global = machine->debugcpu_data;
cpu_debug_data *info = cpu_get_debug_data(global->visiblecpu);
assert(info != NULL);
info->stoptime = attotime_add(timer_get_time(machine), ATTOTIME_IN_MSEC(milliseconds));
info->flags |= DEBUG_FLAG_STOP_TIME;
global->execution_state = EXECUTION_STATE_RUNNING;
}
/*-------------------------------------------------
debug_cpu_next_cpu - execute until we hit
the next CPU
-------------------------------------------------*/
void debug_cpu_next_cpu(running_machine *machine)
{
debugcpu_private *global = machine->debugcpu_data;
cpu_debug_data *info = cpu_get_debug_data(global->visiblecpu);
assert(info != NULL);
info->flags |= DEBUG_FLAG_STOP_CONTEXT;
global->execution_state = EXECUTION_STATE_RUNNING;
}
/***************************************************************************
BREAKPOINTS
***************************************************************************/
/*-------------------------------------------------
debug_cpu_breakpoint_set - set a new
breakpoint, returning its index
-------------------------------------------------*/
int debug_cpu_breakpoint_set(const device_config *device, offs_t address, parsed_expression *condition, const char *action)
{
debugcpu_private *global = device->machine->debugcpu_data;
cpu_debug_data *info = cpu_get_debug_data(device);
debug_cpu_breakpoint *bp;
assert_always(device != NULL, "debug_cpu_breakpoint_set() called with invalid cpu!");
/* allocate breakpoint */
bp = alloc_or_die(debug_cpu_breakpoint);
bp->index = global->bpindex++;
bp->enabled = TRUE;
bp->address = address;
bp->condition = condition;
bp->action = NULL;
if (action != NULL)
{
bp->action = alloc_array_or_die(char, strlen(action) + 1);
strcpy(bp->action, action);
}
/* hook us in */
bp->next = info->bplist;
info->bplist = bp;
/* ensure the live breakpoint flag is set */
breakpoint_update_flags(info);
return bp->index;
}
/*-------------------------------------------------
debug_cpu_breakpoint_clear - clear a
breakpoint by index
-------------------------------------------------*/
int debug_cpu_breakpoint_clear(running_machine *machine, int bpnum)
{
debug_cpu_breakpoint *bp, *pbp;
const device_config *cpu;
/* loop over CPUs and find the requested breakpoint */
for (cpu = machine->cpu[0]; cpu != NULL; cpu = cpu->typenext)
{
cpu_debug_data *info = cpu_get_debug_data(cpu);
for (pbp = NULL, bp = info->bplist; bp != NULL; pbp = bp, bp = bp->next)
if (bp->index == bpnum)
{
/* unlink us from the list */
if (pbp == NULL)
info->bplist = bp->next;
else
pbp->next = bp->next;
/* free the memory */
if (bp->condition != NULL)
expression_free(bp->condition);
if (bp->action != NULL)
free(bp->action);
free(bp);
/* update the flags */
breakpoint_update_flags(info);
return TRUE;
}
}
/* we didn't find it; return an error */
return FALSE;
}
/*-------------------------------------------------
debug_cpu_breakpoint_enable - enable/disable
a breakpoint by index
-------------------------------------------------*/
int debug_cpu_breakpoint_enable(running_machine *machine, int bpnum, int enable)
{
debug_cpu_breakpoint *bp;
const device_config *cpu;
/* loop over CPUs and find the requested breakpoint */
for (cpu = machine->cpu[0]; cpu != NULL; cpu = cpu->typenext)
{
cpu_debug_data *info = cpu_get_debug_data(cpu);
for (bp = info->bplist; bp != NULL; bp = bp->next)
if (bp->index == bpnum)
{
bp->enabled = (enable != 0);
breakpoint_update_flags(info);
return TRUE;
}
}
return FALSE;
}
/***************************************************************************
WATCHPOINTS
***************************************************************************/
/*-------------------------------------------------
debug_cpu_watchpoint_set - set a new
watchpoint, returning its index
-------------------------------------------------*/
int debug_cpu_watchpoint_set(const address_space *space, int type, offs_t address, offs_t length, parsed_expression *condition, const char *action)
{
debugcpu_private *global = space->machine->debugcpu_data;
cpu_debug_data *info = cpu_get_debug_data(space->cpu);
debug_cpu_watchpoint *wp = alloc_or_die(debug_cpu_watchpoint);
/* fill in the structure */
wp->index = global->wpindex++;
wp->enabled = TRUE;
wp->type = type;
wp->address = memory_address_to_byte(space, address) & space->bytemask;
wp->length = memory_address_to_byte(space, length);
wp->condition = condition;
wp->action = NULL;
if (action != NULL)
{
wp->action = alloc_array_or_die(char, strlen(action) + 1);
strcpy(wp->action, action);
}
/* hook us in */
wp->next = info->wplist[space->spacenum];
info->wplist[space->spacenum] = wp;
watchpoint_update_flags(space);
return wp->index;
}
/*-------------------------------------------------
debug_cpu_watchpoint_clear - clear a
watchpoint by index
-------------------------------------------------*/
int debug_cpu_watchpoint_clear(running_machine *machine, int wpnum)
{
debug_cpu_watchpoint *wp, *pwp;
const device_config *cpu;
int spacenum;
/* loop over CPUs and find the requested watchpoint */
for (cpu = machine->cpu[0]; cpu != NULL; cpu = cpu->typenext)
{
cpu_debug_data *info = cpu_get_debug_data(cpu);
for (spacenum = 0; spacenum < ADDRESS_SPACES; spacenum++)
for (pwp = NULL, wp = info->wplist[spacenum]; wp != NULL; pwp = wp, wp = wp->next)
if (wp->index == wpnum)
{
/* unlink us from the list */
if (pwp == NULL)
info->wplist[spacenum] = wp->next;
else
pwp->next = wp->next;
/* free the memory */
if (wp->condition != NULL)
expression_free(wp->condition);
if (wp->action != NULL)
free(wp->action);
free(wp);
watchpoint_update_flags(cpu_get_address_space(cpu, spacenum));
return TRUE;
}
}
/* we didn't find it; return an error */
return FALSE;
}
/*-------------------------------------------------
debug_cpu_watchpoint_enable - enable/disable a
watchpoint by index
-------------------------------------------------*/
int debug_cpu_watchpoint_enable(running_machine *machine, int wpnum, int enable)
{
debug_cpu_watchpoint *wp;
const device_config *cpu;
int spacenum;
/* loop over CPUs and address spaces and find the requested watchpoint */
for (cpu = machine->cpu[0]; cpu != NULL; cpu = cpu->typenext)
{
cpu_debug_data *info = cpu_get_debug_data(cpu);
for (spacenum = 0; spacenum < ADDRESS_SPACES; spacenum++)
for (wp = info->wplist[spacenum]; wp != NULL; wp = wp->next)
if (wp->index == wpnum)
{
wp->enabled = (enable != 0);
watchpoint_update_flags(cpu_get_address_space(cpu, spacenum));
return TRUE;
}
}
return FALSE;
}
/***************************************************************************
MISC DEBUGGER FUNCTIONS
***************************************************************************/
/*-------------------------------------------------
debug_cpu_source_script - specifies a debug
command script to execute
-------------------------------------------------*/
void debug_cpu_source_script(running_machine *machine, const char *file)
{
debugcpu_private *global = machine->debugcpu_data;
/* close any existing source file */
if (global->source_file != NULL)
{
fclose(global->source_file);
global->source_file = NULL;
}
/* open a new one if requested */
if (file != NULL)
{
global->source_file = fopen(file, "r");
if (!global->source_file)
{
if (mame_get_phase(machine) == MAME_PHASE_RUNNING)
debug_console_printf(machine, "Cannot open command file '%s'\n", file);
else
fatalerror("Cannot open command file '%s'", file);
}
}
}
/*-------------------------------------------------
debug_cpu_trace - trace execution of a given
CPU
-------------------------------------------------*/
void debug_cpu_trace(const device_config *device, FILE *file, int trace_over, const char *action)
{
cpu_debug_data *info = cpu_get_debug_data(device);
/* close existing files and delete expressions */
if (info->trace.file != NULL)
fclose(info->trace.file);
info->trace.file = NULL;
if (info->trace.action != NULL)
free(info->trace.action);
info->trace.action = NULL;
/* open any new files */
info->trace.file = file;
info->trace.action = NULL;
info->trace.trace_over_target = ~0;
if (action != NULL)
{
info->trace.action = alloc_array_or_die(char, strlen(action) + 1);
strcpy(info->trace.action, action);
}
/* update flags */
if (info->trace.file != NULL)
info->flags |= trace_over ? DEBUG_FLAG_TRACING_OVER : DEBUG_FLAG_TRACING;
else
info->flags &= ~DEBUG_FLAG_TRACING_ANY;
}
/*-------------------------------------------------
debug_cpu_trace_printf - output data into the
given CPU's tracefile, if tracing
-------------------------------------------------*/
void debug_cpu_trace_printf(const device_config *device, const char *fmt, ...)
{
va_list va;
cpu_debug_data *info = cpu_get_debug_data(device);
if (info->trace.file)
{
va_start(va, fmt);
vfprintf(info->trace.file, fmt, va);
va_end(va);
}
}
/*-------------------------------------------------
debug_cpu_set_instruction_hook - set a hook to
be called on each instruction for a given CPU
-------------------------------------------------*/
void debug_cpu_set_instruction_hook(const device_config *device, debug_instruction_hook_func hook)
{
cpu_debug_data *info = cpu_get_debug_data(device);
/* set the hook and also the CPU's flag for fast knowledge of the hook */
info->instrhook = hook;
if (hook != NULL)
info->flags |= DEBUG_FLAG_HOOKED;
else
info->flags &= ~DEBUG_FLAG_HOOKED;
}
/*-------------------------------------------------
debug_cpu_hotspot_track - enable/disable
tracking of hotspots
-------------------------------------------------*/
int debug_cpu_hotspot_track(const device_config *device, int numspots, int threshhold)
{
cpu_debug_data *info = cpu_get_debug_data(device);
/* if we already have tracking info, kill it */
if (info->hotspots)
free(info->hotspots);
info->hotspots = NULL;
/* only start tracking if we have a non-zero count */
if (numspots > 0)
{
/* allocate memory for hotspots */
info->hotspots = alloc_array_or_die(debug_hotspot_entry, numspots);
memset(info->hotspots, 0xff, sizeof(*info->hotspots) * numspots);
/* fill in the info */
info->hotspot_count = numspots;
info->hotspot_threshhold = threshhold;
}
watchpoint_update_flags(cpu_get_address_space(device, ADDRESS_SPACE_PROGRAM));
return TRUE;
}
/***************************************************************************
DEBUGGER MEMORY ACCESSORS
***************************************************************************/
/*-------------------------------------------------
debug_read_byte - return a byte from the
the specified memory space
-------------------------------------------------*/
UINT8 debug_read_byte(const address_space *space, offs_t address, int apply_translation)
{
debugcpu_private *global = space->machine->debugcpu_data;
cpu_debug_data *info = cpu_get_debug_data(space->cpu);
UINT64 custom;
UINT8 result;
/* mask against the logical byte mask */
address &= space->logbytemask;
/* all accesses from this point on are for the debugger */
memory_set_debugger_access(space, global->debugger_access = TRUE);
/* translate if necessary; if not mapped, return 0xff */
if (apply_translation && !debug_cpu_translate(space, TRANSLATE_READ_DEBUG, &address))
result = 0xff;
/* if there is a custom read handler, and it returns TRUE, use that value */
else if (info->read != NULL && (*info->read)(space->cpu, space->spacenum, address, 1, &custom))
result = custom;
/* otherwise, call the byte reading function for the translated address */
else
result = memory_read_byte(space, address);
/* no longer accessing via the debugger */
memory_set_debugger_access(space, global->debugger_access = FALSE);
return result;
}
/*-------------------------------------------------
debug_read_word - return a word from the
specified memory space
-------------------------------------------------*/
UINT16 debug_read_word(const address_space *space, offs_t address, int apply_translation)
{
debugcpu_private *global = space->machine->debugcpu_data;
UINT16 result;
/* mask against the logical byte mask */
address &= space->logbytemask;
/* if this is misaligned read, or if there are no word readers, just read two bytes */
if ((address & 1) != 0)
{
UINT8 byte0 = debug_read_byte(space, address + 0, apply_translation);
UINT8 byte1 = debug_read_byte(space, address + 1, apply_translation);
/* based on the endianness, the result is assembled differently */
if (space->endianness == ENDIANNESS_LITTLE)
result = byte0 | (byte1 << 8);
else
result = byte1 | (byte0 << 8);
}
/* otherwise, this proceeds like the byte case */
else
{
cpu_debug_data *info = cpu_get_debug_data(space->cpu);
UINT64 custom;
/* all accesses from this point on are for the debugger */
memory_set_debugger_access(space, global->debugger_access = TRUE);
/* translate if necessary; if not mapped, return 0xffff */
if (apply_translation && !debug_cpu_translate(space, TRANSLATE_READ_DEBUG, &address))
result = 0xffff;
/* if there is a custom read handler, and it returns TRUE, use that value */
else if (info->read != NULL && (*info->read)(space->cpu, space->spacenum, address, 2, &custom))
result = custom;
/* otherwise, call the byte reading function for the translated address */
else
result = memory_read_word(space, address);
/* no longer accessing via the debugger */
memory_set_debugger_access(space, global->debugger_access = FALSE);
}
return result;
}
/*-------------------------------------------------
debug_read_dword - return a dword from the
specified memory space
-------------------------------------------------*/
UINT32 debug_read_dword(const address_space *space, offs_t address, int apply_translation)
{
debugcpu_private *global = space->machine->debugcpu_data;
UINT32 result;
/* mask against the logical byte mask */
address &= space->logbytemask;
/* if this is misaligned read, or if there are no dword readers, just read two words */
if ((address & 3) != 0)
{
UINT16 word0 = debug_read_word(space, address + 0, apply_translation);
UINT16 word1 = debug_read_word(space, address + 2, apply_translation);
/* based on the endianness, the result is assembled differently */
if (space->endianness == ENDIANNESS_LITTLE)
result = word0 | (word1 << 16);
else
result = word1 | (word0 << 16);
}
/* otherwise, this proceeds like the byte case */
else
{
cpu_debug_data *info = cpu_get_debug_data(space->cpu);
UINT64 custom;
/* all accesses from this point on are for the debugger */
memory_set_debugger_access(space, global->debugger_access = TRUE);
/* translate if necessary; if not mapped, return 0xffffffff */
if (apply_translation && !debug_cpu_translate(space, TRANSLATE_READ_DEBUG, &address))
result = 0xffffffff;
/* if there is a custom read handler, and it returns TRUE, use that value */
else if (info->read != NULL && (*info->read)(space->cpu, space->spacenum, address, 4, &custom))
result = custom;
/* otherwise, call the byte reading function for the translated address */
else
result = memory_read_dword(space, address);
/* no longer accessing via the debugger */
memory_set_debugger_access(space, global->debugger_access = FALSE);
}
return result;
}
/*-------------------------------------------------
debug_read_qword - return a qword from the
specified memory space
-------------------------------------------------*/
UINT64 debug_read_qword(const address_space *space, offs_t address, int apply_translation)
{
debugcpu_private *global = space->machine->debugcpu_data;
UINT64 result;
/* mask against the logical byte mask */
address &= space->logbytemask;
/* if this is misaligned read, or if there are no qword readers, just read two dwords */
if ((address & 7) != 0)
{
UINT32 dword0 = debug_read_dword(space, address + 0, apply_translation);
UINT32 dword1 = debug_read_dword(space, address + 4, apply_translation);
/* based on the endianness, the result is assembled differently */
if (space->endianness == ENDIANNESS_LITTLE)
result = dword0 | ((UINT64)dword1 << 32);
else
result = dword1 | ((UINT64)dword0 << 32);
}
/* otherwise, this proceeds like the byte case */
else
{
cpu_debug_data *info = cpu_get_debug_data(space->cpu);
UINT64 custom;
/* all accesses from this point on are for the debugger */
memory_set_debugger_access(space, global->debugger_access = TRUE);
/* translate if necessary; if not mapped, return 0xffffffffffffffff */
if (apply_translation && !debug_cpu_translate(space, TRANSLATE_READ_DEBUG, &address))
result = ~(UINT64)0;
/* if there is a custom read handler, and it returns TRUE, use that value */
else if (info->read != NULL && (*info->read)(space->cpu, space->spacenum, address, 8, &custom))
result = custom;
/* otherwise, call the byte reading function for the translated address */
else
result = memory_read_qword(space, address);
/* no longer accessing via the debugger */
memory_set_debugger_access(space, global->debugger_access = FALSE);
}
return result;
}
/*-------------------------------------------------
debug_write_byte - write a byte to the
specified memory space
-------------------------------------------------*/
void debug_write_byte(const address_space *space, offs_t address, UINT8 data, int apply_translation)
{
debugcpu_private *global = space->machine->debugcpu_data;
cpu_debug_data *info = cpu_get_debug_data(space->cpu);
/* mask against the logical byte mask */
address &= space->logbytemask;
/* all accesses from this point on are for the debugger */
memory_set_debugger_access(space, global->debugger_access = TRUE);
/* translate if necessary; if not mapped, we're done */
if (apply_translation && !debug_cpu_translate(space, TRANSLATE_WRITE_DEBUG, &address))
;
/* if there is a custom write handler, and it returns TRUE, use that */
else if (info->write != NULL && (*info->write)(space->cpu, space->spacenum, address, 1, data))
;
/* otherwise, call the byte reading function for the translated address */
else
memory_write_byte(space, address, data);
/* no longer accessing via the debugger */
memory_set_debugger_access(space, global->debugger_access = FALSE);
global->memory_modified = TRUE;
}
/*-------------------------------------------------
debug_write_word - write a word to the
specified memory space
-------------------------------------------------*/
void debug_write_word(const address_space *space, offs_t address, UINT16 data, int apply_translation)
{
debugcpu_private *global = space->machine->debugcpu_data;
/* mask against the logical byte mask */
address &= space->logbytemask;
/* if this is a misaligned write, or if there are no word writers, just read two bytes */
if ((address & 1) != 0)
{
if (space->endianness == ENDIANNESS_LITTLE)
{
debug_write_byte(space, address + 0, data >> 0, apply_translation);
debug_write_byte(space, address + 1, data >> 8, apply_translation);
}
else
{
debug_write_byte(space, address + 0, data >> 8, apply_translation);
debug_write_byte(space, address + 1, data >> 0, apply_translation);
}
}
/* otherwise, this proceeds like the byte case */
else
{
cpu_debug_data *info = cpu_get_debug_data(space->cpu);
/* all accesses from this point on are for the debugger */
memory_set_debugger_access(space, global->debugger_access = TRUE);
/* translate if necessary; if not mapped, we're done */
if (apply_translation && !debug_cpu_translate(space, TRANSLATE_WRITE_DEBUG, &address))
;
/* if there is a custom write handler, and it returns TRUE, use that */
else if (info->write != NULL && (*info->write)(space->cpu, space->spacenum, address, 2, data))
;
/* otherwise, call the byte reading function for the translated address */
else
memory_write_word(space, address, data);
/* no longer accessing via the debugger */
memory_set_debugger_access(space, global->debugger_access = FALSE);
global->memory_modified = TRUE;
}
}
/*-------------------------------------------------
debug_write_dword - write a dword to the
specified memory space
-------------------------------------------------*/
void debug_write_dword(const address_space *space, offs_t address, UINT32 data, int apply_translation)
{
debugcpu_private *global = space->machine->debugcpu_data;
/* mask against the logical byte mask */
address &= space->logbytemask;
/* if this is a misaligned write, or if there are no dword writers, just read two words */
if ((address & 3) != 0)
{
if (space->endianness == ENDIANNESS_LITTLE)
{
debug_write_word(space, address + 0, data >> 0, apply_translation);
debug_write_word(space, address + 2, data >> 16, apply_translation);
}
else
{
debug_write_word(space, address + 0, data >> 16, apply_translation);
debug_write_word(space, address + 2, data >> 0, apply_translation);
}
}
/* otherwise, this proceeds like the byte case */
else
{
cpu_debug_data *info = cpu_get_debug_data(space->cpu);
/* all accesses from this point on are for the debugger */
memory_set_debugger_access(space, global->debugger_access = TRUE);
/* translate if necessary; if not mapped, we're done */
if (apply_translation && !debug_cpu_translate(space, TRANSLATE_WRITE_DEBUG, &address))
;
/* if there is a custom write handler, and it returns TRUE, use that */
else if (info->write != NULL && (*info->write)(space->cpu, space->spacenum, address, 4, data))
;
/* otherwise, call the byte reading function for the translated address */
else
memory_write_dword(space, address, data);
/* no longer accessing via the debugger */
memory_set_debugger_access(space, global->debugger_access = FALSE);
global->memory_modified = TRUE;
}
}
/*-------------------------------------------------
debug_write_qword - write a qword to the
specified memory space
-------------------------------------------------*/
void debug_write_qword(const address_space *space, offs_t address, UINT64 data, int apply_translation)
{
debugcpu_private *global = space->machine->debugcpu_data;
/* mask against the logical byte mask */
address &= space->logbytemask;
/* if this is a misaligned write, or if there are no qword writers, just read two dwords */
if ((address & 7) != 0)
{
if (space->endianness == ENDIANNESS_LITTLE)
{
debug_write_dword(space, address + 0, data >> 0, apply_translation);
debug_write_dword(space, address + 4, data >> 32, apply_translation);
}
else
{
debug_write_dword(space, address + 0, data >> 32, apply_translation);
debug_write_dword(space, address + 4, data >> 0, apply_translation);
}
}
/* otherwise, this proceeds like the byte case */
else
{
cpu_debug_data *info = cpu_get_debug_data(space->cpu);
/* all accesses from this point on are for the debugger */
memory_set_debugger_access(space, global->debugger_access = TRUE);
/* translate if necessary; if not mapped, we're done */
if (apply_translation && !debug_cpu_translate(space, TRANSLATE_WRITE_DEBUG, &address))
;
/* if there is a custom write handler, and it returns TRUE, use that */
else if (info->write != NULL && (*info->write)(space->cpu, space->spacenum, address, 8, data))
;
/* otherwise, call the byte reading function for the translated address */
else
memory_write_qword(space, address, data);
/* no longer accessing via the debugger */
memory_set_debugger_access(space, global->debugger_access = FALSE);
global->memory_modified = TRUE;
}
}
/*-------------------------------------------------
debug_read_opcode - read 1,2,4 or 8 bytes at
the given offset from opcode space
-------------------------------------------------*/
UINT64 debug_read_opcode(const address_space *space, offs_t address, int size, int arg)
{
UINT64 result = ~(UINT64)0 & (~(UINT64)0 >> (64 - 8*size)), result2;
debugcpu_private *global = space->machine->debugcpu_data;
cpu_debug_data *info = cpu_get_debug_data(space->cpu);
/* keep in logical range */
address &= space->logbytemask;
/* shortcut if we have a custom routine */
if (info->readop != NULL)
{
UINT64 result;
/* return early if we got the result directly */
memory_set_debugger_access(space, global->debugger_access = TRUE);
if ((*info->readop)(space->cpu, address, size, &result))
{
memory_set_debugger_access(space, global->debugger_access = FALSE);
return result;
}
}
/* if we're bigger than the address bus, break into smaller pieces */
if (size > space->dbits / 8)
{
int halfsize = size / 2;
UINT64 r0 = debug_read_opcode(space, address + 0, halfsize, arg);
UINT64 r1 = debug_read_opcode(space, address + halfsize, halfsize, arg);
if (space->endianness == ENDIANNESS_LITTLE)
return r0 | (r1 << (8 * halfsize));
else
return r1 | (r0 << (8 * halfsize));
}
/* translate to physical first */
if (!debug_cpu_translate(space, TRANSLATE_FETCH_DEBUG, &address))
return result;
/* keep in physical range */
address &= space->bytemask;
switch (space->dbits / 8 * 10 + size)
{
/* dump opcodes in bytes from a byte-sized bus */
case 11:
break;
/* dump opcodes in bytes from a word-sized bus */
case 21:
address ^= (space->endianness == ENDIANNESS_LITTLE) ? BYTE_XOR_LE(0) : BYTE_XOR_BE(0);
break;
/* dump opcodes in words from a word-sized bus */
case 22:
break;
/* dump opcodes in bytes from a dword-sized bus */
case 41:
address ^= (space->endianness == ENDIANNESS_LITTLE) ? BYTE4_XOR_LE(0) : BYTE4_XOR_BE(0);
break;
/* dump opcodes in words from a dword-sized bus */
case 42:
address ^= (space->endianness == ENDIANNESS_LITTLE) ? WORD_XOR_LE(0) : WORD_XOR_BE(0);
break;
/* dump opcodes in dwords from a dword-sized bus */
case 44:
break;
/* dump opcodes in bytes from a qword-sized bus */
case 81:
address ^= (space->endianness == ENDIANNESS_LITTLE) ? BYTE8_XOR_LE(0) : BYTE8_XOR_BE(0);
break;
/* dump opcodes in words from a qword-sized bus */
case 82:
address ^= (space->endianness == ENDIANNESS_LITTLE) ? WORD2_XOR_LE(0) : WORD2_XOR_BE(0);
break;
/* dump opcodes in dwords from a qword-sized bus */
case 84:
address ^= (space->endianness == ENDIANNESS_LITTLE) ? DWORD_XOR_LE(0) : DWORD_XOR_BE(0);
break;
/* dump opcodes in qwords from a qword-sized bus */
case 88:
break;
default:
fatalerror("debug_read_opcode: unknown type = %d", space->dbits / 8 * 10 + size);
break;
}
/* turn on debugger access */
if (!global->debugger_access)
memory_set_debugger_access(space, global->debugger_access = TRUE);
/* switch off the size and handle unaligned accesses */
switch (size)
{
case 1:
result = (arg) ? memory_raw_read_byte(space, address) : memory_decrypted_read_byte(space, address);
break;
case 2:
result = (arg) ? memory_raw_read_word(space, address & ~1) : memory_decrypted_read_word(space, address & ~1);
if ((address & 1) != 0)
{
result2 = (arg) ? memory_raw_read_word(space, (address & ~1) + 2) : memory_decrypted_read_word(space, (address & ~1) + 2);
if (space->endianness == ENDIANNESS_LITTLE)
result = (result >> (8 * (address & 1))) | (result2 << (16 - 8 * (address & 1)));
else
result = (result << (8 * (address & 1))) | (result2 >> (16 - 8 * (address & 1)));
result &= 0xffff;
}
break;
case 4:
result = (arg) ? memory_raw_read_dword(space, address & ~3) : memory_decrypted_read_dword(space, address & ~3);
if ((address & 3) != 0)
{
result2 = (arg) ? memory_raw_read_dword(space, (address & ~3) + 4) : memory_decrypted_read_dword(space, (address & ~3) + 4);
if (space->endianness == ENDIANNESS_LITTLE)
result = (result >> (8 * (address & 3))) | (result2 << (32 - 8 * (address & 3)));
else
result = (result << (8 * (address & 3))) | (result2 >> (32 - 8 * (address & 3)));
result &= 0xffffffff;
}
break;
case 8:
result = (arg) ? memory_raw_read_qword(space, address & ~7) : memory_decrypted_read_qword(space, address & ~7);
if ((address & 7) != 0)
{
result2 = (arg) ? memory_raw_read_qword(space, (address & ~7) + 8) : memory_decrypted_read_qword(space, (address & ~7) + 8);
if (space->endianness == ENDIANNESS_LITTLE)
result = (result >> (8 * (address & 7))) | (result2 << (64 - 8 * (address & 7)));
else
result = (result << (8 * (address & 7))) | (result2 >> (64 - 8 * (address & 7)));
}
break;
}
/* no longer accessing via the debugger */
memory_set_debugger_access(space, global->debugger_access = FALSE);
return result;
}
/***************************************************************************
INTERNAL HELPERS
***************************************************************************/
/*-------------------------------------------------
debug_cpu_exit - free all memory
-------------------------------------------------*/
static void debug_cpu_exit(running_machine *machine)
{
debugcpu_private *global = machine->debugcpu_data;
const device_config *cpu;
int spacenum;
/* loop over all watchpoints and breakpoints to free their memory */
for (cpu = machine->cpu[0]; cpu != NULL; cpu = cpu->typenext)
{
cpu_debug_data *info = cpu_get_debug_data(cpu);
/* close any tracefiles */
if (info->trace.file != NULL)
fclose(info->trace.file);
if (info->trace.action != NULL)
free(info->trace.action);
/* free the symbol table */
if (info->symtable != NULL)
symtable_free(info->symtable);
/* free all breakpoints */
while (info->bplist != NULL)
debug_cpu_breakpoint_clear(machine, info->bplist->index);
/* loop over all address spaces */
for (spacenum = 0; spacenum < ARRAY_LENGTH(info->wplist); spacenum++)
{
/* free all watchpoints */
while (info->wplist[spacenum] != NULL)
debug_cpu_watchpoint_clear(machine, info->wplist[spacenum]->index);
}
}
/* free the global symbol table */
if (global != NULL && global->symtable != NULL)
symtable_free(global->symtable);
}
/*-------------------------------------------------
on_vblank - called when a VBLANK hits
-------------------------------------------------*/
static void on_vblank(const device_config *device, void *param, int vblank_state)
{
/* just set a global flag to be consumed later */
if (vblank_state)
device->machine->debugcpu_data->vblank_occurred = TRUE;
}
/*-------------------------------------------------
reset_transient_flags - reset the transient
flags on all CPUs
-------------------------------------------------*/
static void reset_transient_flags(running_machine *machine)
{
const device_config *cpu;
/* loop over CPUs and reset the transient flags */
for (cpu = machine->cpu[0]; cpu != NULL; cpu = cpu->typenext)
cpu_get_debug_data(cpu)->flags &= ~DEBUG_FLAG_TRANSIENT;
}
/*-------------------------------------------------
compute_debug_flags - compute the global
debug flags for optimal efficiency
-------------------------------------------------*/
static void compute_debug_flags(const device_config *device)
{
cpu_debug_data *info = cpu_get_debug_data(device);
running_machine *machine = device->machine;
debugcpu_private *global = machine->debugcpu_data;
/* clear out all global flags by default */
machine->debug_flags = DEBUG_FLAG_ENABLED;
/* if we are ignoring this CPU, or if events are pending, we're done */
if ((info->flags & DEBUG_FLAG_OBSERVING) == 0 || mame_is_scheduled_event_pending(machine) || mame_is_save_or_load_pending(machine))
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 ((info->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 ((info->flags & DEBUG_FLAG_STOP_TIME) && attotime_compare(info->endexectime, info->stoptime) <= 0)
machine->debug_flags |= DEBUG_FLAG_CALL_HOOK;
}
/*-------------------------------------------------
perform_trace - log to the tracefile the
data for a given instruction
-------------------------------------------------*/
static void perform_trace(cpu_debug_data *info)
{
offs_t pc = cpu_get_pc(info->device);
int offset, count, i;
char buffer[100];
offs_t dasmresult;
/* are we in trace over mode and in a subroutine? */
if ((info->flags & DEBUG_FLAG_TRACING_OVER) != 0 && info->trace.trace_over_target != ~0)
{
if (info->trace.trace_over_target != pc)
return;
info->trace.trace_over_target = ~0;
}
/* check for a loop condition */
for (i = count = 0; i < TRACE_LOOPS; i++)
if (info->trace.history[i] == pc)
count++;
/* if no more than 1 hit, process normally */
if (count <= 1)
{
const address_space *space = cpu_get_address_space(info->device, ADDRESS_SPACE_PROGRAM);
/* if we just finished looping, indicate as much */
if (info->trace.loops != 0)
fprintf(info->trace.file, "\n (loops for %d instructions)\n\n", info->trace.loops);
info->trace.loops = 0;
/* execute any trace actions first */
if (info->trace.action != NULL)
debug_console_execute_command(info->device->machine, info->trace.action, 0);
/* print the address */
offset = sprintf(buffer, "%0*X: ", space->logaddrchars, pc);
/* print the disassembly */
dasmresult = dasm_wrapped(info->device, &buffer[offset], pc);
/* output the result */
fprintf(info->trace.file, "%s\n", buffer);
/* do we need to step the trace over this instruction? */
if ((info->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(info->device, buffer, trace_over_target) & DASMFLAG_LENGTHMASK;
info->trace.trace_over_target = trace_over_target;
}
/* log this PC */
info->trace.nextdex = (info->trace.nextdex + 1) % TRACE_LOOPS;
info->trace.history[info->trace.nextdex] = pc;
}
/* else just count the loop */
else
info->trace.loops++;
}
/*-------------------------------------------------
prepare_for_step_overout - prepare things for
stepping over an instruction
-------------------------------------------------*/
static void prepare_for_step_overout(cpu_debug_data *info)
{
offs_t pc = cpu_get_pc(info->device);
char dasmbuffer[100];
offs_t dasmresult;
/* disassemble the current instruction and get the flags */
dasmresult = dasm_wrapped(info->device, 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(info->device, dasmbuffer, pc) & DASMFLAG_LENGTHMASK;
info->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 ((info->flags & DEBUG_FLAG_STEPPING_OUT) != 0)
{
if ((dasmresult & DASMFLAG_SUPPORTED) != 0 && (dasmresult & DASMFLAG_STEP_OUT) == 0)
info->stepsleft = 100;
else
info->stepsleft = 1;
}
}
/*-------------------------------------------------
process_source_file - executes commands from
a source file
-------------------------------------------------*/
static void process_source_file(running_machine *machine)
{
debugcpu_private *global = machine->debugcpu_data;
/* loop until the file is exhausted or until we are executing again */
while (global->source_file != NULL && global->execution_state == EXECUTION_STATE_STOPPED)
{
char buf[512];
int i;
char *s;
/* stop at the end of file */
if (feof(global->source_file))
{
fclose(global->source_file);
global->source_file = NULL;
return;
}
/* fetch the next line */
memset(buf, 0, sizeof(buf));
fgets(buf, sizeof(buf), global->source_file);
/* strip out comments (text after '//') */
s = strstr(buf, "//");
if (s)
*s = '\0';
/* strip whitespace */
i = (int)strlen(buf);
while((i > 0) && (isspace((UINT8)buf[i-1])))
buf[--i] = '\0';
/* execute the command */
if (buf[0])
debug_console_execute_command(machine, buf, 1);
}
}
/*-------------------------------------------------
breakpoint_update_flags - update the CPU's
breakpoint flags
-------------------------------------------------*/
static void breakpoint_update_flags(cpu_debug_data *info)
{
debugcpu_private *global = info->device->machine->debugcpu_data;
debug_cpu_breakpoint *bp;
/* see if there are any enabled breakpoints */
info->flags &= ~DEBUG_FLAG_LIVE_BP;
for (bp = info->bplist; bp != NULL; bp = bp->next)
if (bp->enabled)
{
info->flags |= DEBUG_FLAG_LIVE_BP;
break;
}
/* push the flags out globally */
if (global->livecpu != NULL)
compute_debug_flags(global->livecpu);
}
/*-------------------------------------------------
breakpoint_check - check the breakpoints for
a given CPU
-------------------------------------------------*/
static void breakpoint_check(running_machine *machine, cpu_debug_data *info, offs_t pc)
{
debugcpu_private *global = machine->debugcpu_data;
debug_cpu_breakpoint *bp;
UINT64 result;
/* see if we match */
for (bp = info->bplist; bp != NULL; bp = bp->next)
if (bp->enabled && bp->address == pc)
/* if we do, evaluate the condition */
if (bp->condition == NULL || (expression_execute(bp->condition, &result) == EXPRERR_NONE && result != 0))
{
/* halt in the debugger by default */
global->execution_state = EXECUTION_STATE_STOPPED;
/* if we hit, evaluate the action */
if (bp->action != NULL)
debug_console_execute_command(machine, bp->action, 0);
/* print a notification, unless the action made us go again */
if (global->execution_state == EXECUTION_STATE_STOPPED)
debug_console_printf(machine, "Stopped at breakpoint %X\n", bp->index);
break;
}
}
/*-------------------------------------------------
watchpoint_update_flags - update the CPU's
watchpoint flags
-------------------------------------------------*/
static void watchpoint_update_flags(const address_space *space)
{
const cpu_debug_data *info = cpu_get_debug_data(space->cpu);
debug_cpu_watchpoint *wp;
int enablewrite = FALSE;
int enableread = FALSE;
/* if hotspots are enabled, turn on all reads */
if (info->hotspots != NULL)
enableread = TRUE;
/* see if there are any enabled breakpoints */
for (wp = info->wplist[space->spacenum]; wp != NULL; wp = wp->next)
if (wp->enabled)
{
if (wp->type & WATCHPOINT_READ)
enableread = TRUE;
if (wp->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
-------------------------------------------------*/
static void watchpoint_check(const address_space *space, int type, offs_t address, UINT64 value_to_write, UINT64 mem_mask)
{
const cpu_debug_data *info = cpu_get_debug_data(space->cpu);
debugcpu_private *global = space->machine->debugcpu_data;
debug_cpu_watchpoint *wp;
offs_t size = 0;
UINT64 result;
/* 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 (wp = info->wplist[space->spacenum]; wp != NULL; wp = wp->next)
if (wp->enabled && (wp->type & type) != 0 && address + size > wp->address && address < wp->address + wp->length)
/* if we do, evaluate the condition */
if (wp->condition == NULL || (expression_execute(wp->condition, &result) == EXPRERR_NONE && result != 0))
{
/* halt in the debugger by default */
global->execution_state = EXECUTION_STATE_STOPPED;
/* if we hit, evaluate the action */
if (wp->action != NULL)
debug_console_execute_command(space->machine, wp->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"
};
char buffer[100];
if (type & WATCHPOINT_WRITE)
{
sprintf(buffer, "Stopped at watchpoint %X writing %s to %08X (PC=%X)", wp->index, sizes[size], memory_byte_to_address(space, address), cpu_get_pc(space->cpu));
if (value_to_write >> 32)
sprintf(&buffer[strlen(buffer)], " (data=%X%08X)", (UINT32)(value_to_write >> 32), (UINT32)value_to_write);
else
sprintf(&buffer[strlen(buffer)], " (data=%X)", (UINT32)value_to_write);
}
else
sprintf(buffer, "Stopped at watchpoint %X reading %s from %08X (PC=%X)", wp->index, sizes[size], memory_byte_to_address(space, address), cpu_get_pc(space->cpu));
debug_console_printf(space->machine, "%s\n", buffer);
compute_debug_flags(space->cpu);
}
break;
}
global->within_instruction_hook = FALSE;
}
/*-------------------------------------------------
check_hotspots - check for
hotspots on a memory read access
-------------------------------------------------*/
static void check_hotspots(const address_space *space, offs_t address)
{
cpu_debug_data *info = cpu_get_debug_data(space->cpu);
offs_t pc = cpu_get_pc(space->cpu);
int hotindex;
/* see if we have a match in our list */
for (hotindex = 0; hotindex < info->hotspot_count; hotindex++)
if (info->hotspots[hotindex].access == address && info->hotspots[hotindex].pc == pc && info->hotspots[hotindex].space == space)
break;
/* if we didn't find any, make a new entry */
if (hotindex == info->hotspot_count)
{
/* if the bottom of the list is over the threshhold, print it */
debug_hotspot_entry *spot = &info->hotspots[info->hotspot_count - 1];
if (spot->count > info->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(&info->hotspots[1], &info->hotspots[0], sizeof(info->hotspots[0]) * (info->hotspot_count - 1));
info->hotspots[0].access = address;
info->hotspots[0].pc = pc;
info->hotspots[0].space = space;
info->hotspots[0].count = 1;
}
/* if we did find one, increase the count and move it to the top */
else
{
info->hotspots[hotindex].count++;
if (hotindex != 0)
{
debug_hotspot_entry temp = info->hotspots[hotindex];
memmove(&info->hotspots[1], &info->hotspots[0], sizeof(info->hotspots[0]) * hotindex);
info->hotspots[0] = temp;
}
}
}
/*-------------------------------------------------
dasm_wrapped - wraps calls to the disassembler
by fetching the opcode bytes to a temporary
buffer and then disassembling them
-------------------------------------------------*/
static UINT32 dasm_wrapped(const device_config *device, char *buffer, offs_t pc)
{
const address_space *space = cpu_get_address_space(device, ADDRESS_SPACE_PROGRAM);
int maxbytes = cpu_get_max_opcode_bytes(device);
UINT8 opbuf[64], argbuf[64];
offs_t pcbyte;
int numbytes;
/* fetch the bytes up to the maximum */
pcbyte = memory_address_to_byte(space, pc) & space->bytemask;
for (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);
}
return debug_cpu_disassemble(device, buffer, pc, opbuf, argbuf);
}
/***************************************************************************
EXPRESSION HANDLERS
***************************************************************************/
/*-------------------------------------------------
expression_cpu_index - return the CPU index
based on a case insensitive tag search
-------------------------------------------------*/
static const device_config *expression_cpu_index(running_machine *machine, const char *tag)
{
int index;
for (index = 0; index < ARRAY_LENGTH(machine->cpu); index++)
if (machine->cpu[index] != NULL && mame_stricmp(machine->cpu[index]->tag, tag) == 0)
return machine->cpu[index];
return NULL;
}
/*-------------------------------------------------
expression_read_memory - read 1,2,4 or 8 bytes
at the given offset in the given address
space
-------------------------------------------------*/
static UINT64 expression_read_memory(void *param, const char *name, int space, UINT32 address, int size)
{
running_machine *machine = (running_machine *)param;
const device_config *cpu = NULL;
switch (space)
{
case EXPSPACE_PROGRAM:
case EXPSPACE_DATA:
case EXPSPACE_IO:
if (name != NULL)
cpu = expression_cpu_index(machine, name);
if (cpu == NULL)
cpu = debug_cpu_get_visible_cpu(machine);
return expression_read_address_space(cpu_get_address_space(cpu, ADDRESS_SPACE_PROGRAM + (space - EXPSPACE_PROGRAM)), address, size);
case EXPSPACE_OPCODE:
case EXPSPACE_RAMWRITE:
if (name != NULL)
cpu = expression_cpu_index(machine, name);
if (cpu == NULL)
cpu = debug_cpu_get_visible_cpu(machine);
return expression_read_program_direct(cpu_get_address_space(cpu, ADDRESS_SPACE_PROGRAM), (space == EXPSPACE_OPCODE), address, size);
case EXPSPACE_EEPROM:
return expression_read_eeprom(machine, address, size);
case EXPSPACE_REGION:
if (name == NULL)
break;
return expression_read_memory_region(machine, name, address, size);
}
return ~(UINT64)0 >> (64 - 8*size);
}
/*-------------------------------------------------
expression_read_address_space - read memory
from a specific CPU's address space
-------------------------------------------------*/
static UINT64 expression_read_address_space(const address_space *space, offs_t address, int size)
{
UINT64 result = ~(UINT64)0 >> (64 - 8*size);
if (space != NULL)
{
/* adjust the address into a byte address */
address = memory_address_to_byte(space, address);
/* switch contexts and do the read */
switch (size)
{
case 1: result = debug_read_byte(space, address, TRUE); break;
case 2: result = debug_read_word(space, address, TRUE); break;
case 4: result = debug_read_dword(space, address, TRUE); break;
case 8: result = debug_read_qword(space, address, TRUE); break;
}
}
return result;
}
/*-------------------------------------------------
expression_read_program_direct - read memory
directly from an opcode or RAM pointer
-------------------------------------------------*/
static UINT64 expression_read_program_direct(const address_space *space, int opcode, offs_t address, int size)
{
UINT64 result = ~(UINT64)0 >> (64 - 8*size);
if (space != NULL)
{
UINT8 *base;
/* adjust the address into a byte address, but not if being called recursively */
if ((opcode & 2) == 0)
address = memory_address_to_byte(space, address);
/* call ourself recursively until we are byte-sized */
if (size > 1)
{
int halfsize = size / 2;
UINT64 r0, r1;
/* read each half, from lower address to upper address */
r0 = expression_read_program_direct(space, opcode | 2, address + 0, halfsize);
r1 = expression_read_program_direct(space, opcode | 2, address + halfsize, halfsize);
/* assemble based on the target endianness */
if (space->endianness == ENDIANNESS_LITTLE)
result = r0 | (r1 << (8 * halfsize));
else
result = r1 | (r0 << (8 * halfsize));
}
/* handle the byte-sized final requests */
else
{
/* lowmask specified which address bits are within the databus width */
offs_t lowmask = space->dbits / 8 - 1;
/* get the base of memory, aligned to the address minus the lowbits */
if (opcode & 1)
base = (UINT8 *)memory_decrypted_read_ptr(space, address & ~lowmask);
else
base = (UINT8 *)memory_get_read_ptr(space, address & ~lowmask);
/* if we have a valid base, return the appropriate byte */
if (base != NULL)
{
if (space->endianness == ENDIANNESS_LITTLE)
result = base[BYTE8_XOR_LE(address) & lowmask];
else
result = base[BYTE8_XOR_BE(address) & lowmask];
}
}
}
return result;
}
/*-------------------------------------------------
expression_read_memory_region - read memory
from a memory region
-------------------------------------------------*/
static UINT64 expression_read_memory_region(running_machine *machine, const char *rgntag, offs_t address, int size)
{
UINT8 *base = memory_region(machine, rgntag);
UINT64 result = ~(UINT64)0 >> (64 - 8*size);
/* make sure we get a valid base before proceeding */
if (base != NULL)
{
UINT32 length = memory_region_length(machine, rgntag);
UINT32 flags = memory_region_flags(machine, rgntag);
/* call ourself recursively until we are byte-sized */
if (size > 1)
{
int halfsize = size / 2;
UINT64 r0, r1;
/* read each half, from lower address to upper address */
r0 = expression_read_memory_region(machine, rgntag, address + 0, halfsize);
r1 = expression_read_memory_region(machine, rgntag, address + halfsize, halfsize);
/* assemble based on the target endianness */
if ((flags & ROMREGION_ENDIANMASK) == ROMREGION_LE)
result = r0 | (r1 << (8 * halfsize));
else
result = r1 | (r0 << (8 * halfsize));
}
/* only process if we're within range */
else if (address < length)
{
/* lowmask specified which address bits are within the databus width */
UINT32 lowmask = (1 << ((flags & ROMREGION_WIDTHMASK) >> 8)) - 1;
base += address & ~lowmask;
/* if we have a valid base, return the appropriate byte */
if ((flags & ROMREGION_ENDIANMASK) == ROMREGION_LE)
result = base[BYTE8_XOR_LE(address) & lowmask];
else
result = base[BYTE8_XOR_BE(address) & lowmask];
}
}
return result;
}
/*-------------------------------------------------
expression_read_eeprom - read EEPROM data
-------------------------------------------------*/
static UINT64 expression_read_eeprom(running_machine *machine, offs_t address, int size)
{
UINT64 result = ~(UINT64)0 >> (64 - 8*size);
UINT32 eelength, eesize;
void *base;
/* make sure we get a valid base before proceeding */
base = eeprom_get_data_pointer(&eelength, &eesize);
if (base != NULL && address < eelength)
{
/* switch off the size */
switch (eesize)
{
case 1: result &= ((UINT8 *)base)[address]; break;
case 2: result &= BIG_ENDIANIZE_INT16(((UINT16 *)base)[address]); break;
}
}
return result;
}
/*-------------------------------------------------
expression_write_memory - write 1,2,4 or 8
bytes at the given offset in the given address
space
-------------------------------------------------*/
static void expression_write_memory(void *param, const char *name, int space, UINT32 address, int size, UINT64 data)
{
running_machine *machine = (running_machine *)param;
const device_config *cpu = NULL;
switch (space)
{
case EXPSPACE_PROGRAM:
case EXPSPACE_DATA:
case EXPSPACE_IO:
if (name != NULL)
cpu = expression_cpu_index(machine, name);
if (cpu == NULL)
cpu = debug_cpu_get_visible_cpu(machine);
expression_write_address_space(cpu_get_address_space(cpu, ADDRESS_SPACE_PROGRAM + (space - EXPSPACE_PROGRAM)), address, size, data);
break;
case EXPSPACE_OPCODE:
case EXPSPACE_RAMWRITE:
if (name != NULL)
cpu = expression_cpu_index(machine, name);
if (cpu == NULL)
cpu = debug_cpu_get_visible_cpu(machine);
expression_write_program_direct(cpu_get_address_space(cpu, ADDRESS_SPACE_PROGRAM), (space == EXPSPACE_OPCODE), address, size, data);
break;
case EXPSPACE_EEPROM:
expression_write_eeprom(machine, address, size, data);
break;
case EXPSPACE_REGION:
if (name == NULL)
break;
expression_write_memory_region(machine, name, address, size, data);
break;
}
}
/*-------------------------------------------------
expression_write_address_space - write memory
to a specific CPU's address space
-------------------------------------------------*/
static void expression_write_address_space(const address_space *space, offs_t address, int size, UINT64 data)
{
if (space != NULL)
{
/* adjust the address into a byte address */
address = memory_address_to_byte(space, address);
/* switch contexts and do the write */
switch (size)
{
case 1: debug_write_byte(space, address, data, TRUE); break;
case 2: debug_write_word(space, address, data, TRUE); break;
case 4: debug_write_dword(space, address, data, TRUE); break;
case 8: debug_write_qword(space, address, data, TRUE); break;
}
}
}
/*-------------------------------------------------
expression_write_program_direct - write memory
directly to an opcode or RAM pointer
-------------------------------------------------*/
static void expression_write_program_direct(const address_space *space, int opcode, offs_t address, int size, UINT64 data)
{
if (space != NULL)
{
debugcpu_private *global = space->machine->debugcpu_data;
UINT8 *base;
/* adjust the address into a byte address, but not if being called recursively */
if ((opcode & 2) == 0)
address = memory_address_to_byte(space, address);
/* call ourself recursively until we are byte-sized */
if (size > 1)
{
int halfsize = size / 2;
UINT64 r0, r1, halfmask;
/* break apart based on the target endianness */
halfmask = ~(UINT64)0 >> (64 - 8 * halfsize);
if (space->endianness == ENDIANNESS_LITTLE)
{
r0 = data & halfmask;
r1 = (data >> (8 * halfsize)) & halfmask;
}
else
{
r0 = (data >> (8 * halfsize)) & halfmask;
r1 = data & halfmask;
}
/* write each half, from lower address to upper address */
expression_write_program_direct(space, opcode | 2, address + 0, halfsize, r0);
expression_write_program_direct(space, opcode | 2, address + halfsize, halfsize, r1);
}
/* handle the byte-sized final case */
else
{
/* lowmask specified which address bits are within the databus width */
offs_t lowmask = space->dbits / 8 - 1;
/* get the base of memory, aligned to the address minus the lowbits */
if (opcode & 1)
base = (UINT8 *)memory_decrypted_read_ptr(space, address & ~lowmask);
else
base = (UINT8 *)memory_get_read_ptr(space, address & ~lowmask);
/* if we have a valid base, write the appropriate byte */
if (base != NULL)
{
if (space->endianness == ENDIANNESS_LITTLE)
base[BYTE8_XOR_LE(address) & lowmask] = data;
else
base[BYTE8_XOR_BE(address) & lowmask] = data;
global->memory_modified = TRUE;
}
}
}
}
/*-------------------------------------------------
expression_write_memory_region - write memory
from a memory region
-------------------------------------------------*/
static void expression_write_memory_region(running_machine *machine, const char *rgntag, offs_t address, int size, UINT64 data)
{
debugcpu_private *global = machine->debugcpu_data;
UINT8 *base = memory_region(machine, rgntag);
/* make sure we get a valid base before proceeding */
if (base != NULL)
{
UINT32 length = memory_region_length(machine, rgntag);
UINT32 flags = memory_region_flags(machine, rgntag);
/* call ourself recursively until we are byte-sized */
if (size > 1)
{
int halfsize = size / 2;
UINT64 r0, r1, halfmask;
/* break apart based on the target endianness */
halfmask = ~(UINT64)0 >> (64 - 8 * halfsize);
if ((flags & ROMREGION_ENDIANMASK) == ROMREGION_LE)
{
r0 = data & halfmask;
r1 = (data >> (8 * halfsize)) & halfmask;
}
else
{
r0 = (data >> (8 * halfsize)) & halfmask;
r1 = data & halfmask;
}
/* write each half, from lower address to upper address */
expression_write_memory_region(machine, rgntag, address + 0, halfsize, r0);
expression_write_memory_region(machine, rgntag, address + halfsize, halfsize, r1);
}
/* only process if we're within range */
else if (address < length)
{
/* lowmask specified which address bits are within the databus width */
UINT32 lowmask = (1 << ((flags & ROMREGION_WIDTHMASK) >> 8)) - 1;
base += address & ~lowmask;
/* if we have a valid base, set the appropriate byte */
if ((flags & ROMREGION_ENDIANMASK) == ROMREGION_LE)
base[BYTE8_XOR_LE(address) & lowmask] = data;
else
base[BYTE8_XOR_BE(address) & lowmask] = data;
global->memory_modified = TRUE;
}
}
}
/*-------------------------------------------------
expression_write_eeprom - write EEPROM data
-------------------------------------------------*/
static void expression_write_eeprom(running_machine *machine, offs_t address, int size, UINT64 data)
{
debugcpu_private *global = machine->debugcpu_data;
UINT32 eelength, eesize;
void *vbase = eeprom_get_data_pointer(&eelength, &eesize);
/* make sure we get a valid base before proceeding */
if (vbase != NULL && address < eelength)
{
UINT64 mask = ~(UINT64)0 >> (64 - 8*size);
/* switch off the size */
switch (eesize)
{
case 1:
{
UINT8 *base = (UINT8 *)vbase + address;
*base = (*base & ~mask) | (data & mask);
break;
}
case 2:
{
UINT16 *base = (UINT16 *)vbase + address;
UINT16 value = BIG_ENDIANIZE_INT16(*base);
value = (value & ~mask) | (data & mask);
*base = BIG_ENDIANIZE_INT16(value);
break;
}
}
global->memory_modified = TRUE;
}
}
/*-------------------------------------------------
expression_validate - validate that the
provided expression references an
appropriate name
-------------------------------------------------*/
static EXPRERR expression_validate(void *param, const char *name, int space)
{
running_machine *machine = (running_machine *)param;
const device_config *cpu = NULL;
switch (space)
{
case EXPSPACE_PROGRAM:
case EXPSPACE_DATA:
case EXPSPACE_IO:
if (name != NULL)
{
cpu = expression_cpu_index(machine, name);
if (cpu == NULL)
return EXPRERR_INVALID_MEMORY_NAME;
}
if (cpu == NULL)
cpu = debug_cpu_get_visible_cpu(machine);
if (cpu_get_address_space(cpu, ADDRESS_SPACE_PROGRAM + (space - EXPSPACE_PROGRAM)) == NULL)
return EXPRERR_NO_SUCH_MEMORY_SPACE;
break;
case EXPSPACE_OPCODE:
case EXPSPACE_RAMWRITE:
if (name != NULL)
{
cpu = expression_cpu_index(machine, name);
if (cpu == NULL)
return EXPRERR_INVALID_MEMORY_NAME;
}
if (cpu == NULL)
cpu = debug_cpu_get_visible_cpu(machine);
if (cpu_get_address_space(cpu, ADDRESS_SPACE_PROGRAM) == NULL)
return EXPRERR_NO_SUCH_MEMORY_SPACE;
break;
case EXPSPACE_EEPROM:
if (name != NULL)
return EXPRERR_INVALID_MEMORY_NAME;
break;
case EXPSPACE_REGION:
if (name == NULL)
return EXPRERR_MISSING_MEMORY_NAME;
if (memory_region(machine, name) == NULL)
return EXPRERR_INVALID_MEMORY_NAME;
break;
}
return EXPRERR_NONE;
}
/***************************************************************************
VARIABLE GETTERS/SETTERS
***************************************************************************/
/*-------------------------------------------------
get_wpaddr - getter callback for the
'wpaddr' symbol
-------------------------------------------------*/
static UINT64 get_wpaddr(void *globalref, void *ref)
{
running_machine *machine = (running_machine *)globalref;
return machine->debugcpu_data->wpaddr;
}
/*-------------------------------------------------
get_wpdata - getter callback for the
'wpdata' symbol
-------------------------------------------------*/
static UINT64 get_wpdata(void *globalref, void *ref)
{
running_machine *machine = (running_machine *)globalref;
return machine->debugcpu_data->wpdata;
}
/*-------------------------------------------------
get_cpunum - getter callback for the
'cpunum' symbol
-------------------------------------------------*/
static UINT64 get_cpunum(void *globalref, void *ref)
{
running_machine *machine = (running_machine *)globalref;
return cpu_get_index(machine->debugcpu_data->visiblecpu);
}
/*-------------------------------------------------
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_beamx - get beam horizontal position
-------------------------------------------------*/
static UINT64 get_beamx(void *globalref, void *ref)
{
const device_config *screen = (const device_config *)ref;
return (screen != NULL) ? video_screen_get_hpos(screen) : 0;
}
/*-------------------------------------------------
get_beamy - get beam vertical position
-------------------------------------------------*/
static UINT64 get_beamy(void *globalref, void *ref)
{
const device_config *screen = (const device_config *)ref;
return (screen != NULL) ? video_screen_get_vpos(screen) : 0;
}
/*-------------------------------------------------
get_frame - get current frame number
-------------------------------------------------*/
static UINT64 get_frame(void *globalref, void *ref)
{
const device_config *screen = (const device_config *)ref;
return (screen != NULL) ? video_screen_get_frame_number(screen) : 0;
}
/*-------------------------------------------------
get_current_pc - getter callback for a CPU's
current instruction pointer
-------------------------------------------------*/
static UINT64 get_current_pc(void *globalref, void *ref)
{
const device_config *device = (const device_config *)globalref;
return cpu_get_pc(device);
}
/*-------------------------------------------------
get_cycles - getter callback for the
'cycles' symbol
-------------------------------------------------*/
static UINT64 get_cycles(void *globalref, void *ref)
{
const device_config *device = (const device_config *)globalref;
return *cpu_get_icount_ptr(device);
}
/*-------------------------------------------------
get_logunmap - getter callback for the logumap
symbols
-------------------------------------------------*/
static UINT64 get_logunmap(void *globalref, void *ref)
{
const address_space *space = (const address_space *)ref;
return (space != NULL) ? memory_get_log_unmap(space) : TRUE;
}
/*-------------------------------------------------
set_logunmap - setter callback for the logumap
symbols
-------------------------------------------------*/
static void set_logunmap(void *globalref, void *ref, UINT64 value)
{
const address_space *space = (const address_space *)ref;
if (space != NULL)
memory_set_log_unmap(space, value ? 1 : 0);
}
/*-------------------------------------------------
get_cpu_reg - getter callback for a CPU's
register symbols
-------------------------------------------------*/
static UINT64 get_cpu_reg(void *globalref, void *ref)
{
const device_config *device = (const device_config *)globalref;
return cpu_get_reg(device, (FPTR)ref);
}
/*-------------------------------------------------
set_cpu_reg - setter callback for a CPU's
register symbols
-------------------------------------------------*/
static void set_cpu_reg(void *globalref, void *ref, UINT64 value)
{
const device_config *device = (const device_config *)globalref;
cpu_set_reg(device, (FPTR)ref, value);
}