// license:BSD-3-Clause // copyright-holders:Aaron Giles /********************************************************************* debugcpu.h Debugger CPU/memory interface engine. ***************************************************************************/ #pragma once #ifndef __DEBUGCPU_H__ #define __DEBUGCPU_H__ #include "express.h" #include "simple_set.h" //************************************************************************** // CONSTANTS //************************************************************************** const UINT8 WATCHPOINT_READ = 1; const UINT8 WATCHPOINT_WRITE = 2; const UINT8 WATCHPOINT_READWRITE = WATCHPOINT_READ | WATCHPOINT_WRITE; const int COMMENT_VERSION = 1; //************************************************************************** // TYPE DEFINITIONS //************************************************************************** typedef int (*debug_instruction_hook_func)(device_t &device, offs_t curpc); struct xml_data_node; class device_debug { typedef offs_t (*dasm_override_func)(device_t &device, char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram, int options); public: // breakpoint class class breakpoint { friend class device_debug; public: // construction/destruction breakpoint(device_debug* debugInterface, symbol_table &symbols, int index, offs_t address, const char *condition = NULL, const char *action = NULL); // getters const device_debug *debugInterface() const { return m_debugInterface; } breakpoint *next() const { return m_next; } int index() const { return m_index; } bool enabled() const { return m_enabled; } offs_t address() const { return m_address; } const char *condition() const { return m_condition.original_string(); } const char *action() const { return m_action; } // setters void setEnabled(bool value) { m_enabled = value; } private: // internals bool hit(offs_t pc); const device_debug * m_debugInterface; // the interface we were created from breakpoint * m_next; // next in the list int m_index; // user reported index UINT8 m_enabled; // enabled? offs_t m_address; // execution address parsed_expression m_condition; // condition astring m_action; // action }; // watchpoint class class watchpoint { friend class device_debug; public: // construction/destruction watchpoint(device_debug* debugInterface, symbol_table &symbols, int index, address_space &space, int type, offs_t address, offs_t length, const char *condition = NULL, const char *action = NULL); // getters const device_debug *debugInterface() const { return m_debugInterface; } watchpoint *next() const { return m_next; } address_space &space() const { return m_space; } int index() const { return m_index; } int type() const { return m_type; } bool enabled() const { return m_enabled; } offs_t address() const { return m_address; } offs_t length() const { return m_length; } const char *condition() const { return m_condition.original_string(); } const char *action() const { return m_action; } // setters void setEnabled(bool value) { m_enabled = value; } private: // internals bool hit(int type, offs_t address, int size); const device_debug * m_debugInterface; // the interface we were created from watchpoint * m_next; // next in the list address_space & m_space; // address space int m_index; // user reported index bool m_enabled; // enabled? UINT8 m_type; // type (read/write) offs_t m_address; // start address offs_t m_length; // length of watch area parsed_expression m_condition; // condition astring m_action; // action }; // registerpoint class class registerpoint { friend class device_debug; public: // construction/destruction registerpoint(symbol_table &symbols, int index, const char *condition, const char *action = NULL); // getters registerpoint *next() const { return m_next; } int index() const { return m_index; } bool enabled() const { return m_enabled; } const char *condition() const { return m_condition.original_string(); } const char *action() const { return m_action; } private: // internals bool hit(); registerpoint * m_next; // next in the list int m_index; // user reported index UINT8 m_enabled; // enabled? parsed_expression m_condition; // condition astring m_action; // action }; public: // construction/destruction device_debug(device_t &device); ~device_debug(); // getters symbol_table &symtable() { return m_symtable; } // commonly-used pass-throughs offs_t pc() const { return (m_state != NULL) ? m_state->pc() : 0; } int logaddrchars(address_spacenum spacenum = AS_0) const { return (m_memory != NULL && m_memory->has_space(spacenum)) ? m_memory->space(spacenum).logaddrchars() : 8; } int min_opcode_bytes() const { return (m_disasm != NULL) ? m_disasm->max_opcode_bytes() : 1; } int max_opcode_bytes() const { return (m_disasm != NULL) ? m_disasm->max_opcode_bytes() : 1; } device_t& device() const { return m_device; } // hooks used by the rest of the system void start_hook(attotime endtime); void stop_hook(); void interrupt_hook(int irqline); void exception_hook(int exception); void instruction_hook(offs_t curpc); void memory_read_hook(address_space &space, offs_t address, UINT64 mem_mask); void memory_write_hook(address_space &space, offs_t address, UINT64 data, UINT64 mem_mask); // hooks into our operations void set_instruction_hook(debug_instruction_hook_func hook); void set_dasm_override(dasm_override_func dasm_override) { m_dasm_override = dasm_override; } // disassembly offs_t disassemble(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram) const; // debugger focus void ignore(bool ignore = true); bool observing() const { return ((m_flags & DEBUG_FLAG_OBSERVING) != 0); } // single stepping void single_step(int numsteps = 1); void single_step_over(int numsteps = 1); void single_step_out(); // execution void go(offs_t targetpc = ~0); void go_vblank(); void go_interrupt(int irqline = -1); void go_exception(int exception); void go_milliseconds(UINT64 milliseconds); void go_next_device(); void halt_on_next_instruction(const char *fmt, ...) ATTR_PRINTF(2,3); // breakpoints breakpoint *breakpoint_first() const { return m_bplist; } int breakpoint_set(offs_t address, const char *condition = NULL, const char *action = NULL); bool breakpoint_clear(int index); void breakpoint_clear_all(); bool breakpoint_enable(int index, bool enable = true); void breakpoint_enable_all(bool enable = true); // watchpoints watchpoint *watchpoint_first(address_spacenum spacenum) const { return m_wplist[spacenum]; } int watchpoint_set(address_space &space, int type, offs_t address, offs_t length, const char *condition, const char *action); bool watchpoint_clear(int wpnum); void watchpoint_clear_all(); bool watchpoint_enable(int index, bool enable = true); void watchpoint_enable_all(bool enable = true); // registerpoints registerpoint *registerpoint_first() const { return m_rplist; } int registerpoint_set(const char *condition, const char *action = NULL); bool registerpoint_clear(int index); void registerpoint_clear_all(); bool registerpoint_enable(int index, bool enable = true); void registerpoint_enable_all(bool enable = true ); // hotspots bool hotspot_tracking_enabled() const { return (m_hotspots != NULL); } void hotspot_track(int numspots, int threshhold); // comments void comment_add(offs_t address, const char *comment, rgb_t color); bool comment_remove(offs_t addr); const char *comment_text(offs_t addr) const; UINT32 comment_count() const { return m_comment_set.size(); } UINT32 comment_change_count() const { return m_comment_change; } bool comment_export(xml_data_node &node); bool comment_import(xml_data_node &node); UINT32 compute_opcode_crc32(offs_t pc) const; // history offs_t history_pc(int index) const; // pc tracking void set_track_pc(bool value) { m_track_pc = value; } bool track_pc_visited(const offs_t& pc) const; void set_track_pc_visited(const offs_t& pc); void track_pc_data_clear() { m_track_pc_set.clear(); } // memory tracking void set_track_mem(bool value) { m_track_mem = value; } offs_t track_mem_pc_from_space_address_data(const address_spacenum& space, const offs_t& address, const UINT64& data) const; void track_mem_data_clear() { m_track_mem_set.clear(); } // tracing void trace(FILE *file, bool trace_over, const char *action); void trace_printf(const char *fmt, ...) ATTR_PRINTF(2,3); void trace_flush() { if (m_trace != NULL) m_trace->flush(); } void reset_transient_flag() { m_flags &= ~DEBUG_FLAG_TRANSIENT; } static const int HISTORY_SIZE = 256; private: // internal helpers void compute_debug_flags(); void prepare_for_step_overout(offs_t pc); UINT32 dasm_wrapped(astring &buffer, offs_t pc); // breakpoint and watchpoint helpers void breakpoint_update_flags(); void breakpoint_check(offs_t pc); void watchpoint_update_flags(address_space &space); void watchpoint_check(address_space &space, int type, offs_t address, UINT64 value_to_write, UINT64 mem_mask); void hotspot_check(address_space &space, offs_t address); // symbol get/set callbacks static UINT64 get_current_pc(symbol_table &table, void *ref); static UINT64 get_cycles(symbol_table &table, void *ref); static UINT64 get_totalcycles(symbol_table &table, void *ref); static UINT64 get_lastinstructioncycles(symbol_table &table, void *ref); static UINT64 get_logunmap(symbol_table &table, void *ref); static void set_logunmap(symbol_table &table, void *ref, UINT64 value); static UINT64 get_state(symbol_table &table, void *ref); static void set_state(symbol_table &table, void *ref, UINT64 value); // basic device information device_t & m_device; // device we are attached to device_execute_interface * m_exec; // execute interface, if present device_memory_interface * m_memory; // memory interface, if present device_state_interface * m_state; // state interface, if present device_disasm_interface * m_disasm; // disasm interface, if present // global state UINT32 m_flags; // debugging flags for this CPU symbol_table m_symtable; // symbol table for expression evaluation debug_instruction_hook_func m_instrhook; // per-instruction callback hook // disassembly dasm_override_func m_dasm_override; // pointer to provided override function UINT8 m_opwidth; // width of an opcode // stepping information offs_t m_stepaddr; // step target address for DEBUG_FLAG_STEPPING_OVER int m_stepsleft; // number of steps left until done // execution information offs_t m_stopaddr; // stop address for DEBUG_FLAG_STOP_PC attotime m_stoptime; // stop time for DEBUG_FLAG_STOP_TIME int m_stopirq; // stop IRQ number for DEBUG_FLAG_STOP_INTERRUPT int m_stopexception; // stop exception number for DEBUG_FLAG_STOP_EXCEPTION attotime m_endexectime; // ending time of the current execution UINT64 m_total_cycles; // current total cycles UINT64 m_last_total_cycles; // last total cycles // history offs_t m_pc_history[HISTORY_SIZE]; // history of recent PCs UINT32 m_pc_history_index; // current history index // breakpoints and watchpoints breakpoint * m_bplist; // list of breakpoints watchpoint * m_wplist[ADDRESS_SPACES]; // watchpoint lists for each address space registerpoint * m_rplist; // list of registerpoints // tracing class tracer { public: tracer(device_debug &debug, FILE &file, bool trace_over, const char *action); ~tracer(); void update(offs_t pc); void vprintf(const char *format, va_list va); void flush(); private: static const int TRACE_LOOPS = 64; device_debug & m_debug; // reference to our owner FILE & m_file; // tracing file for this CPU astring m_action; // action to perform during a trace offs_t m_history[TRACE_LOOPS]; // history of recent PCs int m_loops; // number of instructions in a loop int m_nextdex; // next index bool m_trace_over; // true if we're tracing over offs_t m_trace_over_target; // target for tracing over // (0 = not tracing over, // ~0 = not currently tracing over) }; tracer * m_trace; // tracer state // hotspots struct hotspot_entry { offs_t m_access; // access address offs_t m_pc; // PC of the access address_space * m_space; // space where the access occurred UINT32 m_count; // number of hits }; dynamic_array m_hotspots; // hotspot list int m_hotspot_threshhold; // threshhold for the number of hits to print // pc tracking class dasm_pc_tag { public: dasm_pc_tag(const offs_t& address, const UINT32& crc); // required to be included in a simple_set bool operator < (const dasm_pc_tag& rhs) const { if (m_address == rhs.m_address) return m_crc < rhs.m_crc; return (m_address < rhs.m_address); } offs_t m_address; // Stores [nothing] for a given address & crc32 UINT32 m_crc; }; simple_set m_track_pc_set; bool m_track_pc; // comments class dasm_comment : public dasm_pc_tag { public: dasm_comment(offs_t address, UINT32 crc, const char *text, rgb_t color); astring m_text; // Stores comment text & color for a given address & crc32 rgb_t m_color; }; simple_set m_comment_set; // collection of comments UINT32 m_comment_change; // change counter for comments // memory tracking class dasm_memory_access { public: dasm_memory_access(const address_spacenum& address_space, const offs_t& address, const UINT64& data, const offs_t& pc); // required to be included in a simple_set bool operator < (const dasm_memory_access& rhs) const { if ((m_address == rhs.m_address) && (m_address_space == rhs.m_address_space)) return m_data < rhs.m_data; return (m_address < rhs.m_address) && (m_address_space == rhs.m_address_space); } // Stores the PC for a given address, memory region, and data value address_spacenum m_address_space; offs_t m_address; UINT64 m_data; offs_t m_pc; }; simple_set m_track_mem_set; bool m_track_mem; // internal flag values static const UINT32 DEBUG_FLAG_OBSERVING = 0x00000001; // observing this CPU static const UINT32 DEBUG_FLAG_HISTORY = 0x00000002; // tracking this CPU's history static const UINT32 DEBUG_FLAG_TRACING = 0x00000004; // tracing this CPU static const UINT32 DEBUG_FLAG_TRACING_OVER = 0x00000008; // tracing this CPU with step over behavior static const UINT32 DEBUG_FLAG_HOOKED = 0x00000010; // per-instruction callback hook static const UINT32 DEBUG_FLAG_STEPPING = 0x00000020; // CPU is single stepping static const UINT32 DEBUG_FLAG_STEPPING_OVER = 0x00000040; // CPU is stepping over a function static const UINT32 DEBUG_FLAG_STEPPING_OUT = 0x00000080; // CPU is stepping out of a function static const UINT32 DEBUG_FLAG_STOP_PC = 0x00000100; // there is a pending stop at cpu->breakpc static const UINT32 DEBUG_FLAG_STOP_INTERRUPT = 0x00000400; // there is a pending stop on the next interrupt static const UINT32 DEBUG_FLAG_STOP_EXCEPTION = 0x00000800; // there is a pending stop on the next exception static const UINT32 DEBUG_FLAG_STOP_VBLANK = 0x00001000; // there is a pending stop on the next VBLANK static const UINT32 DEBUG_FLAG_STOP_TIME = 0x00002000; // there is a pending stop at cpu->stoptime static const UINT32 DEBUG_FLAG_LIVE_BP = 0x00010000; // there are live breakpoints for this CPU static const UINT32 DEBUG_FLAG_STEPPING_ANY = DEBUG_FLAG_STEPPING | DEBUG_FLAG_STEPPING_OVER | DEBUG_FLAG_STEPPING_OUT; static const UINT32 DEBUG_FLAG_TRACING_ANY = DEBUG_FLAG_TRACING | DEBUG_FLAG_TRACING_OVER; static const UINT32 DEBUG_FLAG_TRANSIENT = DEBUG_FLAG_STEPPING_ANY | DEBUG_FLAG_STOP_PC | DEBUG_FLAG_STOP_INTERRUPT | DEBUG_FLAG_STOP_EXCEPTION | DEBUG_FLAG_STOP_VBLANK | DEBUG_FLAG_STOP_TIME; }; //************************************************************************** // FUNCTION PROTOTYPES //************************************************************************** /* ----- initialization and cleanup ----- */ /* initialize the CPU tracking for the debugger */ void debug_cpu_init(running_machine &machine); void debug_cpu_configure_memory(running_machine &machine, symbol_table &table); /* flushes all traces; this is useful if a trace is going on when we fatalerror */ void debug_cpu_flush_traces(running_machine &machine); /* ----- debugging status & information ----- */ /* return the visible CPU device (the one that commands should apply to) */ device_t *debug_cpu_get_visible_cpu(running_machine &machine); /* TRUE if the debugger is currently stopped within an instruction hook callback */ int debug_cpu_within_instruction_hook(running_machine &machine); /* return TRUE if the current execution state is stopped */ int debug_cpu_is_stopped(running_machine &machine); /* ----- symbol table interfaces ----- */ /* return the global symbol table */ symbol_table *debug_cpu_get_global_symtable(running_machine &machine); /* return the locally-visible symbol table */ symbol_table *debug_cpu_get_visible_symtable(running_machine &machine); /* ----- misc debugger functions ----- */ /* specifies a debug command script to execute */ void debug_cpu_source_script(running_machine &machine, const char *file); /* ----- debugger comment helpers ----- */ // save all comments for a given machine bool debug_comment_save(running_machine &machine); // load all comments for a given machine bool debug_comment_load(running_machine &machine); /* ----- debugger memory accessors ----- */ /* return the physical address corresponding to the given logical address */ int debug_cpu_translate(address_space &space, int intention, offs_t *address); /* return a byte from the the specified memory space */ UINT8 debug_read_byte(address_space &space, offs_t address, int apply_translation); /* return a word from the the specified memory space */ UINT16 debug_read_word(address_space &space, offs_t address, int apply_translation); /* return a dword from the the specified memory space */ UINT32 debug_read_dword(address_space &space, offs_t address, int apply_translation); /* return a qword from the the specified memory space */ UINT64 debug_read_qword(address_space &space, offs_t address, int apply_translation); /* return 1,2,4 or 8 bytes from the specified memory space */ UINT64 debug_read_memory(address_space &space, offs_t address, int size, int apply_translation); /* write a byte to the specified memory space */ void debug_write_byte(address_space &space, offs_t address, UINT8 data, int apply_translation); /* write a word to the specified memory space */ void debug_write_word(address_space &space, offs_t address, UINT16 data, int apply_translation); /* write a dword to the specified memory space */ void debug_write_dword(address_space &space, offs_t address, UINT32 data, int apply_translation); /* write a qword to the specified memory space */ void debug_write_qword(address_space &space, offs_t address, UINT64 data, int apply_translation); /* write 1,2,4 or 8 bytes to the specified memory space */ void debug_write_memory(address_space &space, offs_t address, UINT64 data, int size, int apply_translation); /* read 1,2,4 or 8 bytes at the given offset from opcode space */ UINT64 debug_read_opcode(address_space &space, offs_t offset, int size, int arg); #endif