diff options
Diffstat (limited to 'src/emu/debug/debugcpu.c')
-rw-r--r-- | src/emu/debug/debugcpu.c | 2195 |
1 files changed, 2195 insertions, 0 deletions
diff --git a/src/emu/debug/debugcpu.c b/src/emu/debug/debugcpu.c new file mode 100644 index 00000000000..d65b07c216e --- /dev/null +++ b/src/emu/debug/debugcpu.c @@ -0,0 +1,2195 @@ +/********************************************************************* + + debugcpu.c + + Debugger CPU/memory interface engine. + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +*********************************************************************/ + +#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 <ctype.h> + + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define NUM_TEMP_VARIABLES 10 + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + + + +/*************************************************************************** + LOCAL VARIABLES +***************************************************************************/ + +FILE *debug_source_file; +symbol_table *global_symtable; + +static const char *address_space_name[] = { "program", "data", "I/O" }; + +static UINT64 wpdata; +static UINT64 wpaddr; + +static int execution_state; +static UINT32 execution_counter; +static int next_index = 1; +static int within_debugger_code = FALSE; +static int last_cpunum; +static int last_stopped_cpunum; +static int steps_until_stop; +static offs_t step_overout_breakpoint; +static int step_overout_cpunum; +static int key_check_counter; +static osd_ticks_t last_periodic_update_time; +static int break_on_vblank; +static int break_on_interrupt; +static int break_on_interrupt_cpunum; +static int break_on_interrupt_irqline; +static int break_on_time; +static attotime break_on_time_target; +static int memory_modified; +static int memory_hook_cpunum; + +static debug_cpu_info debug_cpuinfo[MAX_CPU]; + +static UINT64 tempvar[NUM_TEMP_VARIABLES]; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +void debug_cpu_exit(running_machine *machine); +static void perform_trace(debug_cpu_info *info); +static void prepare_for_step_overout(void); +static void process_source_file(void); +static UINT64 get_wpaddr(UINT32 ref); +static UINT64 get_wpdata(UINT32 ref); +static UINT64 get_cycles(UINT32 ref); +static UINT64 get_cpunum(UINT32 ref); +static UINT64 get_tempvar(UINT32 ref); +static UINT64 get_logunmap(UINT32 ref); +static UINT64 get_beamx(UINT32 ref); +static UINT64 get_beamy(UINT32 ref); +static void set_tempvar(UINT32 ref, UINT64 value); +static void set_logunmap(UINT32 ref, UINT64 value); +static UINT64 get_current_pc(UINT32 ref); +static UINT64 get_cpu_reg(UINT32 ref); +static void set_cpu_reg(UINT32 ref, UINT64 value); +static void check_watchpoints(int cpunum, int spacenum, int type, offs_t address, offs_t size, UINT64 value_to_write); +static void check_hotspots(int cpunum, int spacenum, offs_t address); + + + +/*************************************************************************** + FRONTENDS FOR OLDER FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + mame_debug_init - start up all subsections +-------------------------------------------------*/ + +void mame_debug_init(running_machine *machine) +{ + /* initialize the various subsections */ + debug_cpu_init(machine); + debug_command_init(machine); + debug_console_init(machine); + debug_view_init(machine); + debug_comment_init(machine); + atexit(debug_flush_traces); + add_logerror_callback(machine, debug_errorlog_write_line); +} + + +/*------------------------------------------------- + mame_debug_break - break into the debugger +-------------------------------------------------*/ + +void mame_debug_break(void) +{ + debug_halt_on_next_instruction(); +} + + +/*------------------------------------------------- + mame_debug_is_active - true if the debugger + is currently live +-------------------------------------------------*/ + +int mame_debug_is_active(void) +{ + return within_debugger_code; +} + + +/*************************************************************************** + INITIALIZATION +***************************************************************************/ + +/*------------------------------------------------- + debug_cpu_init - initialize the CPU + information for debugging +-------------------------------------------------*/ + +void debug_cpu_init(running_machine *machine) +{ + int cpunum, spacenum, regnum; + + /* reset globals */ + execution_state = EXECUTION_STATE_STOPPED; + execution_counter = 0; + next_index = 1; + within_debugger_code = FALSE; + last_cpunum = 0; + last_stopped_cpunum = 0; + steps_until_stop = 0; + step_overout_breakpoint = ~0; + step_overout_cpunum = 0; + key_check_counter = 0; + + /* create a global symbol table */ + global_symtable = symtable_alloc(NULL); + + /* add "wpaddr", "wpdata", "cycles", "cpunum", "logunmap" to the global symbol table */ + symtable_add_register(global_symtable, "wpaddr", 0, get_wpaddr, NULL); + symtable_add_register(global_symtable, "wpdata", 0, get_wpdata, NULL); + symtable_add_register(global_symtable, "cycles", 0, get_cycles, NULL); + symtable_add_register(global_symtable, "cpunum", 0, get_cpunum, NULL); + symtable_add_register(global_symtable, "logunmap", ADDRESS_SPACE_PROGRAM, get_logunmap, set_logunmap); + symtable_add_register(global_symtable, "logunmapd", ADDRESS_SPACE_DATA, get_logunmap, set_logunmap); + symtable_add_register(global_symtable, "logunmapi", ADDRESS_SPACE_IO, get_logunmap, set_logunmap); + symtable_add_register(global_symtable, "beamx", 0, get_beamx, NULL); + symtable_add_register(global_symtable, "beamy", 0, get_beamy, 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, regnum, get_tempvar, set_tempvar); + } + + /* reset the CPU info */ + memset(debug_cpuinfo, 0, sizeof(debug_cpuinfo)); + + /* loop over CPUs and build up their info */ + for (cpunum = 0; cpunum < MAX_CPU; cpunum++) + { + cpu_type cputype = Machine->drv->cpu[cpunum].type; + + /* if this is a dummy, stop looking */ + if (cputype == CPU_DUMMY) + break; + + /* reset the PC data */ + debug_cpuinfo[cpunum].valid = 1; + debug_cpuinfo[cpunum].endianness = cpunum_endianness(cpunum); + debug_cpuinfo[cpunum].opwidth = cpunum_min_instruction_bytes(cpunum); + debug_cpuinfo[cpunum].ignoring = 0; + debug_cpuinfo[cpunum].temp_breakpoint_pc = ~0; + + /* fetch the memory accessors */ + debug_cpuinfo[cpunum].translate = (int (*)(int, offs_t *))cpunum_get_info_fct(cpunum, CPUINFO_PTR_TRANSLATE); + debug_cpuinfo[cpunum].read = (int (*)(int, UINT32, int, UINT64 *))cpunum_get_info_fct(cpunum, CPUINFO_PTR_READ); + debug_cpuinfo[cpunum].write = (int (*)(int, UINT32, int, UINT64))cpunum_get_info_fct(cpunum, CPUINFO_PTR_WRITE); + debug_cpuinfo[cpunum].readop = (int (*)(UINT32, int, UINT64 *))cpunum_get_info_fct(cpunum, CPUINFO_PTR_READOP); + + /* allocate a symbol table */ + debug_cpuinfo[cpunum].symtable = symtable_alloc(global_symtable); + + /* add a global symbol for the current instruction pointer */ + symtable_add_register(debug_cpuinfo[cpunum].symtable, "curpc", 0, get_current_pc, 0); + + /* add all registers into it */ + for (regnum = 0; regnum < MAX_REGS; regnum++) + { + const char *str = cpunum_reg_string(cpunum, 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(*str)) + symname[charnum++] = tolower(*str); + symname[charnum] = 0; + + /* add the symbol to the table */ + symtable_add_register(debug_cpuinfo[cpunum].symtable, symname, regnum, get_cpu_reg, set_cpu_reg); + } + + /* loop over address spaces and get info */ + for (spacenum = 0; spacenum < ADDRESS_SPACES; spacenum++) + { + debug_space_info *spaceinfo = &debug_cpuinfo[cpunum].space[spacenum]; + int datawidth = cpunum_databus_width(cpunum, spacenum); + int logwidth = cpunum_logaddr_width(cpunum, spacenum); + int physwidth = cpunum_addrbus_width(cpunum, spacenum); + int addrshift = cpunum_addrbus_shift(cpunum, spacenum); + int pageshift = cpunum_page_shift(cpunum, spacenum); + + if (logwidth == 0) + logwidth = physwidth; + + spaceinfo->databytes = datawidth / 8; + spaceinfo->pageshift = pageshift; + + /* left/right shifts to convert addresses to bytes */ + spaceinfo->addr2byte_lshift = (addrshift < 0) ? -addrshift : 0; + spaceinfo->addr2byte_rshift = (addrshift > 0) ? addrshift : 0; + + /* number of character used to display addresses */ + spaceinfo->physchars = (physwidth + 3) / 4; + spaceinfo->logchars = (logwidth + 3) / 4; + + /* masks to apply to addresses */ + spaceinfo->physaddrmask = (0xfffffffful >> (32 - physwidth)); + spaceinfo->logaddrmask = (0xfffffffful >> (32 - logwidth)); + + /* masks to apply to byte addresses */ + spaceinfo->physbytemask = ((spaceinfo->physaddrmask << spaceinfo->addr2byte_lshift) | ((1 << spaceinfo->addr2byte_lshift) - 1)) >> spaceinfo->addr2byte_rshift; + spaceinfo->logbytemask = ((spaceinfo->logaddrmask << spaceinfo->addr2byte_lshift) | ((1 << spaceinfo->addr2byte_lshift) - 1)) >> spaceinfo->addr2byte_rshift; + } + } + + add_exit_callback(machine, debug_cpu_exit); +} + + +/*------------------------------------------------- + debug_cpu_exit - free all memory +-------------------------------------------------*/ + +void debug_cpu_exit(running_machine *machine) +{ + int cpunum, spacenum; + + /* loop over all watchpoints and breakpoints to free their memory */ + for (cpunum = 0; cpunum < MAX_CPU; cpunum++) + { + /* close any tracefiles */ + if (debug_cpuinfo[cpunum].trace.file) + fclose(debug_cpuinfo[cpunum].trace.file); + if (debug_cpuinfo[cpunum].trace.action) + free(debug_cpuinfo[cpunum].trace.action); + + /* free the symbol table */ + if (debug_cpuinfo[cpunum].symtable) + symtable_free(debug_cpuinfo[cpunum].symtable); + + /* free all breakpoints */ + while (debug_cpuinfo[cpunum].first_bp) + debug_breakpoint_clear(debug_cpuinfo[cpunum].first_bp->index); + + /* loop over all address spaces */ + for (spacenum = 0; spacenum < ADDRESS_SPACES; spacenum++) + { + /* free all watchpoints */ + while (debug_cpuinfo[cpunum].space[spacenum].first_wp) + debug_watchpoint_clear(debug_cpuinfo[cpunum].space[spacenum].first_wp->index); + } + } + + /* free the global symbol table */ + if (global_symtable) + symtable_free(global_symtable); +} + + + +/*************************************************************************** + EXECUTION CONTROL +***************************************************************************/ + +/*------------------------------------------------- + debug_cpu_single_step - single step past the + requested number of instructions +-------------------------------------------------*/ + +void debug_cpu_single_step(int numsteps) +{ + if (!within_debugger_code) + return; + steps_until_stop = numsteps; + execution_state = EXECUTION_STATE_STEP_INTO; +} + + +/*------------------------------------------------- + debug_cpu_single_step_over - single step over + a single instruction +-------------------------------------------------*/ + +void debug_cpu_single_step_over(int numsteps) +{ + if (!within_debugger_code) + return; + steps_until_stop = numsteps; + step_overout_cpunum = cpu_getactivecpu(); + execution_state = EXECUTION_STATE_STEP_OVER; +} + + +/*------------------------------------------------- + debug_cpu_single_step_out - single step out of + the current function +-------------------------------------------------*/ + +void debug_cpu_single_step_out(void) +{ + if (!within_debugger_code) + return; + steps_until_stop = 100; + step_overout_cpunum = cpu_getactivecpu(); + execution_state = EXECUTION_STATE_STEP_OUT; +} + + +/*------------------------------------------------- + debug_cpu_go - resume execution +-------------------------------------------------*/ + +void debug_cpu_go(offs_t targetpc) +{ + if (!within_debugger_code) + return; + execution_state = EXECUTION_STATE_RUNNING; + debug_cpuinfo[cpu_getactivecpu()].temp_breakpoint_pc = targetpc; +} + + +/*------------------------------------------------- + debug_cpu_go_vblank - run until the next + VBLANK +-------------------------------------------------*/ + +void debug_cpu_go_vblank(void) +{ + if (!within_debugger_code) + return; + execution_state = EXECUTION_STATE_RUNNING; + debug_cpuinfo[cpu_getactivecpu()].temp_breakpoint_pc = ~0; + break_on_vblank = 1; +} + + +/*------------------------------------------------- + debug_cpu_go_interrupt - run until the + specified interrupt fires +-------------------------------------------------*/ + +void debug_cpu_go_interrupt(int irqline) +{ + if (!within_debugger_code) + return; + execution_state = EXECUTION_STATE_RUNNING; + debug_cpuinfo[cpu_getactivecpu()].temp_breakpoint_pc = ~0; + break_on_interrupt = 1; + break_on_interrupt_cpunum = cpu_getactivecpu(); + break_on_interrupt_irqline = irqline; +} + + +/*------------------------------------------------- + debug_cpu_go_milliseconds - run until the + specified delay elapses +-------------------------------------------------*/ + +void debug_cpu_go_milliseconds(UINT64 milliseconds) +{ + if (!within_debugger_code) + return; + execution_state = EXECUTION_STATE_RUNNING; + debug_cpuinfo[cpu_getactivecpu()].temp_breakpoint_pc = ~0; + break_on_time = 1; + break_on_time_target = attotime_add( + timer_get_time(), + attotime_make(milliseconds / 1000, (milliseconds % 1000) * (ATTOSECONDS_PER_SECOND / 1000))); +} + + +/*------------------------------------------------- + debug_cpu_next_cpu - execute until we hit + the next CPU +-------------------------------------------------*/ + +void debug_cpu_next_cpu(void) +{ + if (!within_debugger_code) + return; + execution_state = EXECUTION_STATE_NEXT_CPU; +} + + +/*------------------------------------------------- + debug_cpu_ignore_cpu - ignore/observe a given + CPU +-------------------------------------------------*/ + +void debug_cpu_ignore_cpu(int cpunum, int ignore) +{ + debug_cpuinfo[cpunum].ignoring = ignore; + if (!within_debugger_code) + return; + if (cpunum == cpu_getactivecpu() && debug_cpuinfo[cpunum].ignoring) + execution_state = EXECUTION_STATE_NEXT_CPU; +} + + +/*------------------------------------------------- + debug_cpu_trace - trace execution of a given + CPU +-------------------------------------------------*/ + +void debug_cpu_trace(int cpunum, FILE *file, int trace_over, const char *action) +{ + /* close existing files and delete expressions */ + if (debug_cpuinfo[cpunum].trace.file) + fclose(debug_cpuinfo[cpunum].trace.file); + debug_cpuinfo[cpunum].trace.file = NULL; + + if (debug_cpuinfo[cpunum].trace.action) + free(debug_cpuinfo[cpunum].trace.action); + debug_cpuinfo[cpunum].trace.action = NULL; + + /* open any new files */ + debug_cpuinfo[cpunum].trace.file = file; + debug_cpuinfo[cpunum].trace.action = NULL; + if (action) + { + debug_cpuinfo[cpunum].trace.action = malloc(strlen(action) + 1); + if (debug_cpuinfo[cpunum].trace.action) + strcpy(debug_cpuinfo[cpunum].trace.action, action); + } + + /* specify trace over */ + debug_cpuinfo[cpunum].trace.trace_over_target = trace_over ? ~0 : 0; +} + + + +/*************************************************************************** + UTILITIES +***************************************************************************/ + +/*------------------------------------------------- + debug_get_cpu_info - returns the cpu info + block for a given CPU +-------------------------------------------------*/ + +const debug_cpu_info *debug_get_cpu_info(int cpunum) +{ + return &debug_cpuinfo[cpunum]; +} + + +/*------------------------------------------------- + debug_halt_on_next_instruction - halt in + the debugger on the next instruction +-------------------------------------------------*/ + +void debug_halt_on_next_instruction(void) +{ + debug_console_printf("Internal breakpoint\n"); + execution_state = EXECUTION_STATE_STOPPED; +} + + +/*------------------------------------------------- + debug_refresh_display - redraw the current + video display +-------------------------------------------------*/ + +void debug_refresh_display(void) +{ + video_frame_update(); +} + + +/*------------------------------------------------- + debug_get_execution_state - return the + current execution state +-------------------------------------------------*/ + +int debug_get_execution_state(void) +{ + return execution_state; +} + + +/*------------------------------------------------- + debug_get_execution_counter - return the + current execution counter +-------------------------------------------------*/ + +UINT32 debug_get_execution_counter(void) +{ + return execution_counter; +} + + +/*------------------------------------------------- + get_wpaddr - getter callback for the + 'wpaddr' symbol +-------------------------------------------------*/ + +static UINT64 get_wpaddr(UINT32 ref) +{ + return wpaddr; +} + + +/*------------------------------------------------- + get_wpdata - getter callback for the + 'wpdata' symbol +-------------------------------------------------*/ + +static UINT64 get_wpdata(UINT32 ref) +{ + return wpdata; +} + + +/*------------------------------------------------- + get_cycles - getter callback for the + 'cycles' symbol +-------------------------------------------------*/ + +static UINT64 get_cycles(UINT32 ref) +{ + return activecpu_get_icount(); +} + + +/*------------------------------------------------- + get_cpunum - getter callback for the + 'cpunum' symbol +-------------------------------------------------*/ + +static UINT64 get_cpunum(UINT32 ref) +{ + return cpu_getactivecpu(); +} + + +/*------------------------------------------------- + get_tempvar - getter callback for the + 'tempX' symbols +-------------------------------------------------*/ + +static UINT64 get_tempvar(UINT32 ref) +{ + return tempvar[ref]; +} + + +/*------------------------------------------------- + set_tempvar - setter callback for the + 'tempX' symbols +-------------------------------------------------*/ + +static void set_tempvar(UINT32 ref, UINT64 value) +{ + tempvar[ref] = value; +} + + +/*------------------------------------------------- + get_logunmap - getter callback for the logumap + symbols +-------------------------------------------------*/ + +static UINT64 get_logunmap(UINT32 ref) +{ + return memory_get_log_unmap(ref); +} + + +/*------------------------------------------------- + get_beamx - get beam horizontal position +-------------------------------------------------*/ + +static UINT64 get_beamx(UINT32 ref) +{ + return video_screen_get_hpos(ref); +} + + +/*------------------------------------------------- + get_beamy - get beam vertical position +-------------------------------------------------*/ + +static UINT64 get_beamy(UINT32 ref) +{ + return video_screen_get_vpos(ref); +} + + +/*------------------------------------------------- + set_logunmap - setter callback for the logumap + symbols +-------------------------------------------------*/ + +static void set_logunmap(UINT32 ref, UINT64 value) +{ + memory_set_log_unmap(ref, value ? 1 : 0); +} + + +/*------------------------------------------------- + get_current_pc - getter callback for a CPU's + current instruction pointer +-------------------------------------------------*/ + +static UINT64 get_current_pc(UINT32 ref) +{ + return activecpu_get_pc(); +} + + +/*------------------------------------------------- + get_cpu_reg - getter callback for a CPU's + register symbols +-------------------------------------------------*/ + +static UINT64 get_cpu_reg(UINT32 ref) +{ + return activecpu_get_reg(ref); +} + + +/*------------------------------------------------- + set_cpu_reg - setter callback for a CPU's + register symbols +-------------------------------------------------*/ + +static void set_cpu_reg(UINT32 ref, UINT64 value) +{ + activecpu_set_reg(ref, value); +} + + + +/*************************************************************************** + MAIN CPU CALLBACK +***************************************************************************/ + +/*------------------------------------------------- + mame_debug_hook - called by the CPU cores + before executing each instruction +-------------------------------------------------*/ + +void mame_debug_hook(void) +{ + int cpunum = cpu_getactivecpu(); + offs_t curpc = activecpu_get_pc(); + debug_cpu_info *info = &debug_cpuinfo[cpunum]; + + /* update the history */ + info->pc_history[info->pc_history_index++ % DEBUG_HISTORY_SIZE] = curpc; + + /* quick out if we are ignoring */ + if (info->ignoring) + return; + + /* note that we are in the debugger code */ + within_debugger_code = TRUE; + + /* bump the counter */ + execution_counter++; + + /* are we tracing? */ + if (info->trace.file) + perform_trace(info); + + /* per-instruction hook? */ + if (info->instrhook != NULL && (*info->instrhook)(curpc)) + execution_state = EXECUTION_STATE_STOPPED; + + /* check for execution breakpoints */ + if (execution_state != EXECUTION_STATE_STOPPED) + { + /* see if we hit an interrupt break */ + if (break_on_interrupt == 2 && break_on_interrupt_cpunum == cpunum) + { + debug_console_printf("Stopped on interrupt (CPU %d, IRQ %d)\n", break_on_interrupt_cpunum, break_on_interrupt_irqline); + break_on_interrupt = 0; + execution_state = EXECUTION_STATE_STOPPED; + } + + /* see if we hit a target time */ + if (break_on_time && attotime_compare(timer_get_time(), break_on_time_target) > 0) + { + debug_console_printf("Stopped at time interval %.1g\n", attotime_to_double(timer_get_time())); + break_on_time = 0; + execution_state = EXECUTION_STATE_STOPPED; + } + + /* see if the CPU changed and break if we are waiting for that to happen */ + if (cpunum != last_cpunum) + { + if (execution_state == EXECUTION_STATE_NEXT_CPU) + execution_state = EXECUTION_STATE_STOPPED; + last_cpunum = cpunum; + } + + /* check the temp running breakpoint and break if we hit it */ + if (info->temp_breakpoint_pc != ~0 && execution_state == EXECUTION_STATE_RUNNING && curpc == info->temp_breakpoint_pc) + { + execution_state = EXECUTION_STATE_STOPPED; + debug_console_printf("Stopped at temporary breakpoint %X on CPU %d\n", info->temp_breakpoint_pc, cpunum); + info->temp_breakpoint_pc = ~0; + } + + /* check for execution breakpoints */ + if (info->first_bp) + debug_check_breakpoints(cpunum, curpc); + + /* handle single stepping */ + if (steps_until_stop > 0 && (execution_state >= EXECUTION_STATE_STEP_INTO && execution_state <= EXECUTION_STATE_STEP_OUT)) + { + /* is this an actual step? */ + if (step_overout_breakpoint == ~0 || (cpunum == step_overout_cpunum && curpc == step_overout_breakpoint)) + { + /* decrement the count and reset the breakpoint */ + steps_until_stop--; + step_overout_breakpoint = ~0; + + /* if we hit 0, stop; otherwise, we might want to update everything */ + if (steps_until_stop == 0) + execution_state = EXECUTION_STATE_STOPPED; + else if (execution_state != EXECUTION_STATE_STEP_OUT && (steps_until_stop < 200 || steps_until_stop % 100 == 0)) + { + debug_view_update_all(); + debug_refresh_display(); + } + } + } + + /* check for debug keypresses */ + if (execution_state != EXECUTION_STATE_STOPPED && ++key_check_counter > 10000) + { + key_check_counter = 0; + if (input_ui_pressed(IPT_UI_DEBUG_BREAK)) + { + execution_state = EXECUTION_STATE_STOPPED; + debug_console_printf("User-initiated break\n"); + } + + /* while we're here, check for a periodic update */ + if (cpunum == last_stopped_cpunum && execution_state != EXECUTION_STATE_STOPPED && osd_ticks() > last_periodic_update_time + osd_ticks_per_second()/4) + { + debug_view_update_all(); + last_periodic_update_time = osd_ticks(); + } + } + } + + /* if we are supposed to halt, do it now */ + if (execution_state == EXECUTION_STATE_STOPPED) + { + /* reset the state */ + steps_until_stop = 0; + step_overout_breakpoint = ~0; + + /* update all views */ + debug_view_update_all(); + debug_refresh_display(); + + /* wait for the debugger; during this time, disable sound output */ + sound_mute(TRUE); + while (execution_state == EXECUTION_STATE_STOPPED) + { + /* clear the memory modified flag and wait */ + memory_modified = 0; + osd_wait_for_debugger(); + + /* if something modified memory, update the screen */ + if (memory_modified) + debug_refresh_display(); + + /* check for commands in the source file */ + process_source_file(); + + /* if an event got scheduled, resume */ + if (mame_is_scheduled_event_pending(Machine)) + execution_state = EXECUTION_STATE_RUNNING; + } + sound_mute(FALSE); + + /* remember the last cpunum where we stopped */ + last_stopped_cpunum = cpunum; + } + + /* handle step out/over on the instruction we are about to execute */ + if ((execution_state == EXECUTION_STATE_STEP_OVER || execution_state == EXECUTION_STATE_STEP_OUT) && cpunum == step_overout_cpunum && step_overout_breakpoint == ~0) + prepare_for_step_overout(); + + /* no longer in debugger code */ + within_debugger_code = FALSE; +} + + +/*------------------------------------------------- + perform_trace - log to the tracefile the + data for a given instruction +-------------------------------------------------*/ + +static UINT32 dasm_wrapped(char *buffer, offs_t pc) +{ + const debug_cpu_info *cpuinfo = debug_get_cpu_info(cpu_getactivecpu()); + int maxbytes = activecpu_max_instruction_bytes(); + UINT8 opbuf[64], argbuf[64]; + offs_t pcbyte; + int numbytes; + + /* fetch the bytes up to the maximum */ + pcbyte = ADDR2BYTE_MASKED(pc, cpuinfo, ADDRESS_SPACE_PROGRAM); + for (numbytes = 0; numbytes < maxbytes; numbytes++) + { + opbuf[numbytes] = debug_read_opcode(pcbyte + numbytes, 1, FALSE); + argbuf[numbytes] = debug_read_opcode(pcbyte + numbytes, 1, TRUE); + } + + return activecpu_dasm(buffer, pc, opbuf, argbuf); +} + + +static void perform_trace(debug_cpu_info *info) +{ + offs_t pc = activecpu_get_pc(); + int offset, count, i; + char buffer[100]; + offs_t dasmresult; + + /* are we in trace over mode and in a subroutine? */ + if (info->trace.trace_over_target && (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) + { + /* if we just finished looping, indicate as much */ + if (info->trace.loops) + 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) + debug_console_execute_command(info->trace.action, 0); + + /* print the address */ + offset = sprintf(buffer, "%0*X: ", info->space[ADDRESS_SPACE_PROGRAM].logchars, pc); + + /* print the disassembly */ + dasmresult = dasm_wrapped(&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->trace.trace_over_target && (dasmresult & DASMFLAG_SUPPORTED) + && (dasmresult & DASMFLAG_STEP_OVER)) + { + 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(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(void) +{ + offs_t pc = activecpu_get_pc(); + char dasmbuffer[100]; + offs_t dasmresult; + + /* disassemble the current instruction and get the flags */ + 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) && (dasmresult & DASMFLAG_STEP_OVER)) + { + 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; + step_overout_breakpoint = pc; + } + + /* if we're stepping out and this isn't a step out instruction, reset the steps until stop to a high number */ + if (execution_state == EXECUTION_STATE_STEP_OUT) + { + if ((dasmresult & DASMFLAG_SUPPORTED) && !(dasmresult & DASMFLAG_STEP_OUT)) + steps_until_stop = 100; + else + steps_until_stop = 1; + } +} + + +/*------------------------------------------------- + process_source_file - executes commands from + a source file +-------------------------------------------------*/ + +static void process_source_file(void) +{ + /* loop until the file is exhausted or until we are executing again */ + while (debug_source_file && (execution_state == EXECUTION_STATE_STOPPED)) + { + char buf[512]; + int i; + char *s; + + /* stop at the end of file */ + if (feof(debug_source_file)) + { + fclose(debug_source_file); + debug_source_file = NULL; + return; + } + + /* fetch the next line */ + memset(buf, 0, sizeof(buf)); + fgets(buf, sizeof(buf), debug_source_file); + + /* strip out comments (text after '//') */ + s = strstr(buf, "//"); + if (s) + *s = '\0'; + + /* strip whitespace */ + i = (int)strlen(buf); + while((i > 0) && (isspace(buf[i-1]))) + buf[--i] = '\0'; + + /* execute the command */ + if (buf[0]) + debug_console_execute_command(buf, 1); + } +} + + +/*------------------------------------------------- + debug_vblank_hook - called when the real + VBLANK hits +-------------------------------------------------*/ + +void debug_vblank_hook(void) +{ + /* if we're configured to stop on VBLANK, break */ + if (break_on_vblank) + { + execution_state = EXECUTION_STATE_STOPPED; + debug_console_printf("Stopped at VBLANK\n"); + break_on_vblank = 0; + } +} + + +/*------------------------------------------------- + debug_vblank_hook - called when an interrupt + is acknowledged +-------------------------------------------------*/ + +void debug_interrupt_hook(int cpunum, int irqline) +{ + /* if we're configured to stop on interrupt, break */ + if (break_on_interrupt && cpunum == break_on_interrupt_cpunum && (break_on_interrupt_irqline == -1 || irqline == break_on_interrupt_irqline)) + { + break_on_interrupt = 2; + break_on_interrupt_irqline = irqline; + } +} + + +/*------------------------------------------------- + standard_debug_hook_read - standard read hook +-------------------------------------------------*/ + +static void standard_debug_hook_read(int spacenum, int size, offs_t address) +{ + debug_cpu_info *info = &debug_cpuinfo[memory_hook_cpunum]; + + /* check watchpoints */ + if (info->read_watchpoints) + check_watchpoints(memory_hook_cpunum, spacenum, WATCHPOINT_READ, address, size, 0); + + /* check hotspots */ + if (info->hotspots) + check_hotspots(memory_hook_cpunum, spacenum, address); +} + + +/*------------------------------------------------- + standard_debug_hook_write - standard write hook +-------------------------------------------------*/ + +static void standard_debug_hook_write(int spacenum, int size, offs_t address, UINT64 data) +{ + debug_cpu_info *info = &debug_cpuinfo[memory_hook_cpunum]; + + /* check watchpoints */ + if (info->write_watchpoints) + check_watchpoints(memory_hook_cpunum, spacenum, WATCHPOINT_WRITE, address, size, data); +} + + +/*------------------------------------------------- + debug_get_memory_hooks - get memory hooks + for the specified CPU +-------------------------------------------------*/ + +void debug_get_memory_hooks(int cpunum, debug_hook_read_ptr *read, debug_hook_write_ptr *write) +{ + memory_hook_cpunum = cpunum; + + if (debug_cpuinfo[cpunum].read_watchpoints || debug_cpuinfo[cpunum].hotspots) + *read = standard_debug_hook_read; + else + *read = NULL; + + if (debug_cpuinfo[cpunum].write_watchpoints) + *write = standard_debug_hook_write; + else + *write = NULL; +} + + +/*------------------------------------------------- + debug_set_instruction_hook - set a hook to + be called on each instruction for a given CPU +-------------------------------------------------*/ + +void debug_set_instruction_hook(int cpunum, int (*hook)(offs_t pc)) +{ + debug_cpuinfo[cpunum].instrhook = hook; +} + + + +/*************************************************************************** + BREAKPOINTS +***************************************************************************/ + +/*------------------------------------------------- + debug_check_breakpoints - check the + breakpoints for a given CPU +-------------------------------------------------*/ + +void debug_check_breakpoints(int cpunum, offs_t pc) +{ + debug_cpu_breakpoint *bp; + UINT64 result; + + /* see if we match */ + for (bp = debug_cpuinfo[cpunum].first_bp; bp; 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)) + { + /* halt in the debugger by default */ + execution_state = EXECUTION_STATE_STOPPED; + + /* if we hit, evaluate the action */ + if (bp->action != NULL) + debug_console_execute_command(bp->action, 0); + + /* print a notification, unless the action made us go again */ + if (execution_state == EXECUTION_STATE_STOPPED) + debug_console_printf("Stopped at breakpoint %X\n", bp->index); + break; + } +} + + +/*------------------------------------------------- + debug_breakpoint_first - find the first + breakpoint for a given CPU +-------------------------------------------------*/ + +static debug_cpu_breakpoint *find_breakpoint(int bpnum) +{ + debug_cpu_breakpoint *bp; + int cpunum; + + /* loop over CPUs and find the requested breakpoint */ + for (cpunum = 0; cpunum < MAX_CPU; cpunum++) + for (bp = debug_cpuinfo[cpunum].first_bp; bp; bp = bp->next) + if (bp->index == bpnum) + return bp; + + return NULL; +} + + +/*------------------------------------------------- + debug_breakpoint_first - return the first + breakpoint for a given CPU +-------------------------------------------------*/ + +debug_cpu_breakpoint *debug_breakpoint_first(int cpunum) +{ + return (cpunum < MAX_CPU) ? debug_cpuinfo[cpunum].first_bp : NULL; +} + + +/*------------------------------------------------- + debug_breakpoint_set - set a new breakpoint +-------------------------------------------------*/ + +int debug_breakpoint_set(int cpunum, offs_t address, parsed_expression *condition, const char *action) +{ + debug_cpu_breakpoint *bp; + + assert_always((cpunum >= 0) && (cpunum < cpu_gettotalcpu()), "debug_breakpoint_set() called with invalid cpunum!"); + + /* allocate breakpoint */ + bp = malloc(sizeof(*bp)); + + /* if we can't allocate, return failure */ + if (!bp) + return 0; + + /* fill in the structure */ + bp->index = next_index++; + bp->enabled = 1; + bp->address = address; + bp->condition = condition; + bp->action = NULL; + if (action) + { + bp->action = malloc(strlen(action) + 1); + if (bp->action) + strcpy(bp->action, action); + } + + /* hook us in */ + bp->next = debug_cpuinfo[cpunum].first_bp; + debug_cpuinfo[cpunum].first_bp = bp; + return bp->index; +} + + +/*------------------------------------------------- + debug_breakpoint_clear - clear a breakpoint +-------------------------------------------------*/ + +int debug_breakpoint_clear(int bpnum) +{ + debug_cpu_breakpoint *bp, *pbp; + int cpunum; + + /* loop over CPUs and find the requested breakpoint */ + for (cpunum = 0; cpunum < MAX_CPU; cpunum++) + for (pbp = NULL, bp = debug_cpuinfo[cpunum].first_bp; bp; pbp = bp, bp = bp->next) + if (bp->index == bpnum) + { + /* unlink us from the list */ + if (pbp == NULL) + debug_cpuinfo[cpunum].first_bp = bp->next; + else + pbp->next = bp->next; + + /* free the memory */ + if (bp->condition) + expression_free(bp->condition); + if (bp->action) + free(bp->action); + free(bp); + return 1; + } + + /* we didn't find it; return an error */ + return 0; +} + + +/*------------------------------------------------- + debug_breakpoint_enable - enable/disable a + breakpoint +-------------------------------------------------*/ + +int debug_breakpoint_enable(int bpnum, int enable) +{ + debug_cpu_breakpoint *bp = find_breakpoint(bpnum); + + /* if we found it, set it */ + if (bp != NULL) + { + bp->enabled = (enable != 0); + return 1; + } + return 0; +} + + + +/*************************************************************************** + WATCHPOINTS +***************************************************************************/ + +/*------------------------------------------------- + check_watchpoints - check the + breakpoints for a given CPU and address space +-------------------------------------------------*/ + +static void check_watchpoints(int cpunum, int spacenum, int type, offs_t address, offs_t size, UINT64 value_to_write) +{ + debug_cpu_watchpoint *wp; + UINT64 result; + + /* if we're within debugger code, don't stop */ + if (within_debugger_code) + return; + + within_debugger_code = TRUE; + + /* if we are a write watchpoint, stash the value that will be written */ + wpaddr = address; + if (type & WATCHPOINT_WRITE) + wpdata = value_to_write; + + /* see if we match */ + for (wp = debug_cpuinfo[cpunum].space[spacenum].first_wp; wp; wp = wp->next) + if (wp->enabled && (wp->type & type) && 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)) + { + static const char *sizes[] = + { + "0bytes", "byte", "word", "3bytes", "dword", "5bytes", "6bytes", "7bytes", "qword" + }; + char buffer[100]; + + /* halt in the debugger by default */ + execution_state = EXECUTION_STATE_STOPPED; + + /* if we hit, evaluate the action */ + if (wp->action != NULL) + debug_console_execute_command(wp->action, 0); + + /* print a notification, unless the action made us go again */ + if (execution_state == EXECUTION_STATE_STOPPED) + { + if (type & WATCHPOINT_WRITE) + { + sprintf(buffer, "Stopped at watchpoint %X writing %s to %08X (PC=%X)", wp->index, sizes[size], BYTE2ADDR(address, &debug_cpuinfo[cpunum], spacenum), activecpu_get_pc()); + 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], BYTE2ADDR(address, &debug_cpuinfo[cpunum], spacenum), activecpu_get_pc()); + debug_console_printf("%s\n", buffer); + } + break; + } + + within_debugger_code = FALSE; +} + + +/*------------------------------------------------- + debug_watchpoint_first - find the first + watchpoint for a given CPU +-------------------------------------------------*/ + +static debug_cpu_watchpoint *find_watchpoint(int wpnum) +{ + debug_cpu_watchpoint *wp; + int cpunum, spacenum; + + /* loop over CPUs and address spaces and find the requested watchpoint */ + for (cpunum = 0; cpunum < MAX_CPU; cpunum++) + for (spacenum = 0; spacenum < ADDRESS_SPACES; spacenum++) + for (wp = debug_cpuinfo[cpunum].space[spacenum].first_wp; wp; wp = wp->next) + if (wp->index == wpnum) + return wp; + + return NULL; +} + + +/*------------------------------------------------- + debug_watchpoint_first - return the first + watchpoint for a given CPU +-------------------------------------------------*/ + +debug_cpu_watchpoint *debug_watchpoint_first(int cpunum, int spacenum) +{ + return (cpunum < MAX_CPU && spacenum < ADDRESS_SPACES) ? debug_cpuinfo[cpunum].space[spacenum].first_wp : NULL; +} + + +/*------------------------------------------------- + debug_watchpoint_set - set a new watchpoint +-------------------------------------------------*/ + +int debug_watchpoint_set(int cpunum, int spacenum, int type, offs_t address, offs_t length, parsed_expression *condition, const char *action) +{ + debug_cpu_watchpoint *wp = malloc(sizeof(*wp)); + + /* if we can't allocate, return failure */ + if (!wp) + return 0; + + /* fill in the structure */ + wp->index = next_index++; + wp->enabled = 1; + wp->type = type; + wp->address = ADDR2BYTE_MASKED(address, &debug_cpuinfo[cpunum], spacenum); + wp->length = ADDR2BYTE(length, &debug_cpuinfo[cpunum], spacenum); + wp->condition = condition; + wp->action = NULL; + if (action) + { + wp->action = malloc(strlen(action) + 1); + if (wp->action) + strcpy(wp->action, action); + } + + /* hook us in */ + wp->next = debug_cpuinfo[cpunum].space[spacenum].first_wp; + debug_cpuinfo[cpunum].space[spacenum].first_wp = wp; + if (wp->type & WATCHPOINT_READ) + debug_cpuinfo[cpunum].read_watchpoints++; + if (wp->type & WATCHPOINT_WRITE) + debug_cpuinfo[cpunum].write_watchpoints++; + + /* force debug_get_memory_hooks() to be called */ + cpuintrf_push_context(-1); + cpuintrf_pop_context(); + + return wp->index; +} + + +/*------------------------------------------------- + debug_watchpoint_clear - clear a watchpoint +-------------------------------------------------*/ + +int debug_watchpoint_clear(int wpnum) +{ + debug_cpu_watchpoint *wp, *pwp; + int cpunum, spacenum; + + /* loop over CPUs and find the requested watchpoint */ + for (cpunum = 0; cpunum < MAX_CPU; cpunum++) + for (spacenum = 0; spacenum < ADDRESS_SPACES; spacenum++) + for (pwp = NULL, wp = debug_cpuinfo[cpunum].space[spacenum].first_wp; wp; pwp = wp, wp = wp->next) + if (wp->index == wpnum) + { + /* unlink us from the list */ + if (pwp == NULL) + debug_cpuinfo[cpunum].space[spacenum].first_wp = wp->next; + else + pwp->next = wp->next; + + /* free the memory */ + if (wp->condition) + expression_free(wp->condition); + if (wp->action) + free(wp->action); + if (wp->type & WATCHPOINT_READ) + debug_cpuinfo[cpunum].read_watchpoints--; + if (wp->type & WATCHPOINT_WRITE) + debug_cpuinfo[cpunum].write_watchpoints--; + free(wp); + + /* force debug_get_memory_hooks() to be called */ + cpuintrf_push_context(-1); + cpuintrf_pop_context(); + + return 1; + } + + /* we didn't find it; return an error */ + return 0; +} + + +/*------------------------------------------------- + debug_watchpoint_enable - enable/disable a + watchpoint +-------------------------------------------------*/ + +int debug_watchpoint_enable(int wpnum, int enable) +{ + debug_cpu_watchpoint *wp = find_watchpoint(wpnum); + + /* if we found it, set it */ + if (wp != NULL) + { + wp->enabled = (enable != 0); + return 1; + } + return 0; +} + + + +/*************************************************************************** + HOTSPOTS +***************************************************************************/ + +/*------------------------------------------------- + debug_hotspot_track - enable/disable tracking + of hotspots +-------------------------------------------------*/ + +int debug_hotspot_track(int cpunum, int numspots, int threshhold) +{ + debug_cpu_info *info = &debug_cpuinfo[cpunum]; + + /* 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 = malloc_or_die(sizeof(*info->hotspots) * numspots); + memset(info->hotspots, 0xff, sizeof(*info->hotspots) * numspots); + + /* fill in the info */ + info->hotspot_count = numspots; + info->hotspot_threshhold = threshhold; + } + + /* force debug_get_memory_hooks() to be called */ + cpuintrf_push_context(-1); + cpuintrf_pop_context(); + + return 1; +} + + +/*------------------------------------------------- + check_hotspots - check for + hotspots on a memory read access +-------------------------------------------------*/ + +static void check_hotspots(int cpunum, int spacenum, offs_t address) +{ + debug_cpu_info *info = &debug_cpuinfo[cpunum]; + offs_t pc = activecpu_get_pc(); + 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].spacenum == spacenum) + 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("Hotspot @ %s %08X (PC=%08X) hit %d times (fell off bottom)\n", address_space_name[spot->spacenum], 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].spacenum = spacenum; + 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; + } + } +} + + +/*************************************************************************** + MEMORY ACCESSORS +***************************************************************************/ + +/*------------------------------------------------- + debug_read_byte - return a byte from the + current cpu in the specified memory space +-------------------------------------------------*/ + +UINT8 debug_read_byte(int spacenum, offs_t address, int apply_translation) +{ + const debug_cpu_info *info = &debug_cpuinfo[cpu_getactivecpu()]; + UINT64 custom; + UINT8 result; + + /* mask against the logical byte mask */ + address &= info->space[spacenum].logbytemask; + + /* all accesses from this point on are for the debugger */ + memory_set_debugger_access(1); + + /* translate if necessary; if not mapped, return 0xff */ + if (apply_translation && info->translate != NULL && !(*info->translate)(spacenum, &address)) + result = 0xff; + + /* if there is a custom read handler, and it returns TRUE, use that value */ + else if (info->read && (*info->read)(spacenum, address, 1, &custom)) + result = custom; + + /* otherwise, call the byte reading function for the translated address */ + else + result = (*active_address_space[spacenum].accessors->read_byte)(address); + + /* no longer accessing via the debugger */ + memory_set_debugger_access(0); + return result; +} + + +/*------------------------------------------------- + debug_read_word - return a word from the + current cpu in the specified memory space +-------------------------------------------------*/ + +UINT16 debug_read_word(int spacenum, offs_t address, int apply_translation) +{ + const debug_cpu_info *info = &debug_cpuinfo[cpu_getactivecpu()]; + UINT64 custom; + UINT16 result; + + /* mask against the logical byte mask */ + address &= info->space[spacenum].logbytemask; + + /* if this is misaligned read, or if there are no word readers, just read two bytes */ + if ((address & 1) || !active_address_space[spacenum].accessors->read_word) + { + UINT8 byte0 = debug_read_byte(spacenum, address + 0, apply_translation); + UINT8 byte1 = debug_read_byte(spacenum, address + 1, apply_translation); + + /* based on the endianness, the result is assembled differently */ + if (debug_cpuinfo[cpu_getactivecpu()].endianness == CPU_IS_LE) + result = byte0 | (byte1 << 8); + else + result = byte1 | (byte0 << 8); + } + + /* otherwise, this proceeds like the byte case */ + else + { + /* all accesses from this point on are for the debugger */ + memory_set_debugger_access(1); + + /* translate if necessary; if not mapped, return 0xffff */ + if (apply_translation && info->translate != NULL && !(*info->translate)(spacenum, &address)) + result = 0xffff; + + /* if there is a custom read handler, and it returns TRUE, use that value */ + else if (info->read && (*info->read)(spacenum, address, 2, &custom)) + result = custom; + + /* otherwise, call the byte reading function for the translated address */ + else + result = (*active_address_space[spacenum].accessors->read_word)(address); + + /* no longer accessing via the debugger */ + memory_set_debugger_access(0); + } + + return result; +} + + +/*------------------------------------------------- + debug_read_dword - return a dword from the + current cpu in the specified memory space +-------------------------------------------------*/ + +UINT32 debug_read_dword(int spacenum, offs_t address, int apply_translation) +{ + const debug_cpu_info *info = &debug_cpuinfo[cpu_getactivecpu()]; + UINT64 custom; + UINT32 result; + + /* mask against the logical byte mask */ + address &= info->space[spacenum].logbytemask; + + /* if this is misaligned read, or if there are no dword readers, just read two words */ + if ((address & 3) || !active_address_space[spacenum].accessors->read_dword) + { + UINT16 word0 = debug_read_word(spacenum, address + 0, apply_translation); + UINT16 word1 = debug_read_word(spacenum, address + 2, apply_translation); + + /* based on the endianness, the result is assembled differently */ + if (debug_cpuinfo[cpu_getactivecpu()].endianness == CPU_IS_LE) + result = word0 | (word1 << 16); + else + result = word1 | (word0 << 16); + } + + /* otherwise, this proceeds like the byte case */ + else + { + /* all accesses from this point on are for the debugger */ + memory_set_debugger_access(1); + + /* translate if necessary; if not mapped, return 0xffffffff */ + if (apply_translation && info->translate != NULL && !(*info->translate)(spacenum, &address)) + result = 0xffffffff; + + /* if there is a custom read handler, and it returns TRUE, use that value */ + else if (info->read && (*info->read)(spacenum, address, 4, &custom)) + result = custom; + + /* otherwise, call the byte reading function for the translated address */ + else + result = (*active_address_space[spacenum].accessors->read_dword)(address); + + /* no longer accessing via the debugger */ + memory_set_debugger_access(0); + } + + return result; +} + + +/*------------------------------------------------- + debug_read_qword - return a qword from the + current cpu in the specified memory space +-------------------------------------------------*/ + +UINT64 debug_read_qword(int spacenum, offs_t address, int apply_translation) +{ + const debug_cpu_info *info = &debug_cpuinfo[cpu_getactivecpu()]; + UINT64 custom; + UINT64 result; + + /* mask against the logical byte mask */ + address &= info->space[spacenum].logbytemask; + + /* if this is misaligned read, or if there are no qword readers, just read two dwords */ + if ((address & 7) || !active_address_space[spacenum].accessors->read_qword) + { + UINT32 dword0 = debug_read_dword(spacenum, address + 0, apply_translation); + UINT32 dword1 = debug_read_dword(spacenum, address + 4, apply_translation); + + /* based on the endianness, the result is assembled differently */ + if (debug_cpuinfo[cpu_getactivecpu()].endianness == CPU_IS_LE) + result = dword0 | ((UINT64)dword1 << 32); + else + result = dword1 | ((UINT64)dword0 << 32); + } + + /* otherwise, this proceeds like the byte case */ + else + { + /* all accesses from this point on are for the debugger */ + memory_set_debugger_access(1); + + /* translate if necessary; if not mapped, return 0xffffffffffffffff */ + if (apply_translation && info->translate != NULL && !(*info->translate)(spacenum, &address)) + result = ~(UINT64)0; + + /* if there is a custom read handler, and it returns TRUE, use that value */ + else if (info->read && (*info->read)(spacenum, address, 8, &custom)) + result = custom; + + /* otherwise, call the byte reading function for the translated address */ + else + result = (*active_address_space[spacenum].accessors->read_qword)(address); + + /* no longer accessing via the debugger */ + memory_set_debugger_access(0); + } + + return result; +} + + +/*------------------------------------------------- + debug_write_byte - write a byte to the + current cpu in the specified memory space +-------------------------------------------------*/ + +void debug_write_byte(int spacenum, offs_t address, UINT8 data, int apply_translation) +{ + const debug_cpu_info *info = &debug_cpuinfo[cpu_getactivecpu()]; + + /* mask against the logical byte mask */ + address &= info->space[spacenum].logbytemask; + + /* all accesses from this point on are for the debugger */ + memory_set_debugger_access(1); + + /* translate if necessary; if not mapped, we're done */ + if (apply_translation && info->translate != NULL && !(*info->translate)(spacenum, &address)) + ; + + /* if there is a custom write handler, and it returns TRUE, use that */ + else if (info->write && (*info->write)(spacenum, address, 1, data)) + ; + + /* otherwise, call the byte reading function for the translated address */ + else + (*active_address_space[spacenum].accessors->write_byte)(address, data); + + /* no longer accessing via the debugger */ + memory_set_debugger_access(0); + memory_modified = 1; +} + + +/*------------------------------------------------- + debug_write_word - write a word to the + current cpu in the specified memory space +-------------------------------------------------*/ + +void debug_write_word(int spacenum, offs_t address, UINT16 data, int apply_translation) +{ + const debug_cpu_info *info = &debug_cpuinfo[cpu_getactivecpu()]; + + /* mask against the logical byte mask */ + address &= info->space[spacenum].logbytemask; + + /* if this is a misaligned write, or if there are no word writers, just read two bytes */ + if ((address & 1) || !active_address_space[spacenum].accessors->write_word) + { + if (debug_cpuinfo[cpu_getactivecpu()].endianness == CPU_IS_LE) + { + debug_write_byte(spacenum, address + 0, data >> 0, apply_translation); + debug_write_byte(spacenum, address + 1, data >> 8, apply_translation); + } + else + { + debug_write_byte(spacenum, address + 0, data >> 8, apply_translation); + debug_write_byte(spacenum, address + 1, data >> 0, apply_translation); + } + } + + /* otherwise, this proceeds like the byte case */ + else + { + /* all accesses from this point on are for the debugger */ + memory_set_debugger_access(1); + + /* translate if necessary; if not mapped, we're done */ + if (apply_translation && info->translate && !(*info->translate)(spacenum, &address)) + ; + + /* if there is a custom write handler, and it returns TRUE, use that */ + else if (info->write && (*info->write)(spacenum, address, 2, data)) + ; + + /* otherwise, call the byte reading function for the translated address */ + else + (*active_address_space[spacenum].accessors->write_word)(address, data); + + /* no longer accessing via the debugger */ + memory_set_debugger_access(0); + memory_modified = 1; + } +} + + +/*------------------------------------------------- + debug_write_dword - write a dword to the + current cpu in the specified memory space +-------------------------------------------------*/ + +void debug_write_dword(int spacenum, offs_t address, UINT32 data, int apply_translation) +{ + const debug_cpu_info *info = &debug_cpuinfo[cpu_getactivecpu()]; + + /* mask against the logical byte mask */ + address &= info->space[spacenum].logbytemask; + + /* if this is a misaligned write, or if there are no dword writers, just read two words */ + if ((address & 3) || !active_address_space[spacenum].accessors->write_dword) + { + if (debug_cpuinfo[cpu_getactivecpu()].endianness == CPU_IS_LE) + { + debug_write_word(spacenum, address + 0, data >> 0, apply_translation); + debug_write_word(spacenum, address + 2, data >> 16, apply_translation); + } + else + { + debug_write_word(spacenum, address + 0, data >> 16, apply_translation); + debug_write_word(spacenum, address + 2, data >> 0, apply_translation); + } + } + + /* otherwise, this proceeds like the byte case */ + else + { + /* all accesses from this point on are for the debugger */ + memory_set_debugger_access(1); + + /* translate if necessary; if not mapped, we're done */ + if (apply_translation && info->translate && !(*info->translate)(spacenum, &address)) + ; + + /* if there is a custom write handler, and it returns TRUE, use that */ + else if (info->write && (*info->write)(spacenum, address, 4, data)) + ; + + /* otherwise, call the byte reading function for the translated address */ + else + (*active_address_space[spacenum].accessors->write_dword)(address, data); + + /* no longer accessing via the debugger */ + memory_set_debugger_access(0); + memory_modified = 1; + } +} + + +/*------------------------------------------------- + debug_write_qword - write a qword to the + current cpu in the specified memory space +-------------------------------------------------*/ + +void debug_write_qword(int spacenum, offs_t address, UINT64 data, int apply_translation) +{ + const debug_cpu_info *info = &debug_cpuinfo[cpu_getactivecpu()]; + + /* mask against the logical byte mask */ + address &= info->space[spacenum].logbytemask; + + /* if this is a misaligned write, or if there are no qword writers, just read two dwords */ + if ((address & 7) || !active_address_space[spacenum].accessors->write_qword) + { + if (debug_cpuinfo[cpu_getactivecpu()].endianness == CPU_IS_LE) + { + debug_write_dword(spacenum, address + 0, data >> 0, apply_translation); + debug_write_dword(spacenum, address + 4, data >> 32, apply_translation); + } + else + { + debug_write_dword(spacenum, address + 0, data >> 32, apply_translation); + debug_write_dword(spacenum, address + 4, data >> 0, apply_translation); + } + } + /* otherwise, this proceeds like the byte case */ + else + { + /* all accesses from this point on are for the debugger */ + memory_set_debugger_access(1); + + /* translate if necessary; if not mapped, we're done */ + if (apply_translation && info->translate && !(*info->translate)(spacenum, &address)) + ; + + /* if there is a custom write handler, and it returns TRUE, use that */ + else if (info->write && (*info->write)(spacenum, address, 8, data)) + ; + + /* otherwise, call the byte reading function for the translated address */ + else + (*active_address_space[spacenum].accessors->write_qword)(address, data); + + /* no longer accessing via the debugger */ + memory_set_debugger_access(0); + memory_modified = 1; + } +} + + +/*------------------------------------------------- + debug_read_opcode - read 1,2,4 or 8 bytes at + the given offset from opcode space +-------------------------------------------------*/ + +UINT64 debug_read_opcode(offs_t address, int size, int arg) +{ + const debug_cpu_info *info = &debug_cpuinfo[cpu_getactivecpu()]; + offs_t lowbits_mask; + const void *ptr; + + /* keep in logical range */ + address &= info->space[ADDRESS_SPACE_PROGRAM].logbytemask; + + /* shortcut if we have a custom routine */ + if (info->readop) + { + UINT64 result; + if ((*info->readop)(address, size, &result)) + return result; + } + + /* if we're bigger than the address bus, break into smaller pieces */ + if (size > info->space[ADDRESS_SPACE_PROGRAM].databytes) + { + int halfsize = size / 2; + UINT64 r0 = debug_read_opcode(address + 0, halfsize, arg); + UINT64 r1 = debug_read_opcode(address + halfsize, halfsize, arg); + + if (info->endianness == CPU_IS_LE) + return r0 | (r1 << (8 * halfsize)); + else + return r1 | (r0 << (8 * halfsize)); + } + + /* translate to physical first */ + if (info->translate && !(*info->translate)(ADDRESS_SPACE_PROGRAM, &address)) + return ~(UINT64)0 & (~(UINT64)0 >> (64 - 8*size)); + + /* keep in physical range */ + address &= info->space[ADDRESS_SPACE_PROGRAM].physbytemask; + + /* adjust the address */ + memory_set_opbase(address); + + switch (info->space[ADDRESS_SPACE_PROGRAM].databytes * 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 ^= (info->endianness == CPU_IS_LE) ? 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 ^= (info->endianness == CPU_IS_LE) ? BYTE4_XOR_LE(0) : BYTE4_XOR_BE(0); + break; + + /* dump opcodes in words from a dword-sized bus */ + case 42: + address ^= (info->endianness == CPU_IS_LE) ? 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 ^= (info->endianness == CPU_IS_LE) ? BYTE8_XOR_LE(0) : BYTE8_XOR_BE(0); + break; + + /* dump opcodes in words from a qword-sized bus */ + case 82: + address ^= (info->endianness == CPU_IS_LE) ? WORD2_XOR_LE(0) : WORD2_XOR_BE(0); + break; + + /* dump opcodes in dwords from a qword-sized bus */ + case 84: + address ^= (info->endianness == CPU_IS_LE) ? 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", info->space[ADDRESS_SPACE_PROGRAM].databytes * 10 + size); + break; + } + + /* get pointer to data */ + /* note that we query aligned to the bus width, and then add back the low bits */ + lowbits_mask = info->space[ADDRESS_SPACE_PROGRAM].databytes - 1; + ptr = memory_get_op_ptr(cpu_getactivecpu(), address & ~lowbits_mask, arg); + if (!ptr) + return ~(UINT64)0 & (~(UINT64)0 >> (64 - 8*size)); + ptr = (UINT8 *)ptr + (address & lowbits_mask); + + /* gross! */ +// if (osd_is_bad_read_ptr(ptr, size)) +// fatalerror("debug_read_opcode: cpu %d address %x mapped to invalid memory %p", cpu_getactivecpu(), address, ptr); + + /* return based on the size */ + switch (size) + { + case 1: return *(UINT8 *) ptr; + case 2: return *(UINT16 *)ptr; + case 4: return *(UINT32 *)ptr; + case 8: return *(UINT64 *)ptr; + } + + return 0; /* appease compiler */ +} + + +/*------------------------------------------------- + external_read_memory - read 1,2,4 or 8 bytes at + the given offset in the given address space +-------------------------------------------------*/ + +UINT64 external_read_memory(int space, UINT32 offset, int size) +{ + const debug_cpu_info *info = &debug_cpuinfo[cpu_getactivecpu()]; + if (info->space[space].databytes == 0) + return ~0; + + /* adjust the address into a byte address */ + offset = ADDR2BYTE(offset, info, space); + switch (size) + { + case 1: return debug_read_byte(space, offset, TRUE); + case 2: return debug_read_word(space, offset, TRUE); + case 4: return debug_read_dword(space, offset, TRUE); + case 8: return debug_read_qword(space, offset, TRUE); + } + return ~0; +} + + +/*------------------------------------------------- + external_write_memory - write 1,2,4 or 8 bytes to + the given offset in the given address space +-------------------------------------------------*/ + +void external_write_memory(int space, UINT32 offset, int size, UINT64 value) +{ + const debug_cpu_info *info = &debug_cpuinfo[cpu_getactivecpu()]; + if (info->space[space].databytes == 0) + return; + + /* adjust the address into a byte address */ + offset = ADDR2BYTE(offset, info, space); + switch (size) + { + case 1: debug_write_byte(space, offset, value, TRUE); break; + case 2: debug_write_word(space, offset, value, TRUE); break; + case 4: debug_write_dword(space, offset, value, TRUE); break; + case 8: debug_write_qword(space, offset, value, TRUE); break; + } +} + + +/*------------------------------------------------- + debug_trace_printf - writes text to a given + CPU's trace file +-------------------------------------------------*/ + +void debug_trace_printf(int cpunum, const char *fmt, ...) +{ + va_list va; + + debug_cpu_info *info = &debug_cpuinfo[cpunum]; + + if (info->trace.file) + { + va_start(va, fmt); + vfprintf(info->trace.file, fmt, va); + va_end(va); + } +} + + +/*------------------------------------------------- + debug_source_script - specifies a debug command + script to use +-------------------------------------------------*/ + +void debug_source_script(const char *file) +{ + if (debug_source_file) + { + fclose(debug_source_file); + debug_source_file = NULL; + } + + if (file) + { + debug_source_file = fopen(file, "r"); + if (!debug_source_file) + { + if (mame_get_phase(Machine) == MAME_PHASE_RUNNING) + debug_console_printf("Cannot open command file '%s'\n", file); + else + fatalerror("Cannot open command file '%s'", file); + } + } +} + + +/*------------------------------------------------- + debug_flush_traces - flushes all traces; this is + useful if a trace is going on when we fatalerror +-------------------------------------------------*/ + +void debug_flush_traces(void) +{ + int cpunum; + + for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++) + { + if (debug_cpuinfo[cpunum].trace.file) + fflush(debug_cpuinfo[cpunum].trace.file); + } +} |