/*********************************************************************

    debugcpu.h

    Debugger CPU/memory interface engine.

****************************************************************************

    Copyright Aaron Giles
    All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are
    met:

        * Redistributions of source code must retain the above copyright
          notice, this list of conditions and the following disclaimer.
        * Redistributions in binary form must reproduce the above copyright
          notice, this list of conditions and the following disclaimer in
          the documentation and/or other materials provided with the
          distribution.
        * Neither the name 'MAME' nor the names of its contributors may be
          used to endorse or promote products derived from this software
          without specific prior written permission.

    THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
    IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    POSSIBILITY OF SUCH DAMAGE.

***************************************************************************/

#pragma once

#ifndef __DEBUGCPU_H__
#define __DEBUGCPU_H__

#include "express.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);


typedef struct _xml_data_node 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(symbol_table &symbols, int index, offs_t address, const char *condition = NULL, const char *action = NULL);

		// getters
		breakpoint *next() const { return m_next; }
		int index() const { return m_index; }
		bool enabled() const { return m_enabled; }
		offs_t address() const { return m_address; }
		const char *condition() const { return m_condition.original_string(); }
		const char *action() const { return m_action; }

	private:
		// internals
		bool hit(offs_t pc);

		breakpoint *		m_next;						// next in the list
		int					m_index;					// user reported index
		UINT8				m_enabled;					// enabled?
		offs_t				m_address;					// execution address
		parsed_expression	m_condition;				// condition
		astring				m_action;					// action
	};

	// watchpoint class
	class watchpoint
	{
		friend class device_debug;

	public:
		// construction/destruction
		watchpoint(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
		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; }

	private:
		// internals
		bool hit(int type, offs_t address, int size);

		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
	};

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->space(spacenum) != NULL) ? m_memory->space(spacenum)->logaddrchars() : 8; }
	int min_opcode_bytes() const { return (m_disasm != NULL) ? m_disasm->max_opcode_bytes() : 1; }
	int max_opcode_bytes() const { return (m_disasm != NULL) ? m_disasm->max_opcode_bytes() : 1; }

	// hooks used by the rest of the system
	void start_hook(attotime endtime);
	void stop_hook();
	void interrupt_hook(int irqline);
	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, ...);

	// 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);

	// 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_list.count(); }
	UINT32 comment_change_count() const { return m_comment_change; }
	bool comment_export(xml_data_node &node);
	bool comment_import(xml_data_node &node);
	void comment_dump(offs_t addr = ~0);
	UINT32 compute_opcode_crc32(offs_t address) const;

	// history
	offs_t history_pc(int index) const;

	// tracing
	void trace(FILE *file, bool trace_over, const char *action);
	void trace_printf(const char *fmt, ...);
	void trace_flush() { if (m_trace != 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_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

	// history
	offs_t					m_pc_history[HISTORY_SIZE]; // history of recent PCs
	UINT32					m_pc_history_index;			// current history index

	// breakpoints and watchpoints
	breakpoint *			m_bplist;					// list of breakpoints
	watchpoint *			m_wplist[ADDRESS_SPACES];	// watchpoint lists for each address space

	// tracing
	class tracer
	{
	public:
		tracer(device_debug &debug, FILE &file, bool trace_over, const char *action);
		~tracer();

		void update(offs_t pc);
		void vprintf(const char *format, va_list va);
		void flush();

	private:
		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
	};
	hotspot_entry *			m_hotspots;					// hotspot list
	int						m_hotspot_count;			// number of hotspots
	int						m_hotspot_threshhold;		// threshhold for the number of hits to print

	// comments
	class dasm_comment
	{
	public:
		dasm_comment(const char *text, offs_t address, rgb_t color, UINT32 crc);

		dasm_comment *next() const { return m_next; }

		dasm_comment *		m_next;						// next comment in the list
		offs_t				m_address;					// address in question
		rgb_t				m_color;					// color to use
		UINT32				m_crc;						// CRC of code
		astring				m_text;						// text
	};
	simple_list<dasm_comment> m_comment_list;			// list of comments
	UINT32					m_comment_change;			// change counter for comments

	// 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
