diff options
| author | 2010-06-25 05:11:42 +0000 | |
|---|---|---|
| committer | 2010-06-25 05:11:42 +0000 | |
| commit | 29b6b0de4167319a4f64ee95ecc5f18ba3fdf923 (patch) | |
| tree | a408b4561c3d622c98e267e9a8a269390e0210ba /src/emu/debug | |
| parent | 1c9abccb20fc25c928990ef050bce82efac99cc8 (diff) | |
C++-ified the debugger views. Not quite architecturally where I would
like them, but it's a start. Split implementation of individual view
types out to separate files. Updated all callers.
Also:
* fixed okim6295 memory view
* changed emualloc to free resource pools from earliest to latest
so that early objects can safely clean up stuff they allocated
Diffstat (limited to 'src/emu/debug')
| -rw-r--r-- | src/emu/debug/debugcmd.c | 4 | ||||
| -rw-r--r-- | src/emu/debug/debugcmt.c | 6 | ||||
| -rw-r--r-- | src/emu/debug/debugcon.c | 10 | ||||
| -rw-r--r-- | src/emu/debug/debugcpu.c | 17 | ||||
| -rw-r--r-- | src/emu/debug/debugvw.c | 3433 | ||||
| -rw-r--r-- | src/emu/debug/debugvw.h | 661 | ||||
| -rw-r--r-- | src/emu/debug/dvdisasm.c | 710 | ||||
| -rw-r--r-- | src/emu/debug/dvdisasm.h | 146 | ||||
| -rw-r--r-- | src/emu/debug/dvmemory.c | 813 | ||||
| -rw-r--r-- | src/emu/debug/dvmemory.h | 160 | ||||
| -rw-r--r-- | src/emu/debug/dvstate.c | 370 | ||||
| -rw-r--r-- | src/emu/debug/dvstate.h | 117 | ||||
| -rw-r--r-- | src/emu/debug/dvtext.c | 184 | ||||
| -rw-r--r-- | src/emu/debug/dvtext.h | 90 |
14 files changed, 3274 insertions, 3447 deletions
diff --git a/src/emu/debug/debugcmd.c b/src/emu/debug/debugcmd.c index 1998851f001..7d00f073e06 100644 --- a/src/emu/debug/debugcmd.c +++ b/src/emu/debug/debugcmd.c @@ -1128,7 +1128,7 @@ static void execute_comment(running_machine *machine, int ref, int params, const /* Now try adding the comment */ debug_comment_add(cpu, address, param[1], 0x00ff0000, debug_comment_get_opcode_crc32(cpu, address)); - debug_view_update_type(cpu->machine, DVT_DISASSEMBLY); + cpu->machine->m_debug_view->update_all(DVT_DISASSEMBLY); } @@ -1152,7 +1152,7 @@ static void execute_comment_del(running_machine *machine, int ref, int params, c /* If it's a number, it must be an address */ /* The bankoff and cbn will be pulled from what's currently active */ debug_comment_remove(cpu, address, debug_comment_get_opcode_crc32(cpu, address)); - debug_view_update_type(cpu->machine, DVT_DISASSEMBLY); + cpu->machine->m_debug_view->update_all(DVT_DISASSEMBLY); } diff --git a/src/emu/debug/debugcmt.c b/src/emu/debug/debugcmt.c index 69c0c8671e4..c73b0441c41 100644 --- a/src/emu/debug/debugcmt.c +++ b/src/emu/debug/debugcmt.c @@ -155,7 +155,7 @@ int debug_comment_add(device_t *device, offs_t addr, const char *comment, rgb_t comments->change_count++; /* force an update of disassembly views */ - debug_view_update_type(device->machine, DVT_DISASSEMBLY); + device->machine->m_debug_view->update_all(DVT_DISASSEMBLY); return 1; } @@ -170,7 +170,7 @@ int debug_comment_add(device_t *device, offs_t addr, const char *comment, rgb_t comments->change_count++; /* force an update of disassembly views */ - debug_view_update_type(device->machine, DVT_DISASSEMBLY); + device->machine->m_debug_view->update_all(DVT_DISASSEMBLY); return 1; } @@ -207,7 +207,7 @@ int debug_comment_remove(device_t *device, offs_t addr, UINT32 c_crc) comments->change_count++; /* force an update of disassembly views */ - debug_view_update_type(device->machine, DVT_DISASSEMBLY); + device->machine->m_debug_view->update_all(DVT_DISASSEMBLY); return 1; } diff --git a/src/emu/debug/debugcon.c b/src/emu/debug/debugcon.c index 203cb17bfe5..10ed838ce47 100644 --- a/src/emu/debug/debugcon.c +++ b/src/emu/debug/debugcon.c @@ -382,7 +382,7 @@ CMDERR debug_console_execute_command(running_machine *machine, const char *comma /* update all views */ if (echo) { - debug_view_update_all(machine); + machine->m_debug_view->update_all(); debugger_refresh_display(machine); } return result; @@ -481,7 +481,7 @@ void CLIB_DECL debug_console_printf(running_machine *machine, const char *format text_buffer_print(console_textbuf, buffer); /* force an update of any console views */ - debug_view_update_type(machine, DVT_CONSOLE); + machine->m_debug_view->update_all(DVT_CONSOLE); } @@ -499,7 +499,7 @@ void CLIB_DECL debug_console_vprintf(running_machine *machine, const char *forma text_buffer_print(console_textbuf, buffer); /* force an update of any console views */ - debug_view_update_type(machine, DVT_CONSOLE); + machine->m_debug_view->update_all(DVT_CONSOLE); } @@ -521,7 +521,7 @@ void CLIB_DECL debug_console_printf_wrap(running_machine *machine, int wrapcol, text_buffer_print_wrap(console_textbuf, buffer, wrapcol); /* force an update of any console views */ - debug_view_update_type(machine, DVT_CONSOLE); + machine->m_debug_view->update_all(DVT_CONSOLE); } @@ -547,7 +547,7 @@ void debug_errorlog_write_line(running_machine *machine, const char *line) text_buffer_print(errorlog_textbuf, line); /* force an update of any log views */ - debug_view_update_type(machine, DVT_LOG); + machine->m_debug_view->update_all(DVT_LOG); } diff --git a/src/emu/debug/debugcpu.c b/src/emu/debug/debugcpu.c index 2996d9ac6cf..30b841e638c 100644 --- a/src/emu/debug/debugcpu.c +++ b/src/emu/debug/debugcpu.c @@ -321,7 +321,8 @@ symbol_table *debug_cpu_get_visible_symtable(running_machine *machine) symbol_table *debug_cpu_get_symtable(device_t *device) { - return cpu_get_debug_data(device)->symtable; + cpu_device *cpu = dynamic_cast<cpu_device *>(device); + return (cpu != NULL) ? cpu_get_debug_data(device)->symtable : NULL; } @@ -425,8 +426,8 @@ void debug_cpu_start_hook(device_t *device, attotime endtime) /* check for periodic updates */ if (device == global->visiblecpu && osd_ticks() > global->last_periodic_update_time + osd_ticks_per_second()/4) { - debug_view_update_all(device->machine); - debug_view_flush_updates(device->machine); + device->machine->m_debug_view->update_all(); + device->machine->m_debug_view->flush_osd_updates(); global->last_periodic_update_time = osd_ticks(); } @@ -566,8 +567,8 @@ void debug_cpu_instruction_hook(device_t *device, offs_t curpc) /* update every 100 steps until we are within 200 of the end */ else if ((cpudebug->flags & DEBUG_FLAG_STEPPING_OUT) == 0 && (cpudebug->stepsleft < 200 || cpudebug->stepsleft % 100 == 0)) { - debug_view_update_all(device->machine); - debug_view_flush_updates(device->machine); + device->machine->m_debug_view->update_all(); + device->machine->m_debug_view->flush_osd_updates(); debugger_refresh_display(device->machine); } } @@ -608,7 +609,7 @@ void debug_cpu_instruction_hook(device_t *device, offs_t curpc) global->visiblecpu = device; /* update all views */ - debug_view_update_all(device->machine); + device->machine->m_debug_view->update_all(); debugger_refresh_display(device->machine); /* wait for the debugger; during this time, disable sound output */ @@ -616,7 +617,7 @@ void debug_cpu_instruction_hook(device_t *device, offs_t curpc) while (global->execution_state == EXECUTION_STATE_STOPPED) { /* flush any pending updates before waiting again */ - debug_view_flush_updates(device->machine); + device->machine->m_debug_view->flush_osd_updates(); /* clear the memory modified flag and wait */ global->memory_modified = FALSE; @@ -629,7 +630,7 @@ void debug_cpu_instruction_hook(device_t *device, offs_t curpc) /* if something modified memory, update the screen */ if (global->memory_modified) { - debug_view_update_type(device->machine, DVT_DISASSEMBLY); + device->machine->m_debug_view->update_all(DVT_DISASSEMBLY); debugger_refresh_display(device->machine); } diff --git a/src/emu/debug/debugvw.c b/src/emu/debug/debugvw.c index c99f590d4e9..067896b64a5 100644 --- a/src/emu/debug/debugvw.c +++ b/src/emu/debug/debugvw.c @@ -4,3276 +4,641 @@ Debugger view engine. - Copyright Nicola Salmoria and the MAME Team. - Visit http://mamedev.org for licensing and usage restrictions. +**************************************************************************** + + Copyright Aaron Giles + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name 'MAME' nor the names of its contributors may be + used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. -*********************************************************************/ +***************************************************************************/ #include "emu.h" #include "debugvw.h" +#include "dvtext.h" +#include "dvstate.h" +#include "dvdisasm.h" +#include "dvmemory.h" #include "debugcmd.h" #include "debugcmt.h" #include "debugcpu.h" #include "debugcon.h" #include "express.h" -#include "textbuf.h" #include <ctype.h> -/*************************************************************************** - CONSTANTS -***************************************************************************/ - -#define DEFAULT_DASM_LINES (1000) -#define DEFAULT_DASM_WIDTH (50) -#define DASM_MAX_BYTES (16) -#define MEM_MAX_LINE_WIDTH (1024) - -enum _view_notification -{ - VIEW_NOTIFY_NONE, - VIEW_NOTIFY_VISIBLE_CHANGED, - VIEW_NOTIFY_CURSOR_CHANGED -}; -typedef enum _view_notification view_notification; - -#define REG_DIVIDER -10 -#define REG_CYCLES -11 -#define REG_BEAMX -12 -#define REG_BEAMY -13 -#define REG_FRAME -14 - - - -/*************************************************************************** - TYPE DEFINITIONS -***************************************************************************/ - -/* internal callback function pointers */ -typedef int (*view_alloc_func)(debug_view *view); -typedef void (*view_free_func)(debug_view *view); -typedef void (*view_update_func)(debug_view *view); -typedef void (*view_notify_func)(debug_view *view, view_notification type); -typedef void (*view_char_func)(debug_view *view, int chval); - - -/* debug_view_callbacks contains internal callbacks specific to a given view */ -typedef struct _debug_view_callbacks debug_view_callbacks; -struct _debug_view_callbacks -{ - view_alloc_func alloc; /* allocate memory */ - view_free_func free; /* free memory */ - view_update_func update; /* update contents */ - view_notify_func notify; /* notify of something changed */ - view_char_func handlechar; /* handle a typed character */ -}; - - -/* debug_view_section contains information about a section of a view */ -typedef struct _debug_view_section debug_view_section; -struct _debug_view_section -{ - INT32 pos; /* starting position */ - INT32 width; /* width of this section */ -}; - - -/* debug view expression contains information about an embedded expression */ -typedef struct _debug_view_expression debug_view_expression; -struct _debug_view_expression -{ - UINT64 result; /* last result from the expression */ - parsed_expression * parsed; /* parsed expression data */ - astring string; /* copy of the expression string */ - UINT8 dirty; /* true if the expression needs to be re-evaluated */ -}; - - -/* debug_view describes a single text-based view */ -class debug_view -{ -public: - /* core view data */ - debug_view * next; /* link to the next view */ - running_machine * machine; /* machine associated with this view */ - UINT8 type; /* type of view */ - void * extra_data; /* extra view-specific data */ - debug_view_callbacks cb; /* callback for this view */ - - /* OSD data */ - debug_view_osd_update_func osdupdate; /* callback for the update */ - void * osdprivate; /* OSD-managed private data */ - - /* visibility info */ - debug_view_xy visible; /* visible size (in rows and columns) */ - debug_view_xy total; /* total size (in rows and columns) */ - debug_view_xy topleft; /* top-left visible position (in rows and columns) */ - debug_view_xy cursor; /* cursor position */ - UINT8 supports_cursor; /* does this view support a cursor? */ - UINT8 cursor_visible; /* is the cursor visible? */ - - /* update info */ - UINT8 recompute; /* does this view require a recomputation? */ - UINT8 update_level; /* update level; updates when this hits 0 */ - UINT8 update_pending; /* true if there is a pending update */ - UINT8 osd_update_pending; /* true if there is a pending update */ - debug_view_char * viewdata; /* current array of view data */ - int viewdata_size; /* number of elements of the viewdata array */ -}; - - -/* debug_view_registers contains data specific to a register view */ -typedef struct _debug_view_register debug_view_register; -struct _debug_view_register -{ - UINT64 lastval; /* last value */ - UINT64 currval; /* current value */ - int regnum; /* index */ - UINT8 vallen; /* number of value chars */ - astring symbol; /* symbol */ -}; - - -typedef struct _debug_view_registers debug_view_registers; -struct _debug_view_registers -{ - const registers_subview_item *subview; /* pointer to our subview */ - int divider; /* dividing column */ - UINT64 last_update; /* execution counter at last update */ - debug_view_register reg[MAX_REGS]; /* register data */ -}; - - -/* debug_view_disasm contains data specific to a disassembly view */ -typedef struct _debug_view_disasm debug_view_disasm; -struct _debug_view_disasm -{ - const address_space *space; /* address space whose data we are disassembling */ - disasm_right_column right_column; /* right column contents */ - UINT32 backwards_steps; /* number of backwards steps */ - UINT32 dasm_width; /* width of the disassembly area */ - UINT8 * last_direct_raw; /* last direct raw value */ - UINT8 * last_direct_decrypted; /* last direct decrypted value */ - UINT32 last_change_count; /* last comment change count */ - offs_t last_pcbyte; /* last PC byte value */ - int divider1, divider2; /* left and right divider columns */ - int divider3; /* comment divider column */ - debug_view_expression expression; /* expression-related information */ - debug_view_xy allocated; /* allocated rows/columns */ - offs_t * byteaddress; /* addresses of the instructions */ - char * dasm; /* disassembled instructions */ -}; - - -/* debug_view_memory contains data specific to a memory view */ -typedef struct _debug_view_memory debug_view_memory; -struct _debug_view_memory -{ - const memory_subview_item *subviewlist; /* linked list of memory subviews */ - const memory_subview_item *desc; /* description of our current subview */ - debug_view_expression expression; /* expression describing the start address */ - UINT32 chunks_per_row; /* number of chunks displayed per line */ - UINT8 bytes_per_chunk; /* bytes per chunk */ - UINT8 reverse_view; /* reverse-endian view? */ - UINT8 ascii_view; /* display ASCII characters? */ - UINT8 no_translation; /* don't run addresses through the cpu translation hook */ - debug_view_section section[3]; /* (derived) 3 sections to manage */ - offs_t maxaddr; /* (derived) maximum address to display */ - UINT32 bytes_per_row; /* (derived) number of bytes displayed per line */ - UINT32 byte_offset; /* (derived) offset of starting visible byte */ - char addrformat[10]; /* (derived) format string to use to print addresses */ -}; - - -/* debug_view_textbuf contains data specific to a textbuffer view */ -typedef struct _debug_view_textbuf debug_view_textbuf; -struct _debug_view_textbuf -{ - text_buffer * textbuf; /* pointer to the text buffer */ - UINT8 at_bottom; /* are we tracking new stuff being added? */ - UINT32 topseq; /* sequence number of the top line */ -}; - - -/* memory_view_pos contains positioning data for memory views */ -typedef struct _memory_view_pos memory_view_pos; -struct _memory_view_pos -{ - UINT8 spacing; /* spacing between each entry */ - UINT8 shift[24]; /* shift for each character */ -}; - - -/* debugvw_priate contains internal global data for this module */ -/* In mame.h: typedef struct _debugvw_private debugvw_priate; */ -struct _debugvw_private -{ - debug_view * viewlist; /* list of views */ - const registers_subview_item *registers_subviews;/* linked list of registers subviews */ - const disasm_subview_item *disasm_subviews; /* linked list of disassembly subviews */ -}; - - - -/*************************************************************************** - LOCAL VARIABLES -***************************************************************************/ - -static const memory_view_pos memory_pos_table[9] = -{ - /* 0 bytes per chunk: */ { 0, { 0 } }, - /* 1 byte per chunk: 00 11 22 33 44 55 66 77 */ { 3, { 0x04, 0x00, 0x80 } }, - /* 2 bytes per chunk: 0011 2233 4455 6677 */ { 6, { 0x8c, 0x0c, 0x08, 0x04, 0x00, 0x80 } }, - /* 3 bytes per chunk: */ { 0, { 0 } }, - /* 4 bytes per chunk: 00112233 44556677 */ { 12, { 0x9c, 0x9c, 0x1c, 0x18, 0x14, 0x10, 0x0c, 0x08, 0x04, 0x00, 0x80, 0x80 } }, - /* 5 bytes per chunk: */ { 0, { 0 } }, - /* 6 bytes per chunk: */ { 0, { 0 } }, - /* 7 bytes per chunk: */ { 0, { 0 } }, - /* 8 bytes per chunk: 0011223344556677 */ { 24, { 0xbc, 0xbc, 0xbc, 0xbc, 0x3c, 0x38, 0x34, 0x30, 0x2c, 0x28, 0x24, 0x20, 0x1c, 0x18, 0x14, 0x10, 0x0c, 0x08, 0x04, 0x00, 0x80, 0x80, 0x80, 0x80 } } -}; - - - -/*************************************************************************** - FUNCTION PROTOTYPES -***************************************************************************/ - -static void debug_view_exit(running_machine *machine); - -static int textbuf_view_alloc(debug_view *view, text_buffer *textbuf); -static void textbuf_view_free(debug_view *view); -static void textbuf_view_notify(debug_view *view, view_notification type); -static void textbuf_view_update(debug_view *view); - -static int console_view_alloc(debug_view *view); - -static int log_view_alloc(debug_view *view); - -static const registers_subview_item *registers_view_enumerate_subviews(running_machine *machine); -static int registers_view_alloc(debug_view *view); -static void registers_view_free(debug_view *view); -static void registers_view_update(debug_view *view); - -static const disasm_subview_item *disasm_view_enumerate_subviews(running_machine *machine); -static int disasm_view_alloc(debug_view *view); -static void disasm_view_free(debug_view *view); -static void disasm_view_notify(debug_view *view, view_notification type); -static void disasm_view_update(debug_view *view); -static void disasm_view_char(debug_view *view, int chval); - -static const memory_subview_item *memory_view_enumerate_subviews(running_machine *machine); -static int memory_view_alloc(debug_view *view); -static void memory_view_free(debug_view *view); -static void memory_view_notify(debug_view *view, view_notification type); -static void memory_view_update(debug_view *view); -static void memory_view_char(debug_view *view, int chval); -static void memory_view_recompute(debug_view *view); -static int memory_view_needs_recompute(debug_view *view); -static void memory_view_get_cursor_pos(debug_view *view, offs_t *address, UINT8 *shift); -static void memory_view_set_cursor_pos(debug_view *view, offs_t address, UINT8 shift); -static int memory_view_read(debug_view_memory *memdata, UINT8 size, offs_t offs, UINT64 *data); -static void memory_view_write(debug_view_memory *memdata, UINT8 size, offs_t offs, UINT64 data); - -static const debug_view_callbacks callback_table[] = -{ - { NULL, NULL, NULL, NULL, NULL }, - { console_view_alloc, textbuf_view_free, textbuf_view_update, textbuf_view_notify, NULL }, - { registers_view_alloc, registers_view_free, registers_view_update, NULL, NULL }, - { disasm_view_alloc, disasm_view_free, disasm_view_update, disasm_view_notify, disasm_view_char }, - { memory_view_alloc, memory_view_free, memory_view_update, memory_view_notify, memory_view_char }, - { log_view_alloc, textbuf_view_free, textbuf_view_update, textbuf_view_notify, NULL } -}; - - - -/*************************************************************************** - INLINE FUNCTIONS -***************************************************************************/ +//************************************************************************** +// DEBUG VIEW SOURCE +//************************************************************************** -/*------------------------------------------------- - in_section - return TRUE if the given X - coordinate is within a section --------------------------------------------------*/ +//------------------------------------------------- +// debug_view_source - constructor +//------------------------------------------------- -INLINE int in_section(int x, const debug_view_section *section) +debug_view_source::debug_view_source(const char *name, device_t *device) + : m_next(NULL), + m_name(name), + m_device(device) { - return (x >= section->pos && x < section->pos + section->width); } -/*------------------------------------------------- - adjust_visible_x_for_cursor - adjust a view's - visible X position to ensure the cursor is - visible --------------------------------------------------*/ +//------------------------------------------------- +// ~debug_view_source - destructor +//------------------------------------------------- -INLINE void adjust_visible_x_for_cursor(debug_view *view) +debug_view_source::~debug_view_source() { - if (view->cursor.x < view->topleft.x) - view->topleft.x = view->cursor.x; - else if (view->cursor.x >= view->topleft.x + view->visible.x - 1) - view->topleft.x = view->cursor.x - view->visible.x + 2; } -/*------------------------------------------------- - adjust_visible_y_for_cursor - adjust a view's - visible Y position to ensure the cursor is - visible --------------------------------------------------*/ - -INLINE void adjust_visible_y_for_cursor(debug_view *view) -{ - if (view->cursor.y < view->topleft.y) - view->topleft.y = view->cursor.y; - else if (view->cursor.y >= view->topleft.y + view->visible.y - 1) - view->topleft.y = view->cursor.y - view->visible.y + 2; -} +//************************************************************************** +// DEBUG VIEW SOURCE LIST +//************************************************************************** +//------------------------------------------------- +// debug_view_source_list - constructor +//------------------------------------------------- -/*************************************************************************** - INITIALIZATION AND SHUTDOWN -***************************************************************************/ - -/*------------------------------------------------- - debug_view_init - initializes the view system --------------------------------------------------*/ - -void debug_view_init(running_machine *machine) +debug_view_source_list::debug_view_source_list(running_machine &machine) + : m_machine(machine), + m_head(NULL), + m_tail(NULL), + m_count(0) { - debugvw_private *global; - - /* allocate memory for our globals */ - global = machine->debugvw_data = auto_alloc_clear(machine, debugvw_private); - - /* register for some manual cleanup */ - add_exit_callback(machine, debug_view_exit); - - /* build a list of disassembly and memory subviews */ - global->registers_subviews = registers_view_enumerate_subviews(machine); - global->disasm_subviews = disasm_view_enumerate_subviews(machine); } -/*------------------------------------------------- - debug_view_exit - exits the view system --------------------------------------------------*/ +//------------------------------------------------- +// ~debug_view_source_list - destructor +//------------------------------------------------- -static void debug_view_exit(running_machine *machine) +debug_view_source_list::~debug_view_source_list() { - debugvw_private *global = machine->debugvw_data; - - /* kill all the views */ - while (global->viewlist != NULL) - debug_view_free(global->viewlist); + reset(); } +//------------------------------------------------- +// index - return the index of a source +//------------------------------------------------- -/*************************************************************************** - VIEW CREATION/DELETION -***************************************************************************/ - -/*------------------------------------------------- - debug_view_alloc - allocate a new debug - view --------------------------------------------------*/ - -debug_view *debug_view_alloc(running_machine *machine, int type, debug_view_osd_update_func osdupdate, void *osdprivate) +int debug_view_source_list::index(const debug_view_source &source) const { - debugvw_private *global = machine->debugvw_data; - debug_view *view; - - assert(type >= 0 && type < ARRAY_LENGTH(callback_table)); - - /* allocate memory for the view */ - view = auto_alloc_clear(machine, debug_view); - - /* set the view type information */ - view->machine = machine; - view->type = type; - view->cb = callback_table[type]; - view->osdupdate = osdupdate; - view->osdprivate = osdprivate; - - /* set up some reasonable defaults */ - view->visible.x = view->total.x = 10; - view->visible.y = view->total.y = 10; - - /* allocate memory for the buffer */ - view->viewdata_size = view->visible.y * view->visible.x; - view->viewdata = auto_alloc_array(machine, debug_view_char, view->viewdata_size); - - /* allocate extra memory */ - if (view->cb.alloc != NULL && !(*view->cb.alloc)(view)) + int result = 0; + for (debug_view_source *cursource = m_head; cursource != NULL; cursource = cursource->m_next) { - auto_free(machine, view->viewdata); - auto_free(machine, view); - return NULL; - } - - /* link it in */ - view->next = global->viewlist; - global->viewlist = view; - - /* require a recomputation on the first update */ - view->recompute = TRUE; - view->update_pending = TRUE; - - return view; -} - - -/*------------------------------------------------- - debug_view_free - free a debug view --------------------------------------------------*/ - -void debug_view_free(debug_view *view) -{ - debugvw_private *global = view->machine->debugvw_data; - debug_view **viewptr; - - /* find the view */ - for (viewptr = &global->viewlist; *viewptr != NULL; viewptr = &(*viewptr)->next) - if (*viewptr == view) - { - /* unlink */ - *viewptr = view->next; - - /* free memory */ - if (view->cb.free != NULL) - (*view->cb.free)(view); - if (view->viewdata != NULL) - auto_free(view->machine, view->viewdata); - auto_free(view->machine, view); + if (cursource == &source) break; - } -} - - - -/*************************************************************************** - UPDATE MANAGEMENT -***************************************************************************/ - -/*------------------------------------------------- - debug_view_begin_update - bracket a sequence - of changes so that only one update occurs --------------------------------------------------*/ - -void debug_view_begin_update(debug_view *view) -{ - /* bump the level */ - view->update_level++; -} - - -/*------------------------------------------------- - debug_view_end_update - bracket a sequence - of changes so that only one update occurs --------------------------------------------------*/ - -void debug_view_end_update(debug_view *view) -{ - /* if we hit zero, call the update function */ - if (view->update_level == 1) - { - while (view->update_pending) - { - int size; - - /* no longer pending, but flag for the OSD */ - view->update_pending = FALSE; - view->osd_update_pending = TRUE; - - /* resize the viewdata if needed */ - size = view->visible.x * view->visible.y; - if (size > view->viewdata_size) - { - view->viewdata_size = size; - auto_free(view->machine, view->viewdata); - view->viewdata = auto_alloc_array(view->machine, debug_view_char, view->viewdata_size); - } - - /* update the view */ - if (view->cb.update != NULL) - (*view->cb.update)(view); - } + result++; } - - /* decrement the level */ - view->update_level--; -} - - -/*------------------------------------------------- - debug_view_flush_updates - force all updates - to notify the OSD --------------------------------------------------*/ - -void debug_view_flush_updates(running_machine *machine) -{ - debugvw_private *global = machine->debugvw_data; - debug_view *view; - - /* skip if we're not ready yet */ - if (global == NULL) - return; - - /* loop over each view and force an update */ - for (view = global->viewlist; view != NULL; view = view->next) - if (view->osd_update_pending) - { - /* update the owner */ - if (view->osdupdate != NULL) - (*view->osdupdate)(view, view->osdprivate); - view->osd_update_pending = FALSE; - } + return result; } -/*------------------------------------------------- - debug_view_update_all - force all views to - refresh --------------------------------------------------*/ +//------------------------------------------------- +// by_index - return a source given an index +//------------------------------------------------- -void debug_view_update_all(running_machine *machine) +const debug_view_source *debug_view_source_list::by_index(int index) const { - debugvw_private *global = machine->debugvw_data; - debug_view *view; - - /* skip if we're not ready yet */ - if (global == NULL) - return; - - /* loop over each view and force an update */ - for (view = global->viewlist; view != NULL; view = view->next) - { - debug_view_begin_update(view); - view->update_pending = TRUE; - debug_view_end_update(view); - } -} - - -/*------------------------------------------------- - debug_view_update_type - force all views of - a given type to refresh --------------------------------------------------*/ - -void debug_view_update_type(running_machine *machine, int type) -{ - debugvw_private *global = machine->debugvw_data; - debug_view *view; - - /* skip if we're not ready yet */ - if (global == NULL) - return; - - /* loop over each view and force an update */ - for (view = global->viewlist; view != NULL; view = view->next) - if (view->type == type) - { - debug_view_begin_update(view); - view->recompute = view->update_pending = TRUE; - debug_view_end_update(view); - } -} - - - -/*************************************************************************** - STANDARD VIEW PROPERTIES -***************************************************************************/ - -/*------------------------------------------------- - debug_view_get_chars - return a pointer to - a 2-dimentional array of characters that - represent the visible area of the view --------------------------------------------------*/ - -const debug_view_char *debug_view_get_chars(debug_view *view) -{ - return view->viewdata; -} - - -/*------------------------------------------------- - debug_view_type_character - type a character - into a view --------------------------------------------------*/ - -void debug_view_type_character(debug_view *view, int character) -{ - /* if the view has a character handler, forward it on */ - if (view->cb.handlechar != NULL) - (*view->cb.handlechar)(view, character); -} - - - -/*************************************************************************** - STANDARD VIEW SIZING -***************************************************************************/ - -/*------------------------------------------------- - debug_view_get_total_size - return the total - view size in rows and columns --------------------------------------------------*/ - -debug_view_xy debug_view_get_total_size(debug_view *view) -{ - debug_view_begin_update(view); - debug_view_end_update(view); - return view->total; -} - - -/*------------------------------------------------- - debug_view_get_visible_size - return the - visible size in rows and columns --------------------------------------------------*/ - -debug_view_xy debug_view_get_visible_size(debug_view *view) -{ - debug_view_begin_update(view); - debug_view_end_update(view); - return view->visible; -} - - -/*------------------------------------------------- - debug_view_get_visible_position - return the - top left position of the visible area in rows - and columns --------------------------------------------------*/ - -debug_view_xy debug_view_get_visible_position(debug_view *view) -{ - debug_view_begin_update(view); - debug_view_end_update(view); - return view->topleft; -} - - -/*------------------------------------------------- - debug_view_set_visible_size - set the visible - size in rows and columns --------------------------------------------------*/ - -void debug_view_set_visible_size(debug_view *view, debug_view_xy size) -{ - if (size.x != view->visible.x || size.y != view->visible.y) - { - debug_view_begin_update(view); - view->visible = size; - view->update_pending = TRUE; - if (view->cb.notify != NULL) - (*view->cb.notify)(view, VIEW_NOTIFY_VISIBLE_CHANGED); - debug_view_end_update(view); - } -} - - -/*------------------------------------------------- - debug_view_set_visible_position - set the - top left position of the visible area in rows - and columns --------------------------------------------------*/ - -void debug_view_set_visible_position(debug_view *view, debug_view_xy pos) -{ - if (pos.x != view->topleft.x || pos.y != view->topleft.y) - { - debug_view_begin_update(view); - view->topleft = pos; - view->update_pending = TRUE; - if (view->cb.notify != NULL) - (*view->cb.notify)(view, VIEW_NOTIFY_VISIBLE_CHANGED); - debug_view_end_update(view); - } -} - - - -/*************************************************************************** - STANDARD VIEW CURSOR MANAGEMENT -***************************************************************************/ - -/*------------------------------------------------- - debug_view_get_cursor_position - return the - current cursor position as a row and column --------------------------------------------------*/ - -debug_view_xy debug_view_get_cursor_position(debug_view *view) -{ - debug_view_begin_update(view); - debug_view_end_update(view); - return view->cursor; -} - - -/*------------------------------------------------- - debug_view_get_cursor_supported - return TRUE - if a cursor is supported for this view type --------------------------------------------------*/ - -int debug_view_get_cursor_supported(debug_view *view) -{ - debug_view_begin_update(view); - debug_view_end_update(view); - return view->supports_cursor; -} - - -/*------------------------------------------------- - debug_view_get_cursor_visible - return TRUE - if a cursor is currently visible --------------------------------------------------*/ - -int debug_view_get_cursor_visible(debug_view *view) -{ - debug_view_begin_update(view); - debug_view_end_update(view); - return view->cursor_visible; -} - - -/*------------------------------------------------- - debug_view_set_cursor_position - set the - current cursor position as a row and column --------------------------------------------------*/ - -void debug_view_set_cursor_position(debug_view *view, debug_view_xy pos) -{ - if (pos.x != view->cursor.x || pos.y != view->cursor.y) - { - debug_view_begin_update(view); - view->cursor = pos; - view->update_pending = TRUE; - if (view->cb.notify != NULL) - (*view->cb.notify)(view, VIEW_NOTIFY_CURSOR_CHANGED); - debug_view_end_update(view); - } -} - - -/*------------------------------------------------- - debug_view_set_cursor_visible - set the - visible state of the cursor --------------------------------------------------*/ - -void debug_view_set_cursor_visible(debug_view *view, int visible) -{ - if (visible != view->cursor_visible) - { - debug_view_begin_update(view); - view->cursor_visible = visible; - view->update_pending = TRUE; - if (view->cb.notify != NULL) - (*view->cb.notify)(view, VIEW_NOTIFY_CURSOR_CHANGED); - debug_view_end_update(view); - } -} - - - -/*************************************************************************** - GENERIC EXPRESSION HANDLING -***************************************************************************/ - -/*------------------------------------------------- - debug_view_expression_alloc - allocate data - for an expression --------------------------------------------------*/ - -static void debug_view_expression_alloc(debug_view_expression *expression) -{ -} - - -/*------------------------------------------------- - debug_view_expression_free - free data - allocated for an expression --------------------------------------------------*/ - -static void debug_view_expression_free(debug_view_expression *expression) -{ - if (expression->parsed != NULL) - expression_free(expression->parsed); -} - - -/*------------------------------------------------- - debug_view_expression_set - set a new - expression string --------------------------------------------------*/ - -static void debug_view_expression_set(debug_view_expression *expression, const char *string) -{ - expression->string.cpy(string); - expression->dirty = TRUE; -} - - -/*------------------------------------------------- - debug_view_expression_changed_value - update an - expression and return TRUE if its value has - changed --------------------------------------------------*/ - -static int debug_view_expression_changed_value(debug_view *view, debug_view_expression *expression, device_t *cpu) -{ - int changed = expression->dirty; - EXPRERR exprerr; - - /* if dirty, re-evaluate */ - if (expression->dirty) - { - symbol_table *symtable = (cpu != NULL) ? debug_cpu_get_symtable(cpu) : debug_cpu_get_global_symtable(view->machine); - parsed_expression *expr; - - /* parse the new expression */ - exprerr = expression_parse(expression->string, symtable, &debug_expression_callbacks, view->machine, &expr); - - /* if it worked, update the expression */ - if (exprerr == EXPRERR_NONE) - { - if (expression->parsed != NULL) - expression_free(expression->parsed); - expression->parsed = expr; - } - } - - /* if we have a parsed expression, evalute it */ - if (expression->parsed != NULL) - { - UINT64 oldresult = expression->result; - - /* recompute the value of the expression */ - exprerr = expression_execute(expression->parsed, &expression->result); - changed |= (expression->result != oldresult); - } - - /* expression no longer dirty by definition */ - expression->dirty = FALSE; - return changed; -} - - - -/*************************************************************************** - TEXT BUFFER-BASED VIEWS -***************************************************************************/ - -/*------------------------------------------------- - console_view_alloc - allocate memory for the - console view --------------------------------------------------*/ - -static int console_view_alloc(debug_view *view) -{ - return textbuf_view_alloc(view, debug_console_get_textbuf()); -} - - -/*------------------------------------------------- - log_view_alloc - allocate memory for the log - view --------------------------------------------------*/ - -static int log_view_alloc(debug_view *view) -{ - return textbuf_view_alloc(view, debug_errorlog_get_textbuf()); -} - - -/*------------------------------------------------- - textbuf_view_alloc - allocate memory for a - text buffer-based view --------------------------------------------------*/ - -static int textbuf_view_alloc(debug_view *view, text_buffer *textbuf) -{ - debug_view_textbuf *textdata; - - /* allocate memory */ - textdata = auto_alloc_clear(view->machine, debug_view_textbuf); - - /* by default we track live */ - textdata->textbuf = textbuf; - textdata->at_bottom = TRUE; - - /* stash the extra data pointer */ - view->extra_data = textdata; - return TRUE; -} - - -/*------------------------------------------------- - textbuf_view_free - free memory for a text - buffer-based view --------------------------------------------------*/ - -static void textbuf_view_free(debug_view *view) -{ - debug_view_textbuf *textdata = (debug_view_textbuf *)view->extra_data; - - /* free any memory we callocated */ - auto_free(view->machine, textdata); - view->extra_data = NULL; + if (m_head == NULL) + return NULL; + const debug_view_source *result; + for (result = m_head; index > 0 && result->m_next != NULL; result = result->m_next) + index--; + return result; } -/*------------------------------------------------- - textbuf_view_update - update a text buffer- - based view --------------------------------------------------*/ +//------------------------------------------------- +// reset - free all the view_sources +//------------------------------------------------- -static void textbuf_view_update(debug_view *view) +void debug_view_source_list::reset() { - debug_view_textbuf *textdata = (debug_view_textbuf *)view->extra_data; - debug_view_char *dest = view->viewdata; - UINT32 curseq = 0, row; - - /* update the console info */ - view->total.x = text_buffer_max_width(textdata->textbuf); - view->total.y = text_buffer_num_lines(textdata->textbuf); - if (view->total.x < 80) - view->total.x = 80; - - /* determine the starting sequence number */ - if (!textdata->at_bottom) - { - curseq = textdata->topseq; - if (!text_buffer_get_seqnum_line(textdata->textbuf, curseq)) - textdata->at_bottom = TRUE; - } - if (textdata->at_bottom) - { - curseq = text_buffer_line_index_to_seqnum(textdata->textbuf, view->total.y - 1); - if (view->total.y < view->visible.y) - curseq -= view->total.y - 1; - else - curseq -= view->visible.y - 1; - } - view->topleft.y = curseq - text_buffer_line_index_to_seqnum(textdata->textbuf, 0); - - /* loop over visible rows */ - for (row = 0; row < view->visible.y; row++) + // free from the head + while (m_head != NULL) { - const char *line = text_buffer_get_seqnum_line(textdata->textbuf, curseq++); - UINT32 col = 0; - - /* if this visible row is valid, add it to the buffer */ - if (line != NULL) - { - size_t len = strlen(line); - UINT32 effcol = view->topleft.x; - - /* copy data */ - while (col < view->visible.x && effcol < len) - { - dest->byte = line[effcol++]; - dest->attrib = DCA_NORMAL; - dest++; - col++; - } - } - - /* fill the rest with blanks */ - while (col < view->visible.x) - { - dest->byte = ' '; - dest->attrib = DCA_NORMAL; - dest++; - col++; - } + debug_view_source *source = m_head; + m_head = source->m_next; + auto_free(&m_machine, source); } + + // reset the tail pointer and index + m_tail = NULL; + m_count = 0; } -/*------------------------------------------------- - textbuf_view_notify - handle notification of - updates to visible area --------------------------------------------------*/ +//------------------------------------------------- +// append - add a view_source to the end of the +// list +//------------------------------------------------- -static void textbuf_view_notify(debug_view *view, view_notification type) +void debug_view_source_list::append(debug_view_source &source) { - debug_view_textbuf *textdata = (debug_view_textbuf *)view->extra_data; - - if (type == VIEW_NOTIFY_VISIBLE_CHANGED) - { - /* if the bottom line is visible, just track the bottom */ - textdata->at_bottom = (view->total.y >= view->topleft.y && view->total.y <= view->topleft.y + view->visible.y); + // set the next and index values + source.m_next = NULL; - /* otherwise, track the seqence number of the top line */ - if (!textdata->at_bottom) - textdata->topseq = text_buffer_line_index_to_seqnum(textdata->textbuf, view->topleft.y); - } + // append to the end + if (m_tail == NULL) + m_head = m_tail = &source; + else + m_tail->m_next = &source; + m_tail = &source; + m_count++; } +//------------------------------------------------- +// match_device - find the first view that +// matches the given device +//------------------------------------------------- -/*************************************************************************** - REGISTERS VIEW -***************************************************************************/ - -/*------------------------------------------------- - registers_view_enumerate_subviews - enumerate - all possible subviews for a registers view --------------------------------------------------*/ - -static const registers_subview_item *registers_view_enumerate_subviews(running_machine *machine) +const debug_view_source *debug_view_source_list::match_device(device_t *device) const { - registers_subview_item *head = NULL; - registers_subview_item **tailptr = &head; - int curindex = 0; - - /* iterate over CPUs with program address spaces */ - device_state_interface *state = NULL; - for (bool gotone = machine->devicelist.first(state); gotone; gotone = state->next(state)) - { - registers_subview_item *subview = auto_alloc(machine, registers_subview_item); - - /* populate the subview */ - subview->next = NULL; - subview->index = curindex++; - subview->name.printf("CPU '%s' (%s)", state->device().tag(), state->device().name()); - subview->stateintf = state; - state->device().interface(subview->execintf); - - /* add to the list */ - *tailptr = subview; - tailptr = &subview->next; - } - - return head; + for (debug_view_source *source = m_head; source != NULL; source = source->m_next) + if (device == source->m_device) + return source; + return m_head; } -/*------------------------------------------------- - registers_view_alloc - allocate memory for the - registers view --------------------------------------------------*/ - -static int registers_view_alloc(debug_view *view) -{ - /* fail if no available subviews */ - if (view->machine->debugvw_data->registers_subviews == NULL) - return FALSE; - - /* allocate memory */ - debug_view_registers *regdata = auto_alloc_clear(view->machine, debug_view_registers); - - /* default to the first subview */ - regdata->subview = view->machine->debugvw_data->registers_subviews; - - /* stash the extra data pointer */ - view->extra_data = regdata; - return TRUE; -} +//************************************************************************** +// DEBUG VIEW +//************************************************************************** -/*------------------------------------------------- - registers_view_free - free memory for the - registers view --------------------------------------------------*/ +//------------------------------------------------- +// debug_view - constructor +//------------------------------------------------- -static void registers_view_free(debug_view *view) +debug_view::debug_view(running_machine &machine, debug_view_type type, debug_view_osd_update_func osdupdate, void *osdprivate) + : m_next(NULL), + m_machine(machine), + m_type(type), + m_source(NULL), + m_source_list(machine), + m_osdupdate(osdupdate), + m_osdprivate(osdprivate), + m_visible(10,10), + m_total(10,10), + m_topleft(0,0), + m_cursor(0,0), + m_supports_cursor(false), + m_cursor_visible(false), + m_recompute(true), + m_update_level(0), + m_update_pending(true), + m_osd_update_pending(true), + m_viewdata(NULL), + m_viewdata_size(0) { - debug_view_registers *regdata = (debug_view_registers *)view->extra_data; - - /* free any memory we callocated */ - if (regdata != NULL) - auto_free(view->machine, regdata); - view->extra_data = NULL; + // allocate memory for the buffer + m_viewdata_size = m_visible.y * m_visible.x; + m_viewdata = auto_alloc_array(&machine, debug_view_char, m_viewdata_size); } -/*------------------------------------------------- - registers_view_recompute - recompute all info - for the registers view --------------------------------------------------*/ +//------------------------------------------------- +// ~debug_view - destructor +//------------------------------------------------- -static void registers_view_recompute(debug_view *view) +debug_view::~debug_view() { - debug_view_registers *regdata = (debug_view_registers *)view->extra_data; - int maxtaglen, maxvallen; - - /* reset the view parameters */ - view->topleft.y = 0; - view->topleft.x = 0; - view->total.y = 0; - view->total.x = 0; - regdata->divider = 0; - - /* add a cycles entry: cycles:99999999 */ - maxtaglen = 0; - maxvallen = 0; - if (regdata->subview->execintf != NULL) - { - regdata->reg[view->total.y].lastval = - regdata->reg[view->total.y].currval = 0; - regdata->reg[view->total.y].regnum = REG_CYCLES; - regdata->reg[view->total.y].symbol.cpy("cycles"); - regdata->reg[view->total.y].vallen = 8; - maxtaglen = regdata->reg[view->total.y].symbol.len(); - maxvallen = regdata->reg[view->total.y].vallen; - view->total.y++; - } - - /* add a beam entry: beamx:123 */ - regdata->reg[view->total.y].lastval = - regdata->reg[view->total.y].currval = 0; - regdata->reg[view->total.y].regnum = REG_BEAMX; - regdata->reg[view->total.y].symbol.cpy("beamx"); - regdata->reg[view->total.y].vallen = 3; - maxtaglen = MAX(maxtaglen, regdata->reg[view->total.y].symbol.len()); - maxvallen = MAX(maxvallen, regdata->reg[view->total.y].vallen); - view->total.y++; - - /* add a beam entry: beamy:456 */ - regdata->reg[view->total.y].lastval = - regdata->reg[view->total.y].currval = 0; - regdata->reg[view->total.y].regnum = REG_BEAMY; - regdata->reg[view->total.y].symbol.cpy("beamy"); - regdata->reg[view->total.y].vallen = 3; - maxtaglen = MAX(maxtaglen, regdata->reg[view->total.y].symbol.len()); - maxvallen = MAX(maxvallen, regdata->reg[view->total.y].vallen); - view->total.y++; - - /* add a beam entry: frame:123456 */ - regdata->reg[view->total.y].lastval = - regdata->reg[view->total.y].currval = 0; - regdata->reg[view->total.y].regnum = REG_FRAME; - regdata->reg[view->total.y].symbol.cpy("frame"); - regdata->reg[view->total.y].vallen = 6; - maxtaglen = MAX(maxtaglen, regdata->reg[view->total.y].symbol.len()); - maxvallen = MAX(maxvallen, regdata->reg[view->total.y].vallen); - view->total.y++; - - /* add a flags entry: flags:xxxxxxxx */ - regdata->reg[view->total.y].lastval = - regdata->reg[view->total.y].currval = 0; - regdata->reg[view->total.y].regnum = STATE_GENFLAGS; - regdata->reg[view->total.y].symbol.cpy("flags"); - regdata->reg[view->total.y].vallen = regdata->subview->stateintf->state_string_max_length(regdata->reg[view->total.y].regnum); - maxtaglen = MAX(maxtaglen, regdata->reg[view->total.y].symbol.len()); - maxvallen = MAX(maxvallen, regdata->reg[view->total.y].vallen); - view->total.y++; - - /* add a divider entry */ - regdata->reg[view->total.y].lastval = - regdata->reg[view->total.y].currval = 0; - regdata->reg[view->total.y].regnum = REG_DIVIDER; - view->total.y++; - - /* add all registers into it */ - for (const device_state_entry *entry = regdata->subview->stateintf->state_first(); entry != NULL; entry = entry->next()) - if (entry->visible()) - { - /* note the register number and info */ - regdata->reg[view->total.y].lastval = - regdata->reg[view->total.y].currval = regdata->subview->stateintf->state_value(entry->index()); - regdata->reg[view->total.y].regnum = entry->index(); - regdata->reg[view->total.y].symbol.cpy(entry->symbol()); - regdata->reg[view->total.y].vallen = regdata->subview->stateintf->state_string_max_length(entry->index()); - maxtaglen = MAX(maxtaglen, regdata->reg[view->total.y].symbol.len()); - maxvallen = MAX(maxvallen, regdata->reg[view->total.y].vallen); - view->total.y++; - } - - /* set the current divider and total cols */ - regdata->divider = 1 + maxtaglen + 1; - view->total.x = 1 + maxtaglen + 2 + maxvallen + 1; - - /* no longer need to recompute */ - view->recompute = FALSE; } -/*------------------------------------------------- - registers_view_update - update the contents of - the register view --------------------------------------------------*/ +//------------------------------------------------- +// end_update - bracket a sequence of changes so +// that only one update occurs +//------------------------------------------------- -static void registers_view_update(debug_view *view) +void debug_view::end_update() { - screen_device *screen = view->machine->primary_screen; - debug_view_registers *regdata = (debug_view_registers *)view->extra_data; - debug_view_char *dest = view->viewdata; - UINT64 total_cycles; - UINT32 row, i; - - /* if our assumptions changed, revisit them */ - if (view->recompute) - registers_view_recompute(view); - - /* cannot update if no active CPU */ - total_cycles = 0; - if (regdata->subview->execintf != NULL) - total_cycles = regdata->subview->execintf->total_cycles(); - - /* loop over visible rows */ - for (row = 0; row < view->visible.y; row++) + /* if we hit zero, call the update function */ + if (m_update_level == 1) { - UINT32 effrow = view->topleft.y + row; - UINT32 col = 0; - - /* if this visible row is valid, add it to the buffer */ - if (effrow < view->total.y) + while (m_update_pending) { - debug_view_register *reg = ®data->reg[effrow]; - UINT32 effcol = view->topleft.x; - UINT8 attrib = DCA_NORMAL; - UINT32 len = 0; - astring valstr; - - /* get the effective string */ - if (reg->regnum >= REG_FRAME && reg->regnum <= REG_DIVIDER) - { - reg->lastval = reg->currval; - switch (reg->regnum) - { - case REG_DIVIDER: - reg->vallen = 0; - reg->symbol.reset(); - for (i = 0; i < view->total.x; i++) - reg->symbol.cat("-"); - break; - - case REG_CYCLES: - if (regdata->subview->execintf != NULL) - { - reg->currval = regdata->subview->execintf->cycles_remaining(); - valstr.printf("%-8d", (UINT32)reg->currval); - } - break; - - case REG_BEAMX: - if (screen != NULL) - { - reg->currval = screen->hpos(); - valstr.printf("%3d", (UINT32)reg->currval); - } - break; - - case REG_BEAMY: - if (screen != NULL) - { - reg->currval = screen->vpos(); - valstr.printf("%3d", (UINT32)reg->currval); - } - break; - - case REG_FRAME: - if (screen != NULL) - { - reg->currval = screen->frame_number(); - valstr.printf("%3d", (UINT32)reg->currval); - } - break; - } - } - else - { - if (regdata->last_update != total_cycles) - reg->lastval = reg->currval; - reg->currval = regdata->subview->stateintf->state_value(reg->regnum); - regdata->subview->stateintf->state_string(reg->regnum, valstr); - } + // no longer pending, but flag for the OSD + m_update_pending = false; + m_osd_update_pending = true; - /* see if we changed */ - if (reg->lastval != reg->currval) - attrib = DCA_CHANGED; - - /* build up a string */ - char temp[256]; - if (reg->symbol.len() < regdata->divider - 1) + // resize the viewdata if needed + int size = m_visible.x * m_visible.y; + if (size > m_viewdata_size) { - memset(&temp[len], ' ', regdata->divider - 1 - reg->symbol.len()); - len += regdata->divider - 1 - reg->symbol.len(); + m_viewdata_size = size; + auto_free(&m_machine, m_viewdata); + m_viewdata = auto_alloc_array(&m_machine, debug_view_char, m_viewdata_size); } - memcpy(&temp[len], reg->symbol.cstr(), reg->symbol.len()); - len += reg->symbol.len(); - - temp[len++] = ' '; - temp[len++] = ' '; - - memcpy(&temp[len], valstr.cstr(), reg->vallen); - len += reg->vallen; - - temp[len++] = ' '; - temp[len] = 0; - - /* copy data */ - while (col < view->visible.x && effcol < len) - { - dest->byte = temp[effcol++]; - dest->attrib = attrib | ((effcol <= regdata->divider) ? DCA_ANCILLARY : DCA_NORMAL); - dest++; - col++; - } - } - - /* fill the rest with blanks */ - while (col < view->visible.x) - { - dest->byte = ' '; - dest->attrib = DCA_NORMAL; - dest++; - col++; + // update the view + view_update(); } } - /* remember the last update */ - regdata->last_update = total_cycles; + // decrement the level + m_update_level--; } -/*------------------------------------------------- - registers_view_get_subview_list - return a - linked list of subviews --------------------------------------------------*/ +//------------------------------------------------- +// flush_osd_updates - notify the OSD of any +// pending updates +//------------------------------------------------- -const registers_subview_item *registers_view_get_subview_list(debug_view *view) +void debug_view::flush_osd_updates() { - assert(view->type == DVT_REGISTERS); - return view->machine->debugvw_data->registers_subviews; + if (m_osd_update_pending && m_osdupdate != NULL) + (*m_osdupdate)(*this, m_osdprivate); + m_osd_update_pending = false; } -/*------------------------------------------------- - registers_view_get_subview - return the current - subview index --------------------------------------------------*/ +//------------------------------------------------- +// set_visible_size - set the visible size in +// rows and columns +//-------------------------------------------------*/ -int registers_view_get_subview(debug_view *view) +void debug_view::set_visible_size(debug_view_xy size) { - debug_view_registers *regdata = (debug_view_registers *)view->extra_data; - const registers_subview_item *subview; - int index = 0; - - assert(view->type == DVT_REGISTERS); - debug_view_begin_update(view); - debug_view_end_update(view); - - for (subview = view->machine->debugvw_data->registers_subviews; subview != NULL; subview = subview->next) + if (size.x != m_visible.x || size.y != m_visible.y) { - if (regdata->subview == subview) - return index; - index++; + begin_update(); + m_visible = size; + m_update_pending = true; + view_notify(VIEW_NOTIFY_VISIBLE_CHANGED); + end_update(); } - return 0; } -/*------------------------------------------------- - registers_view_set_subview - select a new - subview by index --------------------------------------------------*/ +//------------------------------------------------- +// set_visible_position - set the top left +// position of the visible area in rows and +// columns +//------------------------------------------------- -void registers_view_set_subview(debug_view *view, int index) +void debug_view::set_visible_position(debug_view_xy pos) { - const registers_subview_item *subview = registers_view_get_subview_by_index(view->machine->debugvw_data->registers_subviews, index); - debug_view_registers *regdata = (debug_view_registers *)view->extra_data; - - assert(view->type == DVT_REGISTERS); - assert(subview != NULL); - if (subview == NULL) - return; - - /* handle a change */ - if (regdata->subview != subview) + if (pos.x != m_topleft.x || pos.y != m_topleft.y) { - debug_view_begin_update(view); - regdata->subview = subview; - view->recompute = view->update_pending = TRUE; - debug_view_end_update(view); + begin_update(); + m_topleft = pos; + m_update_pending = true; + view_notify(VIEW_NOTIFY_VISIBLE_CHANGED); + end_update(); } } +//------------------------------------------------- +// set_cursor_position - set the current cursor +// position as a row and column +//------------------------------------------------- -/*************************************************************************** - DISASSEMBLY VIEW -***************************************************************************/ - -/*------------------------------------------------- - disasm_view_enumerate_subviews - enumerate - all possible subviews for a disassembly view --------------------------------------------------*/ - -static const disasm_subview_item *disasm_view_enumerate_subviews(running_machine *machine) +void debug_view::set_cursor_position(debug_view_xy pos) { - disasm_subview_item *head = NULL; - disasm_subview_item **tailptr = &head; - int curindex = 0; - - /* iterate over CPUs with program address spaces */ - device_execute_interface *exec = NULL; - for (bool gotone = machine->devicelist.first(exec); gotone; gotone = exec->next(exec)) + if (pos.x != m_cursor.x || pos.y != m_cursor.y) { - disasm_subview_item *subview = auto_alloc(machine, disasm_subview_item); - - /* populate the subview */ - subview->next = NULL; - subview->index = curindex++; - subview->name.printf("%s '%s'", exec->device().name(), exec->device().tag()); - subview->space = cpu_get_address_space(*exec, ADDRESS_SPACE_PROGRAM); - - /* add to the list */ - *tailptr = subview; - tailptr = &subview->next; - } - - return head; -} - - -/*------------------------------------------------- - disasm_view_alloc - allocate disasm for the - disassembly view --------------------------------------------------*/ - -static int disasm_view_alloc(debug_view *view) -{ - debug_view_disasm *dasmdata; - int total_comments = 0; - device_t *cpu; - - /* fail if no available subviews */ - if (view->machine->debugvw_data->disasm_subviews == NULL) - return FALSE; - - /* allocate disasm */ - dasmdata = auto_alloc_clear(view->machine, debug_view_disasm); - - /* default to the first subview */ - dasmdata->space = view->machine->debugvw_data->disasm_subviews->space; - - /* allocate the expression data */ - debug_view_expression_alloc(&dasmdata->expression); - - /* count the number of comments */ - for (cpu = view->machine->firstcpu; cpu != NULL; cpu = cpu_next(cpu)) - total_comments += debug_comment_get_count(cpu); - - /* initialize */ - dasmdata->right_column = (total_comments > 0) ? DASM_RIGHTCOL_COMMENTS : DASM_RIGHTCOL_RAW; - dasmdata->backwards_steps = 3; - dasmdata->dasm_width = DEFAULT_DASM_WIDTH; - - /* stash the extra data pointer */ - view->total.y = DEFAULT_DASM_LINES; - view->extra_data = dasmdata; - - /* we support cursors */ - view->supports_cursor = TRUE; - return TRUE; -} - - -/*------------------------------------------------- - disasm_view_free - free disasm for the - disassembly view --------------------------------------------------*/ - -static void disasm_view_free(debug_view *view) -{ - debug_view_disasm *dasmdata = (debug_view_disasm *)view->extra_data; - - /* free any disasm we callocated */ - if (dasmdata != NULL) - { - debug_view_expression_free(&dasmdata->expression); - if (dasmdata->byteaddress != NULL) - auto_free(view->machine, dasmdata->byteaddress); - if (dasmdata->dasm != NULL) - auto_free(view->machine, dasmdata->dasm); - auto_free(view->machine, dasmdata); - } - view->extra_data = NULL; -} - - -/*------------------------------------------------- - disasm_view_notify - handle notification of - updates to cursor changes --------------------------------------------------*/ - -static void disasm_view_notify(debug_view *view, view_notification type) -{ - if (type == VIEW_NOTIFY_CURSOR_CHANGED) - adjust_visible_y_for_cursor(view); -} - - -/*------------------------------------------------- - disasm_view_char - handle a character typed - within the current view --------------------------------------------------*/ - -static void disasm_view_char(debug_view *view, int chval) -{ - debug_view_disasm *dasmdata = (debug_view_disasm *)view->extra_data; - debug_view_xy origcursor = view->cursor; - UINT8 end_buffer = 3; - INT32 temp; - - switch (chval) - { - case DCH_UP: - if (view->cursor.y > 0) - view->cursor.y--; - break; - - case DCH_DOWN: - if (view->cursor.y < view->total.y - 1) - view->cursor.y++; - break; - - case DCH_PUP: - temp = view->cursor.y - (view->visible.y - end_buffer); - if (temp < 0) - view->cursor.y = 0; - else - view->cursor.y = temp; - break; - - case DCH_PDOWN: - temp = view->cursor.y + (view->visible.y - end_buffer); - if (temp > view->total.y - 1) - view->cursor.y = view->total.y - 1; - else - view->cursor.y = temp; - break; - - case DCH_HOME: /* set the active column to the PC */ - { - offs_t pc = memory_address_to_byte(dasmdata->space, cpu_get_pc(dasmdata->space->cpu)) & dasmdata->space->logbytemask; - int curline; - - /* figure out which row the pc is on */ - for (curline = 0; curline < dasmdata->allocated.y; curline++) - if (dasmdata->byteaddress[curline] == pc) - view->cursor.y = curline; - break; - } - - case DCH_CTRLHOME: - view->cursor.y = 0; - break; - - case DCH_CTRLEND: - view->cursor.y = view->total.y - 1; - break; - } - - /* send a cursor changed notification */ - if (view->cursor.y != origcursor.y) - { - debug_view_begin_update(view); - disasm_view_notify(view, VIEW_NOTIFY_CURSOR_CHANGED); - view->update_pending = TRUE; - debug_view_end_update(view); - } -} - - -/*------------------------------------------------- - disasm_view_find_pc_backwards - back up the - specified numberof instructions from the given - PC --------------------------------------------------*/ - -static offs_t disasm_view_find_pc_backwards(const address_space *space, offs_t targetpc, int numinstrs) -{ - cpu_device *cpudevice = downcast<cpu_device *>(space->cpu); - assert(cpudevice != NULL); - - int minlen = memory_byte_to_address(space, cpudevice->min_opcode_bytes()); - int maxlen = memory_byte_to_address(space, cpudevice->max_opcode_bytes()); - offs_t targetpcbyte = memory_address_to_byte(space, targetpc) & space->logbytemask; - offs_t lastgoodpc = targetpc; - offs_t fillpcbyte, curpc; - UINT8 opbuf[1024], argbuf[1024]; - char dasmbuffer[100]; - - /* compute the increment */ - if (minlen == 0) minlen = 1; - if (maxlen == 0) maxlen = 1; - - /* start off numinstrs back */ - curpc = targetpc - minlen * numinstrs; - if (curpc > targetpc) - curpc = 0; - - /* loop until we find what we are looking for */ - fillpcbyte = targetpcbyte; - while (1) - { - offs_t curpcbyte = memory_address_to_byte(space, curpc) & space->logbytemask; - offs_t scanpc; - int instcount = 0; - int instlen; - - /* fill the buffer up to the target */ - while (curpcbyte < fillpcbyte) - { - fillpcbyte--; - opbuf[1000 + fillpcbyte - targetpcbyte] = debug_read_opcode(space, fillpcbyte, 1, FALSE); - argbuf[1000 + fillpcbyte - targetpcbyte] = debug_read_opcode(space, fillpcbyte, 1, TRUE); - } - - /* loop until we get past the target instruction */ - for (scanpc = curpc; scanpc < targetpc; scanpc += instlen) - { - offs_t scanpcbyte = memory_address_to_byte(space, scanpc) & space->logbytemask; - offs_t physpcbyte = scanpcbyte; - - /* get the disassembly, but only if mapped */ - instlen = 1; - if (debug_cpu_translate(space, TRANSLATE_FETCH, &physpcbyte)) - instlen = debug_cpu_disassemble(space->cpu, dasmbuffer, scanpc, &opbuf[1000 + scanpcbyte - targetpcbyte], &argbuf[1000 + scanpcbyte - targetpcbyte]) & DASMFLAG_LENGTHMASK; - - /* count this one */ - instcount++; - } - - /* if we ended up right on targetpc, this is a good candidate */ - if (scanpc == targetpc && instcount <= numinstrs) - lastgoodpc = curpc; - - /* we're also done if we go back too far */ - if (targetpc - curpc >= numinstrs * maxlen) - break; - - /* and if we hit 0, we're done */ - if (curpc == 0) - break; - - /* back up one more and try again */ - curpc -= minlen; - if (curpc > targetpc) - curpc = 0; + begin_update(); + m_cursor = pos; + m_update_pending = true; + view_notify(VIEW_NOTIFY_CURSOR_CHANGED); + end_update(); } - - return lastgoodpc; -} - - -/*------------------------------------------------- - disasm_view_generate_bytes - generate the - opcode byte values --------------------------------------------------*/ - -static void disasm_view_generate_bytes(const address_space *space, offs_t pcbyte, int numbytes, int minbytes, char *string, int maxchars, int encrypted) -{ - int byte, offset = 0; - - /* output the first value */ - if (maxchars >= 2 * minbytes) - offset = sprintf(string, "%s", core_i64_hex_format(debug_read_opcode(space, pcbyte, minbytes, FALSE), minbytes * 2)); - - /* output subsequent values */ - for (byte = minbytes; byte < numbytes && offset + 1 + 2 * minbytes < maxchars; byte += minbytes) - offset += sprintf(&string[offset], " %s", core_i64_hex_format(debug_read_opcode(space, pcbyte + byte, minbytes, encrypted), minbytes * 2)); - - /* if we ran out of room, indicate more */ - string[maxchars - 1] = 0; - if (byte < numbytes && maxchars > 3) - string[maxchars - 2] = string[maxchars - 3] = string[maxchars - 4] = '.'; } -/*------------------------------------------------- - disasm_view_recompute - recompute selected info - for the disassembly view --------------------------------------------------*/ +//------------------------------------------------- +// set_cursor_visible - set the visible state of +// the cursor +//------------------------------------------------- -static int disasm_view_recompute(debug_view *view, offs_t pc, int startline, int lines) +void debug_view::set_cursor_visible(bool visible) { - debug_view_disasm *dasmdata = (debug_view_disasm *)view->extra_data; - const address_space *space = dasmdata->space; - int minbytes, maxbytes, maxbytes_clamped; - int changed = FALSE; - int line; - - /* determine how many characters we need for an address and set the divider */ - dasmdata->divider1 = 1 + space->logaddrchars + 1; - - /* assume a fixed number of characters for the disassembly */ - dasmdata->divider2 = dasmdata->divider1 + 1 + dasmdata->dasm_width + 1; - - /* determine how many bytes we might need to display */ - cpu_device *cpudevice = downcast<cpu_device *>(space->cpu); - assert(cpudevice != NULL); - minbytes = cpudevice->min_opcode_bytes(); - maxbytes = cpudevice->max_opcode_bytes(); - - /* ensure that the PC is aligned to the minimum opcode size */ - pc &= ~memory_byte_to_address_end(space, minbytes - 1); - - /* set the width of the third column according to display mode */ - if (dasmdata->right_column == DASM_RIGHTCOL_RAW || dasmdata->right_column == DASM_RIGHTCOL_ENCRYPTED) + if (visible != m_cursor_visible) { - maxbytes_clamped = MIN(maxbytes, DASM_MAX_BYTES); - view->total.x = dasmdata->divider2 + 1 + 2 * maxbytes_clamped + (maxbytes_clamped / minbytes - 1) + 1; + begin_update(); + m_cursor_visible = visible; + m_update_pending = true; + view_notify(VIEW_NOTIFY_CURSOR_CHANGED); + end_update(); } - else if (dasmdata->right_column == DASM_RIGHTCOL_COMMENTS) - view->total.x = dasmdata->divider2 + 1 + 50; /* DEBUG_COMMENT_MAX_LINE_LENGTH */ - else - view->total.x = dasmdata->divider2 + 1; - - /* reallocate memory if we don't have enough */ - if (dasmdata->allocated.x < view->total.x || dasmdata->allocated.y < view->total.y) - { - /* update our values */ - dasmdata->allocated.x = view->total.x; - dasmdata->allocated.y = view->total.y; - - /* allocate address array */ - auto_free(view->machine, dasmdata->byteaddress); - dasmdata->byteaddress = auto_alloc_array(view->machine, offs_t, dasmdata->allocated.y); - - /* allocate disassembly buffer */ - auto_free(view->machine, dasmdata->dasm); - dasmdata->dasm = auto_alloc_array(view->machine, char, dasmdata->allocated.x * dasmdata->allocated.y); - } - - /* iterate over lines */ - for (line = 0; line < lines; line++) - { - int instr = startline + line; - char *destbuf = &dasmdata->dasm[instr * dasmdata->allocated.x]; - char buffer[100], oldbuf[100]; - offs_t pcbyte, physpcbyte; - int numbytes = 0; - - /* convert PC to a byte offset */ - pcbyte = memory_address_to_byte(space, pc) & space->logbytemask; - - /* save a copy of the previous line as a backup if we're only doing one line */ - if (lines == 1) - strncpy(oldbuf, destbuf, MIN(sizeof(oldbuf), dasmdata->allocated.x)); - - /* convert back and set the address of this instruction */ - dasmdata->byteaddress[instr] = pcbyte; - sprintf(&destbuf[0], " %s ", core_i64_hex_format(memory_byte_to_address(space, pcbyte), space->logaddrchars)); - - /* make sure we can translate the address, and then disassemble the result */ - physpcbyte = pcbyte; - if (debug_cpu_translate(space, TRANSLATE_FETCH_DEBUG, &physpcbyte)) - { - UINT8 opbuf[64], argbuf[64]; - - /* fetch the bytes up to the maximum */ - for (numbytes = 0; numbytes < maxbytes; numbytes++) - { - opbuf[numbytes] = debug_read_opcode(space, pcbyte + numbytes, 1, FALSE); - argbuf[numbytes] = debug_read_opcode(space, pcbyte + numbytes, 1, TRUE); - } - - /* disassemble the result */ - pc += numbytes = debug_cpu_disassemble(space->cpu, buffer, pc & space->logaddrmask, opbuf, argbuf) & DASMFLAG_LENGTHMASK; - } - else - strcpy(buffer, "<unmapped>"); - - /* append the disassembly to the buffer */ - sprintf(&destbuf[dasmdata->divider1 + 1], "%-*s ", dasmdata->dasm_width, buffer); - - /* output the right column */ - if (dasmdata->right_column == DASM_RIGHTCOL_RAW || dasmdata->right_column == DASM_RIGHTCOL_ENCRYPTED) - { - /* get the bytes */ - numbytes = memory_address_to_byte(space, numbytes) & space->logbytemask; - disasm_view_generate_bytes(space, pcbyte, numbytes, minbytes, &destbuf[dasmdata->divider2], dasmdata->allocated.x - dasmdata->divider2, dasmdata->right_column == DASM_RIGHTCOL_ENCRYPTED); - } - else if (dasmdata->right_column == DASM_RIGHTCOL_COMMENTS) - { - offs_t comment_address = memory_byte_to_address(space, dasmdata->byteaddress[instr]); - const char *text; - - /* get and add the comment, if present */ - text = debug_comment_get_text(space->cpu, comment_address, debug_comment_get_opcode_crc32(space->cpu, comment_address)); - if (text != NULL) - sprintf(&destbuf[dasmdata->divider2], "// %.*s", dasmdata->allocated.x - dasmdata->divider2 - 1, text); - } - - /* see if the line changed at all */ - if (lines == 1 && strncmp(oldbuf, destbuf, MIN(sizeof(oldbuf), dasmdata->allocated.x)) != 0) - changed = TRUE; - } - - /* update opcode base information */ - dasmdata->last_direct_decrypted = space->direct.decrypted; - dasmdata->last_direct_raw = space->direct.raw; - dasmdata->last_change_count = debug_comment_all_change_count(space->machine); - - /* now longer need to recompute */ - view->recompute = FALSE; - return changed; } -/*------------------------------------------------- - disasm_view_update - update the contents of - the disassembly view --------------------------------------------------*/ +//------------------------------------------------- +// set_subview - set the current subview +//------------------------------------------------- -static void disasm_view_update(debug_view *view) +void debug_view::set_source(const debug_view_source &source) { - debug_view_disasm *dasmdata = (debug_view_disasm *)view->extra_data; - const address_space *space = dasmdata->space; - debug_view_char *dest = view->viewdata; - int recomputed_this_time = FALSE; - offs_t pc, pcbyte; - EXPRERR exprerr; - UINT32 row; - - /* no space, do nothing */ - if (space == NULL) - return; - pc = cpu_get_pc(space->cpu); - pcbyte = memory_address_to_byte(space, pc) & space->logbytemask; - - /* if our expression is dirty, fix it */ - if (dasmdata->expression.dirty) + if (&source != m_source) { - parsed_expression *expr; - - /* parse the new expression */ - exprerr = expression_parse(dasmdata->expression.string, debug_cpu_get_symtable(space->cpu), &debug_expression_callbacks, space->machine, &expr); - - /* if it worked, update the expression */ - if (exprerr == EXPRERR_NONE) - { - if (dasmdata->expression.parsed != NULL) - expression_free(dasmdata->expression.parsed); - dasmdata->expression.parsed = expr; - } - - /* always recompute if the expression is dirty */ - view->recompute = TRUE; - } - - /* if we're tracking a value, make sure it is visible */ - if (dasmdata->expression.parsed != NULL) - { - UINT64 result; - - /* recompute the value of the expression */ - exprerr = expression_execute(dasmdata->expression.parsed, &result); - if (exprerr == EXPRERR_NONE && result != dasmdata->expression.result) - { - offs_t resultbyte = memory_address_to_byte(space, result) & space->logbytemask; - - /* update the result */ - dasmdata->expression.result = result; - - /* see if the new result is an address we already have */ - for (row = 0; row < dasmdata->allocated.y; row++) - if (dasmdata->byteaddress[row] == resultbyte) - break; - - /* if we didn't find it, or if it's really close to the bottom, recompute */ - if (row == dasmdata->allocated.y || row >= view->total.y - view->visible.y) - view->recompute = TRUE; - - /* otherwise, if it's not visible, adjust the view so it is */ - else if (row < view->topleft.y || row >= view->topleft.y + view->visible.y - 2) - view->topleft.y = (row > 3) ? row - 3 : 0; - } - - /* no longer dirty */ - dasmdata->expression.dirty = FALSE; - } - - /* if the opcode base has changed, rework things */ - if (space->direct.decrypted != dasmdata->last_direct_decrypted || space->direct.raw != dasmdata->last_direct_raw) - view->recompute = TRUE; - - /* if the comments have changed, redo it */ - if (dasmdata->last_change_count != debug_comment_all_change_count(space->machine)) - view->recompute = TRUE; - - /* if we need to recompute, do it */ -recompute: - if (view->recompute) - { - /* recompute the view */ - if (dasmdata->byteaddress != NULL && dasmdata->last_change_count != debug_comment_all_change_count(space->machine)) - { - /* smoosh us against the left column, but not the top row */ - view->topleft.x = 0; - - /* recompute from where we last recomputed! */ - disasm_view_recompute(view, memory_byte_to_address(space, dasmdata->byteaddress[0]), 0, view->total.y); - } - else - { - /* determine the addresses of what we will display */ - offs_t backpc = disasm_view_find_pc_backwards(space, (UINT32)dasmdata->expression.result, dasmdata->backwards_steps); - - /* put ourselves back in the top left */ - view->topleft.y = 0; - view->topleft.x = 0; - - disasm_view_recompute(view, backpc, 0, view->total.y); - } - recomputed_this_time = TRUE; - } - - /* figure out the row where the PC is and recompute the disassembly */ - if (pcbyte != dasmdata->last_pcbyte) - { - /* find the row with the PC on it */ - for (row = 0; row < view->visible.y; row++) - { - UINT32 effrow = view->topleft.y + row; - if (effrow >= dasmdata->allocated.y) - break; - if (pcbyte == dasmdata->byteaddress[effrow]) - { - /* see if we changed */ - int changed = disasm_view_recompute(view, pc, effrow, 1); - if (changed && !recomputed_this_time) - { - view->recompute = TRUE; - goto recompute; - } - - /* set the effective row and PC */ - view->cursor.y = effrow; - } - } - dasmdata->last_pcbyte = pcbyte; - } - - /* loop over visible rows */ - for (row = 0; row < view->visible.y; row++) - { - UINT32 effrow = view->topleft.y + row; - UINT8 attrib = DCA_NORMAL; - debug_cpu_breakpoint *bp; - UINT32 col = 0; - - /* if this visible row is valid, add it to the buffer */ - if (effrow < dasmdata->allocated.y) - { - const char *data = &dasmdata->dasm[effrow * dasmdata->allocated.x]; - UINT32 effcol = view->topleft.x; - UINT32 len = 0; - - /* if we're on the line with the PC, recompute and hilight it */ - if (pcbyte == dasmdata->byteaddress[effrow]) - attrib = DCA_CURRENT; - - /* if we're on a line with a breakpoint, tag it changed */ - else - { - const cpu_debug_data *cpuinfo = cpu_get_debug_data(space->cpu); - for (bp = cpuinfo->bplist; bp != NULL; bp = bp->next) - if (dasmdata->byteaddress[effrow] == (memory_address_to_byte(space, bp->address) & space->logbytemask)) - attrib = DCA_CHANGED; - } - - /* if we're on the active column and everything is couth, highlight it */ - if (view->cursor_visible && effrow == view->cursor.y) - attrib |= DCA_SELECTED; - - /* get the effective string */ - len = (UINT32)strlen(data); - - /* copy data */ - while (col < view->visible.x && effcol < len) - { - dest->byte = data[effcol++]; - dest->attrib = (effcol <= dasmdata->divider1 || effcol >= dasmdata->divider2) ? (attrib | DCA_ANCILLARY) : attrib; - - /* comments are just green for now - maybe they shouldn't even be this? */ - if (effcol >= dasmdata->divider2 && dasmdata->right_column == DASM_RIGHTCOL_COMMENTS) - attrib |= DCA_COMMENT; - - dest++; - col++; - } - } - - /* fill the rest with blanks */ - while (col < view->visible.x) - { - dest->byte = ' '; - dest->attrib = (effrow < view->total.y) ? (attrib | DCA_ANCILLARY) : attrib; - dest++; - col++; - } + begin_update(); + m_source = &source; + m_update_pending = true; + view_notify(VIEW_NOTIFY_SOURCE_CHANGED); + end_update(); } } -/*------------------------------------------------- - disasm_view_get_subview_list - return a linked - list of subviews --------------------------------------------------*/ +//------------------------------------------------- +// adjust_visible_x_for_cursor - adjust a view's +// visible X position to ensure the cursor is +// visible +//------------------------------------------------- -const disasm_subview_item *disasm_view_get_subview_list(debug_view *view) +void debug_view::adjust_visible_x_for_cursor() { - assert(view->type == DVT_DISASSEMBLY); - return view->machine->debugvw_data->disasm_subviews; -} - - -/*------------------------------------------------- - disasm_view_get_subview - return the current - subview index --------------------------------------------------*/ - -int disasm_view_get_subview(debug_view *view) -{ - debug_view_disasm *dasmdata = (debug_view_disasm *)view->extra_data; - const disasm_subview_item *subview; - int index = 0; - - assert(view->type == DVT_DISASSEMBLY); - debug_view_begin_update(view); - debug_view_end_update(view); - - for (subview = view->machine->debugvw_data->disasm_subviews; subview != NULL; subview = subview->next) - { - if (subview->space == dasmdata->space) - return index; - index++; - } - return 0; + if (m_cursor.x < m_topleft.x) + m_topleft.x = m_cursor.x; + else if (m_cursor.x >= m_topleft.x + m_visible.x - 1) + m_topleft.x = m_cursor.x - m_visible.x + 2; } -/*------------------------------------------------- - disasm_view_get_expression - return the - expression string describing the home address --------------------------------------------------*/ +//------------------------------------------------- +// adjust_visible_y_for_cursor - adjust a view's +// visible Y position to ensure the cursor is +// visible +//------------------------------------------------- -const char *disasm_view_get_expression(debug_view *view) +void debug_view::adjust_visible_y_for_cursor() { - debug_view_disasm *dasmdata = (debug_view_disasm *)view->extra_data; - assert(view->type == DVT_DISASSEMBLY); - debug_view_begin_update(view); - debug_view_end_update(view); - return dasmdata->expression.string; + if (m_cursor.y < m_topleft.y) + m_topleft.y = m_cursor.y; + else if (m_cursor.y >= m_topleft.y + m_visible.y - 1) + m_topleft.y = m_cursor.y - m_visible.y + 2; } -/*------------------------------------------------- - disasm_view_get_right_column - return the - contents of the right column --------------------------------------------------*/ +//------------------------------------------------- +// view_notify - handle notification of updates +//------------------------------------------------- -disasm_right_column disasm_view_get_right_column(debug_view *view) +void debug_view::view_notify(debug_view_notification type) { - debug_view_disasm *dasmdata = (debug_view_disasm *)view->extra_data; - assert(view->type == DVT_DISASSEMBLY); - debug_view_begin_update(view); - debug_view_end_update(view); - return dasmdata->right_column; + // default does nothing } -/*------------------------------------------------- - disasm_view_get_backward_steps - return the - number of instructions displayed before the - home address --------------------------------------------------*/ +//------------------------------------------------- +// view_char - handle a character typed within +// the current view +//------------------------------------------------- -UINT32 disasm_view_get_backward_steps(debug_view *view) +void debug_view::view_char(int chval) { - debug_view_disasm *dasmdata = (debug_view_disasm *)view->extra_data; - assert(view->type == DVT_DISASSEMBLY); - debug_view_begin_update(view); - debug_view_end_update(view); - return dasmdata->backwards_steps; + // default does nothing } -/*------------------------------------------------- - disasm_view_get_disasm_width - return the - width in characters of the main disassembly - section --------------------------------------------------*/ -UINT32 disasm_view_get_disasm_width(debug_view *view) -{ - debug_view_disasm *dasmdata = (debug_view_disasm *)view->extra_data; - assert(view->type == DVT_DISASSEMBLY); - debug_view_begin_update(view); - debug_view_end_update(view); - return dasmdata->dasm_width; -} +//************************************************************************** +// DEBUG VIEW MANAGER +//************************************************************************** +//------------------------------------------------- +// debug_view_manager - constructor +//------------------------------------------------- -/*------------------------------------------------- - disasm_view_get_selected_address - return the - PC of the currently selected address in the - view --------------------------------------------------*/ - -offs_t disasm_view_get_selected_address(debug_view *view) +debug_view_manager::debug_view_manager(running_machine &machine) + : m_machine(machine), + m_viewlist(NULL) { - debug_view_disasm *dasmdata = (debug_view_disasm *)view->extra_data; - assert(view->type == DVT_DISASSEMBLY); - debug_view_begin_update(view); - debug_view_end_update(view); - return memory_byte_to_address(dasmdata->space, dasmdata->byteaddress[view->cursor.y]); } -/*------------------------------------------------- - disasm_view_set_subview - select a new subview - by index --------------------------------------------------*/ +//------------------------------------------------- +// ~debug_view_manager - destructor +//------------------------------------------------- -void disasm_view_set_subview(debug_view *view, int index) +debug_view_manager::~debug_view_manager() { - const disasm_subview_item *subview = disasm_view_get_subview_by_index(view->machine->debugvw_data->disasm_subviews, index); - debug_view_disasm *dasmdata = (debug_view_disasm *)view->extra_data; - - assert(view->type == DVT_DISASSEMBLY); - assert(subview != NULL); - if (subview == NULL) - return; - - /* handle a change */ - if (subview->space != dasmdata->space) + while (m_viewlist != NULL) { - debug_view_begin_update(view); - dasmdata->space = subview->space; - - /* we need to recompute the expression in the context of the new space's CPU */ - dasmdata->expression.dirty = TRUE; - view->recompute = view->update_pending = TRUE; - debug_view_end_update(view); + debug_view *oldhead = m_viewlist; + m_viewlist = oldhead->m_next; + auto_free(&m_machine, oldhead); } } -/*------------------------------------------------- - disasm_view_set_expression - set the - expression string describing the home address --------------------------------------------------*/ +//------------------------------------------------- +// alloc_view - create a new view +//------------------------------------------------- -void disasm_view_set_expression(debug_view *view, const char *expression) +debug_view *debug_view_manager::alloc_view(debug_view_type type, debug_view_osd_update_func osdupdate, void *osdprivate) { - debug_view_disasm *dasmdata = (debug_view_disasm *)view->extra_data; - - assert(view->type == DVT_DISASSEMBLY); - assert(expression != NULL); - - debug_view_begin_update(view); - debug_view_expression_set(&dasmdata->expression, expression); - view->recompute = view->update_pending = TRUE; - debug_view_end_update(view); -} - - -/*------------------------------------------------- - disasm_view_set_right_column - set the - contents of the right column --------------------------------------------------*/ - -void disasm_view_set_right_column(debug_view *view, disasm_right_column contents) -{ - debug_view_disasm *dasmdata = (debug_view_disasm *)view->extra_data; - - assert(view->type == DVT_DISASSEMBLY); - assert(contents == DASM_RIGHTCOL_RAW || contents == DASM_RIGHTCOL_ENCRYPTED || contents == DASM_RIGHTCOL_COMMENTS); - - if (contents != dasmdata->right_column) + switch (type) { - debug_view_begin_update(view); - dasmdata->right_column = contents; - view->recompute = view->update_pending = TRUE; - debug_view_end_update(view); - } -} + case DVT_CONSOLE: + return append(auto_alloc(&m_machine, debug_view_console(m_machine, osdupdate, osdprivate))); + case DVT_STATE: + return append(auto_alloc(&m_machine, debug_view_state(m_machine, osdupdate, osdprivate))); -/*------------------------------------------------- - disasm_view_set_backward_steps - set the - number of instructions displayed before the - home address --------------------------------------------------*/ + case DVT_DISASSEMBLY: + return append(auto_alloc(&m_machine, debug_view_disasm(m_machine, osdupdate, osdprivate))); -void disasm_view_set_backward_steps(debug_view *view, UINT32 steps) -{ - debug_view_disasm *dasmdata = (debug_view_disasm *)view->extra_data; + case DVT_MEMORY: + return append(auto_alloc(&m_machine, debug_view_memory(m_machine, osdupdate, osdprivate))); - assert(view->type == DVT_DISASSEMBLY); + case DVT_LOG: + return append(auto_alloc(&m_machine, debug_view_log(m_machine, osdupdate, osdprivate))); - if (steps != dasmdata->backwards_steps) - { - debug_view_begin_update(view); - dasmdata->backwards_steps = steps; - view->recompute = view->update_pending = TRUE; - debug_view_end_update(view); - } -} + case DVT_TIMERS: +// return append(auto_alloc(&m_machine, debug_view_timers(m_machine, osdupdate, osdprivate))); - -/*------------------------------------------------- - disasm_view_set_disasm_width - set the - width in characters of the main disassembly - section --------------------------------------------------*/ - -void disasm_view_set_disasm_width(debug_view *view, UINT32 width) -{ - debug_view_disasm *dasmdata = (debug_view_disasm *)view->extra_data; - - assert(view->type == DVT_DISASSEMBLY); - - if (width != dasmdata->dasm_width) - { - debug_view_begin_update(view); - dasmdata->dasm_width = width; - view->recompute = view->update_pending = TRUE; - debug_view_end_update(view); + case DVT_ALLOCS: +// return append(auto_alloc(&m_machine, debug_view_allocs(m_machine, osdupdate, osdprivate))); + + default: + fatalerror("Attempt to create invalid debug view type %d\n", type); } + return NULL; } -/*------------------------------------------------- - disasm_view_set_selected_address - set the - PC of the currently selected address in the - view --------------------------------------------------*/ +//------------------------------------------------- +// free_view - free a view +//------------------------------------------------- -void disasm_view_set_selected_address(debug_view *view, offs_t address) +void debug_view_manager::free_view(debug_view &view) { - debug_view_disasm *dasmdata = (debug_view_disasm *)view->extra_data; - offs_t byteaddress = memory_address_to_byte(dasmdata->space, address) & dasmdata->space->logbytemask; - int line; - - assert(view->type == DVT_DISASSEMBLY); - - for (line = 0; line < view->total.y; line++) - if (dasmdata->byteaddress[line] == byteaddress) + // free us but only if we're in the list + for (debug_view **viewptr = &m_viewlist; *viewptr != NULL; viewptr = &(*viewptr)->m_next) + if (*viewptr == &view) { - view->cursor.y = line; - debug_view_set_cursor_position(view, view->cursor); + *viewptr = view.m_next; + auto_free(&m_machine, &view); break; } } +//------------------------------------------------- +// update_all - force all views to refresh +//------------------------------------------------- -/*************************************************************************** - MEMORY VIEW -***************************************************************************/ - -/*------------------------------------------------- - memory_view_enumerate_subviews - enumerate - all possible subviews for a memory view --------------------------------------------------*/ - -static const memory_subview_item *memory_view_enumerate_subviews(running_machine *machine) +void debug_view_manager::update_all(debug_view_type type) { - memory_subview_item *head = NULL; - memory_subview_item **tailptr = &head; - int curindex = 0; - - /* first add all the device's address spaces */ - device_memory_interface *memintf = NULL; - for (bool gotone = machine->devicelist.first(memintf); gotone; gotone = memintf->next(memintf)) - for (int spacenum = 0; spacenum < ADDRESS_SPACES; spacenum++) - { - const address_space *space = memintf->space(spacenum); - if (space != NULL) - { - memory_subview_item *subview = auto_alloc(machine, memory_subview_item); - - /* populate the subview */ - subview->next = NULL; - subview->index = curindex++; - subview->name.printf("%s '%s' %s space memory", memintf->device().name(), memintf->device().tag(), space->name); - subview->space = space; - subview->endianness = space->endianness; - subview->prefsize = space->dbits / 8; - - /* add to the list */ - *tailptr = subview; - tailptr = &subview->next; - } - } - - /* then add all the memory regions */ - for (const region_info *region = machine->regionlist.first(); region != NULL; region = region->next()) - { - memory_subview_item *subview = auto_alloc(machine, memory_subview_item); - - /* populate the subview */ - subview->next = NULL; - subview->index = curindex++; - subview->name.printf("Region '%s'", region->name.cstr()); - subview->base = *region; - subview->length = region->bytes(); - subview->offsetxor = NATIVE_ENDIAN_VALUE_LE_BE(region->width() - 1, 0); - subview->endianness = region->endianness(); - subview->prefsize = MIN(region->width(), 8); - - /* add to the list */ - *tailptr = subview; - tailptr = &subview->next; - } - - /* finally add all global array symbols */ - for (int itemnum = 0; itemnum < 10000; itemnum++) - { - /* stop when we run out of items */ - UINT32 valsize, valcount; - void *base; - const char *name = state_save_get_indexed_item(machine, itemnum, &base, &valsize, &valcount); - if (name == NULL) - break; - - /* if this is a single-entry global, add it */ - if (valcount > 1 && strstr(name, "globals/")) - { - memory_subview_item *subview = auto_alloc(machine, memory_subview_item); - - /* populate the subview */ - subview->next = NULL; - subview->index = curindex++; - subview->name.cpy(strrchr(name, '/') + 1); - subview->base = base; - subview->length = valcount * valsize; - subview->prefsize = MIN(valsize, 8); - - /* add to the list */ - *tailptr = subview; - tailptr = &subview->next; - } - } - - return head; -} - - -/*------------------------------------------------- - memory_view_alloc - allocate memory for the - memory view --------------------------------------------------*/ - -static int memory_view_alloc(debug_view *view) -{ - const memory_subview_item *subviews = memory_view_enumerate_subviews(view->machine); - debug_view_memory *memdata; - - /* if no subviews, fail */ - if (subviews == NULL) - return FALSE; - - /* allocate memory */ - memdata = auto_alloc_clear(view->machine, debug_view_memory); - memdata->subviewlist = subviews; - - /* allocate the expression data */ - debug_view_expression_alloc(&memdata->expression); - - /* stash the extra data pointer */ - view->extra_data = memdata; - - /* we support cursors */ - view->supports_cursor = TRUE; - - /* default to the first subview */ - memdata->desc = memdata->subviewlist; - - /* start out with 16 bytes in a single column and ASCII displayed */ - memdata->bytes_per_chunk = memdata->desc->prefsize; - memdata->chunks_per_row = 16 / memdata->desc->prefsize; - memdata->bytes_per_row = memdata->bytes_per_chunk * memdata->chunks_per_row; - memdata->ascii_view = TRUE; - - return TRUE; + // loop over each view and force an update + for (debug_view *view = m_viewlist; view != NULL; view = view->next()) + if (type == DVT_NONE || type == view->type()) + view->force_update(); } -/*------------------------------------------------- - memory_view_free - free memory for the - memory view --------------------------------------------------*/ +//------------------------------------------------- +// flush_osd_updates - flush all pending OSD +// updates +//------------------------------------------------- -static void memory_view_free(debug_view *view) +void debug_view_manager::flush_osd_updates() { - debug_view_memory *memdata = (debug_view_memory *)view->extra_data; - - /* free any memory we allocated */ - if (memdata != NULL) - { - while (memdata->subviewlist != NULL) - { - memory_subview_item *item = (memory_subview_item *)memdata->subviewlist; - memdata->subviewlist = item->next; - auto_free(view->machine, item); - } - debug_view_expression_free(&memdata->expression); - auto_free(view->machine, memdata); - } - view->extra_data = NULL; -} - - -/*------------------------------------------------- - memory_view_notify - handle notification of - updates to cursor changes --------------------------------------------------*/ - -static void memory_view_notify(debug_view *view, view_notification type) -{ - if (type == VIEW_NOTIFY_CURSOR_CHANGED) - { - offs_t address; - UINT8 shift; - - /* normalize the cursor */ - memory_view_get_cursor_pos(view, &address, &shift); - memory_view_set_cursor_pos(view, address, shift); - } + for (debug_view *view = m_viewlist; view != NULL; view = view->m_next) + view->flush_osd_updates(); } -/*------------------------------------------------- - memory_view_update - update the contents of - the memory view --------------------------------------------------*/ +//------------------------------------------------- +// append - append a view to the end of our list +//------------------------------------------------- -static void memory_view_update(debug_view *view) +debug_view *debug_view_manager::append(debug_view *view) { - debug_view_memory *memdata = (debug_view_memory *)view->extra_data; - const address_space *space = memdata->desc->space; - const memory_view_pos *posdata; - UINT32 row; - - /* if we need to recompute, do it now */ - if (memory_view_needs_recompute(view)) - memory_view_recompute(view); - - /* get positional data */ - posdata = &memory_pos_table[memdata->bytes_per_chunk]; - - /* loop over visible rows */ - for (row = 0; row < view->visible.y; row++) - { - debug_view_char *destmin = view->viewdata + row * view->visible.x; - debug_view_char *destmax = destmin + view->visible.x; - debug_view_char *destrow = destmin - view->topleft.x; - UINT32 effrow = view->topleft.y + row; - debug_view_char *dest; - int ch, chunknum; - - /* reset the line of data; section 1 is normal, others are ancillary, cursor is selected */ - dest = destmin; - for (ch = 0; ch < view->visible.x; ch++, dest++) - { - UINT32 effcol = view->topleft.x + ch; - dest->byte = ' '; - dest->attrib = DCA_ANCILLARY; - if (in_section(effcol, &memdata->section[1])) - { - dest->attrib = DCA_NORMAL; - if (view->cursor_visible && effrow == view->cursor.y && effcol == view->cursor.x) - dest->attrib |= DCA_SELECTED; - } - } - - /* if this visible row is valid, add it to the buffer */ - if (effrow < view->total.y) - { - offs_t addrbyte = memdata->byte_offset + effrow * memdata->bytes_per_row; - offs_t address = (space != NULL) ? memory_byte_to_address(space, addrbyte) : addrbyte; - char addrtext[20]; - - /* generate the address */ - sprintf(addrtext, memdata->addrformat, address); - dest = destrow + memdata->section[0].pos + 1; - for (ch = 0; addrtext[ch] != 0 && ch < memdata->section[0].width - 1; ch++, dest++) - if (dest >= destmin && dest < destmax) - dest->byte = addrtext[ch]; - - /* generate the data */ - for (chunknum = 0; chunknum < memdata->chunks_per_row; chunknum++) - { - int chunkindex = memdata->reverse_view ? (memdata->chunks_per_row - 1 - chunknum) : chunknum; - UINT64 chunkdata; - int ismapped; - - ismapped = memory_view_read(memdata, memdata->bytes_per_chunk, addrbyte + chunknum * memdata->bytes_per_chunk, &chunkdata); - dest = destrow + memdata->section[1].pos + 1 + chunkindex * posdata->spacing; - for (ch = 0; ch < posdata->spacing; ch++, dest++) - if (dest >= destmin && dest < destmax) - { - UINT8 shift = posdata->shift[ch]; - if (shift < 64) - dest->byte = ismapped ? "0123456789ABCDEF"[(chunkdata >> shift) & 0x0f] : '*'; - } - } - - /* generate the ASCII data */ - if (memdata->section[2].width > 0) - { - dest = destrow + memdata->section[2].pos + 1; - for (ch = 0; ch < memdata->bytes_per_row; ch++, dest++) - if (dest >= destmin && dest < destmax) - { - int ismapped; - UINT64 chval; - - ismapped = memory_view_read(memdata, 1, addrbyte + ch, &chval); - dest->byte = (ismapped && isprint(chval)) ? chval : '.'; - } - } - } - } + debug_view **viewptr; + for (viewptr = &m_viewlist; *viewptr != NULL; viewptr = &(*viewptr)->m_next) ; + *viewptr = view; + return view; } -/*------------------------------------------------- - memory_view_char - handle a character typed - within the current view --------------------------------------------------*/ - -static void memory_view_char(debug_view *view, int chval) -{ - static const char hexvals[] = "0123456789abcdef"; - debug_view_memory *memdata = (debug_view_memory *)view->extra_data; - offs_t address; - char *hexchar; - int ismapped; - UINT64 data; - UINT32 delta; - UINT8 shift; - - /* get the position */ - memory_view_get_cursor_pos(view, &address, &shift); - - /* handle the incoming key */ - switch (chval) - { - case DCH_UP: - if (address >= memdata->byte_offset + memdata->bytes_per_row) - address -= memdata->bytes_per_row; - break; - - case DCH_DOWN: - if (address <= memdata->maxaddr - memdata->bytes_per_row) - address += memdata->bytes_per_row; - break; - - case DCH_PUP: - for (delta = (view->visible.y - 2) * memdata->bytes_per_row; delta > 0; delta -= memdata->bytes_per_row) - if (address >= memdata->byte_offset + delta) - { - address -= delta; - break; - } - break; - case DCH_PDOWN: - for (delta = (view->visible.y - 2) * memdata->bytes_per_row; delta > 0; delta -= memdata->bytes_per_row) - if (address <= memdata->maxaddr - delta) - { - address += delta; - break; - } - break; +//************************************************************************** +// DEBUG VIEW EXPRESSION +//************************************************************************** - case DCH_HOME: - address -= address % memdata->bytes_per_row; - shift = (memdata->bytes_per_chunk * 8) - 4; - break; +//------------------------------------------------- +// debug_view_expression - constructor +//------------------------------------------------- - case DCH_CTRLHOME: - address = memdata->byte_offset; - shift = (memdata->bytes_per_chunk * 8) - 4; - break; - - case DCH_END: - address += (memdata->bytes_per_row - (address % memdata->bytes_per_row) - 1); - shift = 0; - break; - - case DCH_CTRLEND: - address = memdata->maxaddr; - shift = 0; - break; - - case DCH_CTRLLEFT: - if (address >= memdata->byte_offset + memdata->bytes_per_chunk) - address -= memdata->bytes_per_chunk; - break; - - case DCH_CTRLRIGHT: - if (address <= memdata->maxaddr - memdata->bytes_per_chunk) - address += memdata->bytes_per_chunk; - break; - - default: - hexchar = (char *)strchr(hexvals, tolower(chval)); - if (hexchar == NULL) - break; - ismapped = memory_view_read(memdata, memdata->bytes_per_chunk, address, &data); - if (!ismapped) - break; - data &= ~((UINT64)0x0f << shift); - data |= (UINT64)(hexchar - hexvals) << shift; - memory_view_write(memdata, memdata->bytes_per_chunk, address, data); - /* fall through... */ - - case DCH_RIGHT: - if (shift == 0 && address != memdata->maxaddr) - { - shift = memdata->bytes_per_chunk * 8 - 4; - address += memdata->bytes_per_chunk; - } - else - shift -= 4; - break; - - case DCH_LEFT: - if (shift == memdata->bytes_per_chunk * 8 - 4 && address != memdata->byte_offset) - { - shift = 0; - address -= memdata->bytes_per_chunk; - } - else - shift += 4; - break; - } - - /* set a new position */ - debug_view_begin_update(view); - memory_view_set_cursor_pos(view, address, shift); - view->update_pending = TRUE; - debug_view_end_update(view); -} - - -/*------------------------------------------------- - memory_view_recompute - recompute the internal - data and structure of the memory view --------------------------------------------------*/ - -static void memory_view_recompute(debug_view *view) +debug_view_expression::debug_view_expression(running_machine &machine) + : m_machine(machine), + m_dirty(true), + m_result(0), + m_parsed(NULL), + m_string("0"), + m_context(debug_cpu_get_global_symtable(&machine)) { - debug_view_memory *memdata = (debug_view_memory *)view->extra_data; - const address_space *space = memdata->desc->space; - offs_t cursoraddr; - UINT8 cursorshift; - int addrchars; - - /* get the current cursor position */ - memory_view_get_cursor_pos(view, &cursoraddr, &cursorshift); - - /* determine the maximum address and address format string from the raw information */ - if (space != NULL) - { - memdata->maxaddr = memdata->no_translation ? space->bytemask : space->logbytemask; - addrchars = memdata->no_translation ? space->addrchars : space->logaddrchars; - } - else - { - memdata->maxaddr = memdata->desc->length - 1; - addrchars = sprintf(memdata->addrformat, "%X", memdata->maxaddr); - } - - /* generate an 8-byte aligned format for the address */ - if (!memdata->reverse_view) - sprintf(memdata->addrformat, "%*s%%0%dX", 8 - addrchars, "", addrchars); - else - sprintf(memdata->addrformat, "%%0%dX%*s", addrchars, 8 - addrchars, ""); - - /* if we are viewing a space with a minimum chunk size, clamp the bytes per chunk */ - if (space != NULL && space->ashift < 0) - { - UINT32 min_bytes_per_chunk = 1 << -space->ashift; - while (memdata->bytes_per_chunk < min_bytes_per_chunk) - { - memdata->bytes_per_chunk *= 2; - memdata->chunks_per_row /= 2; - } - memdata->chunks_per_row = MAX(1, memdata->chunks_per_row); - } - - /* recompute the byte offset based on the most recent expression result */ - memdata->bytes_per_row = memdata->bytes_per_chunk * memdata->chunks_per_row; - memdata->byte_offset = memdata->expression.result % memdata->bytes_per_row; - - /* compute the section widths */ - memdata->section[0].width = 1 + 8 + 1; - memdata->section[1].width = 1 + 3 * memdata->bytes_per_row + 1; - memdata->section[2].width = memdata->ascii_view ? (1 + memdata->bytes_per_row + 1) : 0; - - /* compute the section positions */ - if (!memdata->reverse_view) - { - memdata->section[0].pos = 0; - memdata->section[1].pos = memdata->section[0].pos + memdata->section[0].width; - memdata->section[2].pos = memdata->section[1].pos + memdata->section[1].width; - view->total.x = memdata->section[2].pos + memdata->section[2].width; - } - else - { - memdata->section[2].pos = 0; - memdata->section[1].pos = memdata->section[2].pos + memdata->section[2].width; - memdata->section[0].pos = memdata->section[1].pos + memdata->section[1].width; - view->total.x = memdata->section[0].pos + memdata->section[0].width; - } - - /* derive total sizes from that */ - view->total.y = ((UINT64)memdata->maxaddr - (UINT64)memdata->byte_offset + (UINT64)memdata->bytes_per_row - 1) / memdata->bytes_per_row; - - /* reset the current cursor position */ - memory_view_set_cursor_pos(view, cursoraddr, cursorshift); } -/*------------------------------------------------- - memory_view_needs_recompute - determine if - anything has changed that requires a - recomputation --------------------------------------------------*/ +//------------------------------------------------- +// ~debug_view_expression - destructor +//------------------------------------------------- -static int memory_view_needs_recompute(debug_view *view) +debug_view_expression::~debug_view_expression() { - debug_view_memory *memdata = (debug_view_memory *)view->extra_data; - const address_space *space = memdata->desc->space; - int recompute = view->recompute; - - /* handle expression changes */ - if (debug_view_expression_changed_value(view, &memdata->expression, (space != NULL && space->cpu != NULL && space->cpu->type() == CPU) ? space->cpu : NULL)) - { - offs_t resultbyte; - - recompute = TRUE; - view->topleft.y = (memdata->expression.result - memdata->byte_offset) / memdata->bytes_per_row; - view->topleft.y = MAX(view->topleft.y, 0); - view->topleft.y = MIN(view->topleft.y, view->total.y - 1); - if (space != NULL) - resultbyte = memory_address_to_byte(space, memdata->expression.result) & space->logbytemask; - else - resultbyte = memdata->expression.result; - - memory_view_set_cursor_pos(view, resultbyte, memdata->bytes_per_chunk * 8 - 4); - } - - /* expression is clean at this point, and future recomputation is not necessary */ - view->recompute = FALSE; - return recompute; + // free our parsed expression + if (m_parsed != NULL) + expression_free(m_parsed); } -/*------------------------------------------------- - memory_view_get_cursor_pos - return the cursor - position as an address and a shift value --------------------------------------------------*/ +//------------------------------------------------- +// set_string - set the expression string +//------------------------------------------------- -static void memory_view_get_cursor_pos(debug_view *view, offs_t *address, UINT8 *shift) +void debug_view_expression::set_string(const char *string) { - debug_view_memory *memdata = (debug_view_memory *)view->extra_data; - const memory_view_pos *posdata = &memory_pos_table[memdata->bytes_per_chunk]; - int xposition, chunknum, chunkoffs; - - /* start with the base address for this row */ - *address = memdata->byte_offset + view->cursor.y * memdata->bytes_per_chunk * memdata->chunks_per_row; - - /* determine the X position within the middle section, clamping as necessary */ - xposition = view->cursor.x - memdata->section[1].pos - 1; - if (xposition < 0) - xposition = 0; - else if (xposition >= posdata->spacing * memdata->chunks_per_row) - xposition = posdata->spacing * memdata->chunks_per_row - 1; - - /* compute chunk number and offset within that chunk */ - chunknum = xposition / posdata->spacing; - chunkoffs = xposition % posdata->spacing; - - /* reverse the chunknum if we're reversed */ - if (memdata->reverse_view) - chunknum = memdata->chunks_per_row - 1 - chunknum; - - /* compute the address and shift */ - *address += chunknum * memdata->bytes_per_chunk; - *shift = posdata->shift[chunkoffs] & 0x7f; + m_string = string; + m_dirty = true; } -/*------------------------------------------------- - memory_view_set_cursor_pos - set the cursor - position as a function of an address and a - shift value --------------------------------------------------*/ +//------------------------------------------------- +// set_context - set the context for the +// expression +//------------------------------------------------- -static void memory_view_set_cursor_pos(debug_view *view, offs_t address, UINT8 shift) +void debug_view_expression::set_context(symbol_table *context) { - debug_view_memory *memdata = (debug_view_memory *)view->extra_data; - const memory_view_pos *posdata = &memory_pos_table[memdata->bytes_per_chunk]; - int chunknum; - - /* offset the address by the byte offset */ - if (address < memdata->byte_offset) - address = memdata->byte_offset; - address -= memdata->byte_offset; - - /* compute the Y coordinate and chunk index */ - view->cursor.y = address / memdata->bytes_per_row; - chunknum = (address % memdata->bytes_per_row) / memdata->bytes_per_chunk; - - /* reverse the chunknum if we're reversed */ - if (memdata->reverse_view) - chunknum = memdata->chunks_per_row - 1 - chunknum; - - /* scan within the chunk to find the shift */ - for (view->cursor.x = 0; view->cursor.x < posdata->spacing; view->cursor.x++) - if (posdata->shift[view->cursor.x] == shift) - break; - - /* add in the chunk offset and shift to the right of divider1 */ - view->cursor.x += memdata->section[1].pos + 1 + posdata->spacing * chunknum; - - /* clamp to the window bounds */ - view->cursor.x = MIN(view->cursor.x, view->total.x); - view->cursor.y = MIN(view->cursor.y, view->total.y); - - /* scroll if out of range */ - adjust_visible_x_for_cursor(view); - adjust_visible_y_for_cursor(view); + m_context = (context != NULL) ? context : debug_cpu_get_global_symtable(&m_machine); + m_dirty = true; } -/*------------------------------------------------- - memory_view_read - generic memory view data - reader --------------------------------------------------*/ +//------------------------------------------------- +// recompute - recompute the value of an +// expression +//------------------------------------------------- -static int memory_view_read(debug_view_memory *memdata, UINT8 size, offs_t offs, UINT64 *data) +bool debug_view_expression::recompute() { - /* if no raw data, just use the standard debug routines */ - if (memdata->desc->space != NULL) - { - const address_space *space = memdata->desc->space; - offs_t dummyaddr = offs; - int ismapped; - - ismapped = memdata->no_translation ? TRUE : debug_cpu_translate(space, TRANSLATE_READ_DEBUG, &dummyaddr); - *data = ~(UINT64)0; - if (ismapped) - { - switch (size) - { - case 1: *data = debug_read_byte(space, offs, !memdata->no_translation); break; - case 2: *data = debug_read_word(space, offs, !memdata->no_translation); break; - case 4: *data = debug_read_dword(space, offs, !memdata->no_translation); break; - case 8: *data = debug_read_qword(space, offs, !memdata->no_translation); break; - } - } - return ismapped; - } + bool changed = m_dirty; - /* if larger than a byte, reduce by half and recurse */ - if (size > 1) + // if dirty, re-evaluate + if (m_dirty) { - UINT64 data0, data1; - int ismapped; - - size /= 2; - ismapped = memory_view_read(memdata, size, offs + 0 * size, &data0); - ismapped |= memory_view_read(memdata, size, offs + 1 * size, &data1); - if (memdata->desc->endianness == ENDIANNESS_LITTLE) - *data = data0 | (data1 << (size * 8)); - else - *data = data1 | (data0 << (size * 8)); - return ismapped; - } - - /* all 0xff if out of bounds */ - offs ^= memdata->desc->offsetxor; - if (offs >= memdata->desc->length) - return FALSE; - *data = *((UINT8 *)memdata->desc->base + offs); - return TRUE; -} - - -/*------------------------------------------------- - memory_view_write - generic memory view data - writer --------------------------------------------------*/ - -static void memory_view_write(debug_view_memory *memdata, UINT8 size, offs_t offs, UINT64 data) -{ - /* if no raw data, just use the standard debug routines */ - if (memdata->desc->space != NULL) - { - const address_space *space = memdata->desc->space; - - switch (size) - { - case 1: debug_write_byte(space, offs, data, !memdata->no_translation); break; - case 2: debug_write_word(space, offs, data, !memdata->no_translation); break; - case 4: debug_write_dword(space, offs, data, !memdata->no_translation); break; - case 8: debug_write_qword(space, offs, data, !memdata->no_translation); break; - } - return; - } + // parse the new expression + parsed_expression *expr; + EXPRERR exprerr = expression_parse(m_string, m_context, &debug_expression_callbacks, &m_machine, &expr); - /* if larger than a byte, reduce by half and recurse */ - if (size > 1) - { - size /= 2; - if (memdata->desc->endianness == ENDIANNESS_LITTLE) - { - memory_view_write(memdata, size, offs + 0 * size, data); - memory_view_write(memdata, size, offs + 1 * size, data >> (8 * size)); - } - else + // if it worked, update the expression + if (exprerr == EXPRERR_NONE) { - memory_view_write(memdata, size, offs + 1 * size, data); - memory_view_write(memdata, size, offs + 0 * size, data >> (8 * size)); + if (m_parsed != NULL) + expression_free(m_parsed); + m_parsed = expr; } - return; - } - - /* ignore if out of bounds */ - offs ^= memdata->desc->offsetxor; - if (offs >= memdata->desc->length) - return; - *((UINT8 *)memdata->desc->base + offs) = data; - -/* hack for FD1094 editing */ -#ifdef FD1094_HACK - if (memdata->desc->base == memory_region(view->machine, "user2")) - { - extern void fd1094_regenerate_key(running_machine *machine); - fd1094_regenerate_key(view->machine); } -#endif -} - - -/*------------------------------------------------- - memory_view_get_subview_list - return a linked - list of subviews --------------------------------------------------*/ - -const memory_subview_item *memory_view_get_subview_list(debug_view *view) -{ - debug_view_memory *memdata = (debug_view_memory *)view->extra_data; - assert(view->type == DVT_MEMORY); - return memdata->subviewlist; -} - - -/*------------------------------------------------- - memory_view_get_subview - return the current - subview index --------------------------------------------------*/ - -int memory_view_get_subview(debug_view *view) -{ - debug_view_memory *memdata = (debug_view_memory *)view->extra_data; - - assert(view->type == DVT_MEMORY); - debug_view_begin_update(view); - debug_view_end_update(view); - - return memdata->desc->index; -} - -/*------------------------------------------------- - memory_view_get_expression - return the - expression string describing the home address --------------------------------------------------*/ - -const char *memory_view_get_expression(debug_view *view) -{ - debug_view_memory *memdata = (debug_view_memory *)view->extra_data; - assert(view->type == DVT_MEMORY); - debug_view_begin_update(view); - debug_view_end_update(view); - return memdata->expression.string; -} - - -/*------------------------------------------------- - memory_view_get_bytes_per_chunk - return the - currently displayed bytes per chunk --------------------------------------------------*/ - -UINT8 memory_view_get_bytes_per_chunk(debug_view *view) -{ - debug_view_memory *memdata = (debug_view_memory *)view->extra_data; - assert(view->type == DVT_MEMORY); - debug_view_begin_update(view); - debug_view_end_update(view); - return memdata->bytes_per_chunk; -} - - -/*------------------------------------------------- - memory_view_get_chunks_per_row - return the - number of chunks displayed across a row --------------------------------------------------*/ - -UINT32 memory_view_get_chunks_per_row(debug_view *view) -{ - debug_view_memory *memdata = (debug_view_memory *)view->extra_data; - assert(view->type == DVT_MEMORY); - debug_view_begin_update(view); - debug_view_end_update(view); - return memdata->chunks_per_row; -} - - -/*------------------------------------------------- - memory_view_get_reverse - return TRUE if the - memory view is displayed reverse --------------------------------------------------*/ - -UINT8 memory_view_get_reverse(debug_view *view) -{ - debug_view_memory *memdata = (debug_view_memory *)view->extra_data; - assert(view->type == DVT_MEMORY); - debug_view_begin_update(view); - debug_view_end_update(view); - return memdata->reverse_view; -} - - -/*------------------------------------------------- - memory_view_get_ascii - return TRUE if the - memory view is displaying an ASCII - representation --------------------------------------------------*/ - -UINT8 memory_view_get_ascii(debug_view *view) -{ - debug_view_memory *memdata = (debug_view_memory *)view->extra_data; - assert(view->type == DVT_MEMORY); - debug_view_begin_update(view); - debug_view_end_update(view); - return memdata->ascii_view; -} - - -/*------------------------------------------------- - memory_view_get_physical - return TRUE if the - memory view is displaying physical addresses - versus logical addresses --------------------------------------------------*/ - -UINT8 memory_view_get_physical(debug_view *view) -{ - debug_view_memory *memdata = (debug_view_memory *)view->extra_data; - assert(view->type == DVT_MEMORY); - debug_view_begin_update(view); - debug_view_end_update(view); - return memdata->no_translation; -} - - -/*------------------------------------------------- - memory_view_set_subview - select a new subview - by index --------------------------------------------------*/ - -void memory_view_set_subview(debug_view *view, int index) -{ - debug_view_memory *memdata = (debug_view_memory *)view->extra_data; - const memory_subview_item *subview; - - assert(view->type == DVT_MEMORY); - - /* pick the requested view */ - subview = memory_view_get_subview_by_index(memdata->subviewlist, index); - if (subview == NULL) - return; - - /* handle a change */ - if (subview != memdata->desc) + // if we have a parsed expression, evalute it + if (m_parsed != NULL) { - debug_view_begin_update(view); - memdata->desc = subview; - memdata->chunks_per_row = memdata->bytes_per_chunk * memdata->chunks_per_row / memdata->desc->prefsize; - memdata->bytes_per_chunk = memdata->desc->prefsize; - - /* we need to recompute the expression in the context of the new space */ - memdata->expression.dirty = TRUE; - view->recompute = view->update_pending = TRUE; - debug_view_end_update(view); - } -} + UINT64 oldresult = m_result; - -/*------------------------------------------------- - memory_view_set_expression - set the - expression string describing the home address --------------------------------------------------*/ - -void memory_view_set_expression(debug_view *view, const char *expression) -{ - debug_view_memory *memdata = (debug_view_memory *)view->extra_data; - - assert(view->type == DVT_MEMORY); - assert(expression != NULL); - - debug_view_begin_update(view); - debug_view_expression_set(&memdata->expression, expression); - view->recompute = view->update_pending = TRUE; - debug_view_end_update(view); -} - - -/*------------------------------------------------- - memory_view_set_bytes_per_chunk - specify the - number of bytes displayed per chunk --------------------------------------------------*/ - -void memory_view_set_bytes_per_chunk(debug_view *view, UINT8 chunkbytes) -{ - debug_view_memory *memdata = (debug_view_memory *)view->extra_data; - - assert(view->type == DVT_MEMORY); - assert(chunkbytes < ARRAY_LENGTH(memory_pos_table) && memory_pos_table[chunkbytes].spacing != 0); - - if (chunkbytes != memdata->bytes_per_chunk) - { - int endianness = memdata->desc->endianness; - offs_t address; - UINT8 shift; - - debug_view_begin_update(view); - memory_view_get_cursor_pos(view, &address, &shift); - address += (shift / 8) ^ ((endianness == ENDIANNESS_LITTLE) ? 0 : (memdata->bytes_per_chunk - 1)); - shift %= 8; - - memdata->bytes_per_chunk = chunkbytes; - memdata->chunks_per_row = memdata->bytes_per_row / chunkbytes; - view->recompute = view->update_pending = TRUE; - - shift += 8 * ((address % memdata->bytes_per_chunk) ^ ((endianness == ENDIANNESS_LITTLE) ? 0 : (memdata->bytes_per_chunk - 1))); - address -= address % memdata->bytes_per_chunk; - memory_view_set_cursor_pos(view, address, shift); - debug_view_end_update(view); - } -} - - -/*------------------------------------------------- - memory_view_set_chunks_per_row - specify the - number of chunks displayed across a row --------------------------------------------------*/ - -void memory_view_set_chunks_per_row(debug_view *view, UINT32 rowchunks) -{ - debug_view_memory *memdata = (debug_view_memory *)view->extra_data; - - assert(view->type == DVT_MEMORY); - - if (rowchunks < 1) - return; - - if (rowchunks != memdata->chunks_per_row) - { - offs_t address; - UINT8 shift; - - debug_view_begin_update(view); - memory_view_get_cursor_pos(view, &address, &shift); - memdata->chunks_per_row = rowchunks; - view->recompute = view->update_pending = TRUE; - memory_view_set_cursor_pos(view, address, shift); - debug_view_end_update(view); - } -} - - -/*------------------------------------------------- - memory_view_set_reverse - specify TRUE if the - memory view is displayed reverse --------------------------------------------------*/ - -void memory_view_set_reverse(debug_view *view, UINT8 reverse) -{ - debug_view_memory *memdata = (debug_view_memory *)view->extra_data; - - assert(view->type == DVT_MEMORY); - - if (reverse != memdata->reverse_view) - { - offs_t address; - UINT8 shift; - - debug_view_begin_update(view); - memory_view_get_cursor_pos(view, &address, &shift); - memdata->reverse_view = reverse; - view->recompute = view->update_pending = TRUE; - memory_view_set_cursor_pos(view, address, shift); - debug_view_end_update(view); + // recompute the value of the expression + EXPRERR exprerr = expression_execute(m_parsed, &m_result); + if (m_result != oldresult) + changed = true; } -} - - -/*------------------------------------------------- - memory_view_set_ascii - specify TRUE if the - memory view should display an ASCII - representation --------------------------------------------------*/ - -void memory_view_set_ascii(debug_view *view, UINT8 ascii) -{ - debug_view_memory *memdata = (debug_view_memory *)view->extra_data; - - assert(view->type == DVT_MEMORY); - if (ascii != memdata->ascii_view) - { - offs_t address; - UINT8 shift; - - debug_view_begin_update(view); - memory_view_get_cursor_pos(view, &address, &shift); - memdata->ascii_view = ascii; - view->recompute = view->update_pending = TRUE; - memory_view_set_cursor_pos(view, address, shift); - debug_view_end_update(view); - } -} - - -/*------------------------------------------------- - memory_view_set_physical - specify TRUE if the - memory view should display physical addresses - versus logical addresses --------------------------------------------------*/ - -void memory_view_set_physical(debug_view *view, UINT8 physical) -{ - debug_view_memory *memdata = (debug_view_memory *)view->extra_data; - - assert(view->type == DVT_MEMORY); - - if (physical != memdata->no_translation) - { - debug_view_begin_update(view); - memdata->no_translation = physical; - view->recompute = view->update_pending = TRUE; - debug_view_end_update(view); - } + // expression no longer dirty by definition + m_dirty = false; + return changed; } diff --git a/src/emu/debug/debugvw.h b/src/emu/debug/debugvw.h index c133da69f95..86f7ae463a2 100644 --- a/src/emu/debug/debugvw.h +++ b/src/emu/debug/debugvw.h @@ -4,173 +4,113 @@ Debugger view engine. - Copyright Nicola Salmoria and the MAME Team. - Visit http://mamedev.org for licensing and usage restrictions. +**************************************************************************** + + Copyright Aaron Giles + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name 'MAME' nor the names of its contributors may be + used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. -*********************************************************************/ +***************************************************************************/ #ifndef __DEBUGVIEW_H__ #define __DEBUGVIEW_H__ -/*************************************************************************** - CONSTANTS -***************************************************************************/ - -/* types passed to debug_view_alloc */ -#define DVT_CONSOLE (1) -#define DVT_REGISTERS (2) -#define DVT_DISASSEMBLY (3) -#define DVT_MEMORY (4) -#define DVT_LOG (5) -#define DVT_TIMERS (6) -#define DVT_ALLOCS (7) +//************************************************************************** +// CONSTANTS +//************************************************************************** -enum _disasm_right_column +// types passed to debug_view_manager::alloc_view() +enum debug_view_type { - DASM_RIGHTCOL_NONE, - DASM_RIGHTCOL_RAW, - DASM_RIGHTCOL_ENCRYPTED, - DASM_RIGHTCOL_COMMENTS + DVT_NONE, + DVT_CONSOLE, + DVT_STATE, + DVT_DISASSEMBLY, + DVT_MEMORY, + DVT_LOG, + DVT_TIMERS, + DVT_ALLOCS }; -typedef enum _disasm_right_column disasm_right_column; - -/* properties available for disassembly views */ -#define DVP_DASM_EXPRESSION (101) /* r/w - const char * */ -#define DVP_DASM_TRACK_LIVE (102) /* r/w - UINT32 */ -#define DVP_DASM_RIGHT_COLUMN (103) /* r/w - UINT32 */ -#define DVP_DASM_BACKWARD_STEPS (104) /* r/w - UINT32 */ -#define DVP_DASM_WIDTH (105) /* r/w - UINT32 */ -#define DVP_DASM_ACTIVE_ADDRESS (112) /* r/w - UINT32 */ - -/* properties available for memory views */ -#define DVP_MEM_SPACE (100) /* r/w - address space * */ -#define DVP_MEM_EXPRESSION (101) /* r/w - const char * */ -#define DVP_MEM_TRACK_LIVE (102) /* r/w - UINT32 */ -#define DVP_MEM_SPACENUM (103) /* r/w - UINT32 */ -#define DVP_MEM_BYTES_PER_CHUNK (104) /* r/w - UINT32 */ -#define DVP_MEM_REVERSE_VIEW (105) /* r/w - UINT32 */ -#define DVP_MEM_ASCII_VIEW (106) /* r/w - UINT32 */ -#define DVP_MEM_RAW_BASE (107) /* r/w - void * */ -#define DVP_MEM_RAW_LENGTH (108) /* r/w - UINT32 */ -#define DVP_MEM_RAW_OFFSET_XOR (109) /* r/w - UINT32 */ -#define DVP_MEM_RAW_LITTLE_ENDIAN (110) /* r/w - UINT32 */ -#define DVP_MEM_WIDTH (111) /* r/w - UINT32 */ -#define DVP_MEM_NO_TRANSLATION (113) /* r/w - UINT32 */ - -/* attribute bits for debug_view_char.attrib */ -#define DCA_NORMAL (0x00) /* in Windows: black on white */ -#define DCA_CHANGED (0x01) /* in Windows: red foreground */ -#define DCA_SELECTED (0x02) /* in Windows: light red background */ -#define DCA_INVALID (0x04) /* in Windows: dark blue foreground */ -#define DCA_DISABLED (0x08) /* in Windows: darker foreground */ -#define DCA_ANCILLARY (0x10) /* in Windows: grey background */ -#define DCA_CURRENT (0x20) /* in Windows: yellow background */ -#define DCA_COMMENT (0x40) /* in Windows: green foreground */ - -/* special characters that can be passed as a DVP_CHARACTER */ -#define DCH_UP (1) /* up arrow */ -#define DCH_DOWN (2) /* down arrow */ -#define DCH_LEFT (3) /* left arrow */ -#define DCH_RIGHT (4) /* right arrow */ -#define DCH_PUP (5) /* page up */ -#define DCH_PDOWN (6) /* page down */ -#define DCH_HOME (7) /* home */ -#define DCH_CTRLHOME (8) /* ctrl+home */ -#define DCH_END (9) /* end */ -#define DCH_CTRLEND (10) /* ctrl+end */ -#define DCH_CTRLRIGHT (11) /* ctrl+right */ -#define DCH_CTRLLEFT (12) /* ctrl+left */ - - - -/*************************************************************************** - TYPE DEFINITIONS -***************************************************************************/ - -/* opaque structure representing a debug view */ -class debug_view; - - -/* OSD callback function for a view */ -typedef void (*debug_view_osd_update_func)(debug_view *view, void *osdprivate); -/* pair of X,Y coordinates for sizing */ -struct debug_view_xy +// notifications passed to view_notify() +enum debug_view_notification { - INT32 x; - INT32 y; + VIEW_NOTIFY_NONE, + VIEW_NOTIFY_VISIBLE_CHANGED, + VIEW_NOTIFY_CURSOR_CHANGED, + VIEW_NOTIFY_SOURCE_CHANGED }; -/* a registers subview item */ -class registers_subview_item -{ - DISABLE_COPYING(registers_subview_item); +// attribute bits for debug_view_char.attrib +const UINT8 DCA_NORMAL = 0x00; // in Windows: black on white +const UINT8 DCA_CHANGED = 0x01; // in Windows: red foreground +const UINT8 DCA_SELECTED = 0x02; // in Windows: light red background +const UINT8 DCA_INVALID = 0x04; // in Windows: dark blue foreground +const UINT8 DCA_DISABLED = 0x08; // in Windows: darker foreground +const UINT8 DCA_ANCILLARY = 0x10; // in Windows: grey background +const UINT8 DCA_CURRENT = 0x20; // in Windows: yellow background +const UINT8 DCA_COMMENT = 0x40; // in Windows: green foreground -public: - registers_subview_item() - : next(NULL), - index(0), - stateintf(NULL), - execintf(NULL) { } - - registers_subview_item *next; /* link to next item */ - int index; /* index of this item */ - astring name; /* name of the subview item */ - device_state_interface *stateintf; /* state interface */ - device_execute_interface *execintf; /* execution interface */ -}; +// special characters that can be passed to process_char() +const int DCH_UP = 1; // up arrow +const int DCH_DOWN = 2; // down arrow +const int DCH_LEFT = 3; // left arrow +const int DCH_RIGHT = 4; // right arrow +const int DCH_PUP = 5; // page up +const int DCH_PDOWN = 6; // page down +const int DCH_HOME = 7; // home +const int DCH_CTRLHOME = 8; // ctrl+home +const int DCH_END = 9; // end +const int DCH_CTRLEND = 10; // ctrl+end +const int DCH_CTRLRIGHT = 11; // ctrl+right +const int DCH_CTRLLEFT = 12; // ctrl+left -/* a disassembly subview item */ -class disasm_subview_item -{ - DISABLE_COPYING(disasm_subview_item); -public: - disasm_subview_item() - : next(NULL), - index(0), - space(NULL) { } - - disasm_subview_item *next; /* link to next item */ - int index; /* index of this item */ - astring name; /* name of the subview item */ - const address_space *space; /* address space to display */ -}; +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** -/* a memory subview item */ -class memory_subview_item -{ - DISABLE_COPYING(memory_subview_item); +// forward references +class debug_view; +typedef struct _symbol_table symbol_table; +typedef struct _parsed_expression parsed_expression; -public: - memory_subview_item() - : next(NULL), - index(0), - space(NULL), - base(NULL), - length(0), - offsetxor(0), - endianness(ENDIANNESS_NATIVE), - prefsize(1) { } - - memory_subview_item *next; /* link to next item */ - int index; /* index of this item */ - astring name; /* name of the subview item */ - const address_space *space; /* address space we reference (if any) */ - void * base; /* pointer to memory base */ - offs_t length; /* length of memory */ - offs_t offsetxor; /* XOR to apply to offsets */ - UINT8 endianness; /* endianness of memory */ - UINT8 prefsize; /* preferred bytes per chunk */ -}; + +// OSD callback function for a view +typedef void (*debug_view_osd_update_func)(debug_view &view, void *osdprivate); -/* a single "character" in the debug view has an ASCII value and an attribute byte */ +// a single "character" in the debug view has an ASCII value and an attribute byte struct debug_view_char { UINT8 byte; @@ -178,281 +118,212 @@ struct debug_view_char }; +// pair of X,Y coordinates for sizing +class debug_view_xy +{ +public: + debug_view_xy(int _x = 0, int _y = 0) : x(_x), y(_y) { } -/*************************************************************************** - FUNCTION PROTOTYPES -***************************************************************************/ - - -/* ----- initialization and cleanup ----- */ - -/* initializes the view system */ -void debug_view_init(running_machine *machine); - - - -/* ----- view creation/deletion ----- */ - -/* allocate a new debug view */ -debug_view *debug_view_alloc(running_machine *machine, int type, debug_view_osd_update_func osdupdate, void *osdprivate); - -/* free a debug view */ -void debug_view_free(debug_view *view); - - - -/* ----- update management ----- */ - -/* begin a sequence of changes so that only one update occurs */ -void debug_view_begin_update(debug_view *view); - -/* complete a sequence of changes so that only one update occurs */ -void debug_view_end_update(debug_view *view); - -/* flush any pending updates to the OSD layer */ -void debug_view_flush_updates(running_machine *machine); - -/* force all views to refresh */ -void debug_view_update_all(running_machine *machine); - -/* force all views of a given type to refresh */ -void debug_view_update_type(running_machine *machine, int type); - - - -/* ----- standard view properties ----- */ - -/* return a pointer to a 2-dimentional array of characters that represent the visible area of the view */ -const debug_view_char *debug_view_get_chars(debug_view *view); - -/* type a character into a view */ -void debug_view_type_character(debug_view *view, int character); - - - -/* ----- standard view sizing ----- */ - -/* return the total view size in rows and columns */ -debug_view_xy debug_view_get_total_size(debug_view *view); - -/* return the visible size in rows and columns */ -debug_view_xy debug_view_get_visible_size(debug_view *view); - -/* return the top left position of the visible area in rows and columns */ -debug_view_xy debug_view_get_visible_position(debug_view *view); - -/* set the visible size in rows and columns */ -void debug_view_set_visible_size(debug_view *view, debug_view_xy size); - -/* set the top left position of the visible area in rows and columns */ -void debug_view_set_visible_position(debug_view *view, debug_view_xy pos); - - - -/* ----- standard view cursor management ----- */ - -/* return the current cursor position as a row and column */ -debug_view_xy debug_view_get_cursor_position(debug_view *view); - -/* return TRUE if a cursor is supported for this view type */ -int debug_view_get_cursor_supported(debug_view *view); - -/* return TRUE if a cursor is currently visible */ -int debug_view_get_cursor_visible(debug_view *view); - -/* set the current cursor position as a row and column */ -void debug_view_set_cursor_position(debug_view *view, debug_view_xy pos); - -/* set the visible state of the cursor */ -void debug_view_set_cursor_visible(debug_view *view, int visible); - - - -/* ----- registers view-specific properties ----- */ - -/* return a linked list of subviews */ -const registers_subview_item *registers_view_get_subview_list(debug_view *view); - -/* return the current subview index */ -int registers_view_get_subview(debug_view *view); - -/* select a new subview by index */ -void registers_view_set_subview(debug_view *view, int index); - - - -/* ----- disassembly view-specific properties ----- */ - -/* return a linked list of subviews */ -const disasm_subview_item *disasm_view_get_subview_list(debug_view *view); - -/* return the current subview index */ -int disasm_view_get_subview(debug_view *view); - -/* return the expression string describing the home address */ -const char *disasm_view_get_expression(debug_view *view); - -/* return the contents of the right column */ -disasm_right_column disasm_view_get_right_column(debug_view *view); - -/* return the number of instructions displayed before the home address */ -UINT32 disasm_view_get_backward_steps(debug_view *view); - -/* return the width in characters of the main disassembly section */ -UINT32 disasm_view_get_disasm_width(debug_view *view); - -/* return the PC of the currently selected address in the view */ -offs_t disasm_view_get_selected_address(debug_view *view); - -/* select a new subview by index */ -void disasm_view_set_subview(debug_view *view, int index); - -/* set the expression string describing the home address */ -void disasm_view_set_expression(debug_view *view, const char *expression); - -/* set the contents of the right column */ -void disasm_view_set_right_column(debug_view *view, disasm_right_column contents); - -/* set the number of instructions displayed before the home address */ -void disasm_view_set_backward_steps(debug_view *view, UINT32 steps); - -/* set the width in characters of the main disassembly section */ -void disasm_view_set_disasm_width(debug_view *view, UINT32 width); - -/* set the PC of the currently selected address in the view */ -void disasm_view_set_selected_address(debug_view *view, offs_t address); - - - -/* ----- memory view-specific properties ----- */ - -/* return a linked list of subviews */ -const memory_subview_item *memory_view_get_subview_list(debug_view *view); - -/* return the current subview index */ -int memory_view_get_subview(debug_view *view); - -/* return the expression string describing the home address */ -const char *memory_view_get_expression(debug_view *view); - -/* return the currently displayed bytes per chunk */ -UINT8 memory_view_get_bytes_per_chunk(debug_view *view); - -/* return the number of chunks displayed across a row */ -UINT32 memory_view_get_chunks_per_row(debug_view *view); - -/* return TRUE if the memory view is displayed reverse */ -UINT8 memory_view_get_reverse(debug_view *view); - -/* return TRUE if the memory view is displaying an ASCII representation */ -UINT8 memory_view_get_ascii(debug_view *view); - -/* return TRUE if the memory view is displaying physical addresses versus logical addresses */ -UINT8 memory_view_get_physical(debug_view *view); - -/* select a new subview by index */ -void memory_view_set_subview(debug_view *view, int index); - -/* set the expression string describing the home address */ -void memory_view_set_expression(debug_view *view, const char *expression); - -/* specify the number of bytes displayed per chunk */ -void memory_view_set_bytes_per_chunk(debug_view *view, UINT8 chunkbytes); - -/* specify the number of chunks displayed across a row */ -void memory_view_set_chunks_per_row(debug_view *view, UINT32 rowchunks); - -/* specify TRUE if the memory view is displayed reverse */ -void memory_view_set_reverse(debug_view *view, UINT8 reverse); - -/* specify TRUE if the memory view should display an ASCII representation */ -void memory_view_set_ascii(debug_view *view, UINT8 ascii); - -/* specify TRUE if the memory view should display physical addresses versus logical addresses */ -void memory_view_set_physical(debug_view *view, UINT8 physical); - - - -/*************************************************************************** - INLINE FUNCTIONS -***************************************************************************/ + INT32 x; + INT32 y; +}; -/*------------------------------------------------- - registers_view_get_subview_by_index - return a - pointer to a registers subview by index --------------------------------------------------*/ -INLINE const registers_subview_item *registers_view_get_subview_by_index(const registers_subview_item *itemlist, int index) +// debug_view_sources select from multiple sources available within a view +class debug_view_source { - for ( ; itemlist != NULL; itemlist = itemlist->next) - if (itemlist->index == index) - return itemlist; - return NULL; -} + DISABLE_COPYING(debug_view_source); + + friend class debug_view_source_list; +public: + // construction/destruction + debug_view_source(const char *name, device_t *device = NULL); + virtual ~debug_view_source(); + + // getters + const char *name() const { return m_name; } + debug_view_source *next() const { return m_next; } + device_t *device() const { return m_device; } + +private: + // internal state + debug_view_source * m_next; // link to next item + astring m_name; // name of the source item + device_t * m_device; // associated device (if applicable) +}; -/*------------------------------------------------- - registers_view_get_current_subview - return a - pointer to the current subview of a - registers view --------------------------------------------------*/ -INLINE const registers_subview_item *registers_view_get_current_subview(debug_view *view) +// a debug_view_source_list contains a list of debug_view_sources +class debug_view_source_list { - return registers_view_get_subview_by_index(registers_view_get_subview_list(view), registers_view_get_subview(view)); -} + DISABLE_COPYING(debug_view_source_list); +public: + // construction/destruction + debug_view_source_list(running_machine &machine); + ~debug_view_source_list(); + + // getters + const debug_view_source *head() const { return m_head; } + int count() const { return m_count; } + int index(const debug_view_source &source) const; + const debug_view_source *by_index(int index) const; + + // operations + void reset(); + void append(debug_view_source &view_source); + const debug_view_source *match_device(device_t *device) const; + int match_device_index(device_t *device) const { return index(*match_device(device)); } + +private: + // internal state + running_machine & m_machine; // reference to our machine + debug_view_source * m_head; // head of the list + debug_view_source * m_tail; // end of the tail + UINT32 m_count; // number of items in the list +}; -/*------------------------------------------------- - disasm_view_get_subview_by_index - return a - pointer to a disasm subview by index --------------------------------------------------*/ -INLINE const disasm_subview_item *disasm_view_get_subview_by_index(const disasm_subview_item *itemlist, int index) +// debug_view describes a single text-based view +class debug_view { - for ( ; itemlist != NULL; itemlist = itemlist->next) - if (itemlist->index == index) - return itemlist; - return NULL; -} + friend class debug_view_manager; + +protected: + // construction/destruction + debug_view(running_machine &machine, debug_view_type type, debug_view_osd_update_func osdupdate, void *osdprivate); + virtual ~debug_view(); +public: + // getters + running_machine &machine() const { return m_machine; } + debug_view *next() const { return m_next; } + debug_view_type type() const { return m_type; } + const debug_view_char *viewdata() const { return m_viewdata; } + debug_view_xy total_size() { flush_updates(); return m_total; } + debug_view_xy visible_size() { flush_updates(); return m_visible; } + debug_view_xy visible_position() { flush_updates(); return m_topleft; } + debug_view_xy cursor_position() { flush_updates(); return m_cursor; } + bool cursor_supported() { flush_updates(); return m_supports_cursor; } + bool cursor_visible() { flush_updates(); return m_cursor_visible; } + const debug_view_source *source() const { return m_source; } + const debug_view_source_list &source_list() const { return m_source_list; } + + // setters + void set_size(int width, int height); + void set_visible_size(debug_view_xy size); + void set_visible_position(debug_view_xy pos); + void set_cursor_position(debug_view_xy pos); + void set_cursor_visible(bool visible = true); + void set_source(const debug_view_source &source); + void process_char(int character) { view_char(character); } + +protected: + // internal updating helpers + void begin_update() { m_update_level++; } + void end_update(); + void force_update() { begin_update(); m_update_pending = true; end_update(); } + void flush_updates() { begin_update(); end_update(); } + void flush_osd_updates(); + + // cursor management helpers + void adjust_visible_x_for_cursor(); + void adjust_visible_y_for_cursor(); + + // overridables + virtual void view_update() = 0; + virtual void view_notify(debug_view_notification type); + virtual void view_char(int chval); + +protected: + // core view data + debug_view * m_next; // link to the next view + running_machine & m_machine; // machine associated with this view + debug_view_type m_type; // type of view + const debug_view_source *m_source; // currently selected data source + debug_view_source_list m_source_list; // list of available data sources + + // OSD data + debug_view_osd_update_func m_osdupdate; // callback for the update + void * m_osdprivate; // OSD-managed private data + + // visibility info + debug_view_xy m_visible; // visible size (in rows and columns) + debug_view_xy m_total; // total size (in rows and columns) + debug_view_xy m_topleft; // top-left visible position (in rows and columns) + debug_view_xy m_cursor; // cursor position + bool m_supports_cursor; // does this view support a cursor? + bool m_cursor_visible; // is the cursor visible? + + // update info + bool m_recompute; // does this view require a recomputation? + UINT8 m_update_level; // update level; updates when this hits 0 + bool m_update_pending; // true if there is a pending update + bool m_osd_update_pending; // true if there is a pending update + debug_view_char * m_viewdata; // current array of view data + int m_viewdata_size; // number of elements of the viewdata array +}; -/*------------------------------------------------- - disasm_view_get_current_subview - return a - pointer to the current subview of a - disassembly view --------------------------------------------------*/ -INLINE const disasm_subview_item *disasm_view_get_current_subview(debug_view *view) +// debug_view_manager manages all the views +class debug_view_manager { - return disasm_view_get_subview_by_index(disasm_view_get_subview_list(view), disasm_view_get_subview(view)); -} +public: + // construction/destruction + debug_view_manager(running_machine &machine); + ~debug_view_manager(); + // view allocation + debug_view *alloc_view(debug_view_type type, debug_view_osd_update_func osdupdate, void *osdprivate); + void free_view(debug_view &view); -/*------------------------------------------------- - memory_view_get_subview_by_index - return a - pointer to a memory subview by index --------------------------------------------------*/ + // update helpers + void update_all(debug_view_type type = DVT_NONE); + void flush_osd_updates(); -INLINE const memory_subview_item *memory_view_get_subview_by_index(const memory_subview_item *itemlist, int index) -{ - for ( ; itemlist != NULL; itemlist = itemlist->next) - if (itemlist->index == index) - return itemlist; - return NULL; -} +private: + // private helpers + debug_view *append(debug_view *view); + // internal state + running_machine & m_machine; // reference to our machine + debug_view * m_viewlist; // list of views +}; -/*------------------------------------------------- - memory_view_get_current_subview - return a - pointer to the current subview of a - memory view --------------------------------------------------*/ -INLINE const memory_subview_item *memory_view_get_current_subview(debug_view *view) +// debug_view_expression is a helper for handling embedded expressions +class debug_view_expression { - return memory_view_get_subview_by_index(memory_view_get_subview_list(view), memory_view_get_subview(view)); -} +public: + // construction/destruction + debug_view_expression(running_machine &machine); + ~debug_view_expression(); + + // getters + bool dirty() const { return m_dirty; } + UINT64 last_value() const { return m_result; } + UINT64 value() { if (m_dirty) recompute(); return m_result; } + const char *string() const { return m_string; } + symbol_table *context() const { return m_context; } + + // setters + void mark_dirty() { m_dirty = true; } + void set_string(const char *string); + void set_context(symbol_table *context); + +private: + // internal helpers + bool recompute(); + + // internal state + running_machine & m_machine; // reference to the machine + bool m_dirty; // true if the expression needs to be re-evaluated + UINT64 m_result; // last result from the expression + parsed_expression * m_parsed; // parsed expression data + astring m_string; // copy of the expression string + symbol_table * m_context; // context we are using +}; #endif diff --git a/src/emu/debug/dvdisasm.c b/src/emu/debug/dvdisasm.c new file mode 100644 index 00000000000..4df4802ca1f --- /dev/null +++ b/src/emu/debug/dvdisasm.c @@ -0,0 +1,710 @@ +/********************************************************************* + + dvdisasm.c + + Disassembly debugger view. + +**************************************************************************** + + 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. + +***************************************************************************/ + +#include "emu.h" +#include "debugvw.h" +#include "dvdisasm.h" +#include "debugcmt.h" +#include "debugcpu.h" + + + +//************************************************************************** +// DEBUG VIEW DISASM SOURCE +//************************************************************************** + +//------------------------------------------------- +// debug_view_disasm_source - constructor +//------------------------------------------------- + +debug_view_disasm_source::debug_view_disasm_source(const char *name, device_t &device) + : debug_view_source(name, &device), + m_device(device), + m_disasmintf(dynamic_cast<device_disasm_interface *>(&device)), + m_space(dynamic_cast<device_memory_interface *>(&device)->space(AS_PROGRAM)) +{ +} + + + +//************************************************************************** +// DEBUG VIEW DISASM +//************************************************************************** + +//------------------------------------------------- +// debug_view_disasm - constructor +//------------------------------------------------- + +debug_view_disasm::debug_view_disasm(running_machine &machine, debug_view_osd_update_func osdupdate, void *osdprivate) + : debug_view(machine, DVT_DISASSEMBLY, osdupdate, osdprivate), + m_right_column(DASM_RIGHTCOL_RAW), + m_backwards_steps(3), + m_dasm_width(DEFAULT_DASM_WIDTH), + m_last_direct_raw(NULL), + m_last_direct_decrypted(NULL), + m_last_change_count(0), + m_last_pcbyte(0), + m_divider1(0), + m_divider2(0), + m_divider3(0), + m_expression(machine), + m_allocated(0,0), + m_byteaddress(NULL), + m_dasm(NULL) +{ + // fail if no available sources + enumerate_sources(); + if (m_source_list.count() == 0) + throw std::bad_alloc(); + + // count the number of comments + int total_comments = 0; + for (const debug_view_source *source = m_source_list.head(); source != NULL; source = source->next()) + { + const debug_view_disasm_source &dasmsource = downcast<const debug_view_disasm_source &>(*source); + total_comments += debug_comment_get_count(&dasmsource.m_device); + } + + // initialize + if (total_comments > 0) + m_right_column = DASM_RIGHTCOL_COMMENTS; + + // configure the view + m_total.y = DEFAULT_DASM_LINES; + m_supports_cursor = true; +} + + +//------------------------------------------------- +// ~debug_view_disasm - destructor +//------------------------------------------------- + +debug_view_disasm::~debug_view_disasm() +{ + auto_free(&m_machine, m_byteaddress); + auto_free(&m_machine, m_dasm); +} + + +//------------------------------------------------- +// enumerate_sources - enumerate all possible +// sources for a disassembly view +//------------------------------------------------- + +void debug_view_disasm::enumerate_sources() +{ + // start with an empty list + m_source_list.reset(); + + // iterate over devices with disassembly interfaces + device_disasm_interface *dasm = NULL; + astring name; + for (bool gotone = m_machine.devicelist.first(dasm); gotone; gotone = dasm->next(dasm)) + { + name.printf("%s '%s'", dasm->device().name(), dasm->device().tag()); + m_source_list.append(*auto_alloc(&m_machine, debug_view_disasm_source(name, dasm->device()))); + } + + // reset the source to a known good entry + m_source = m_source_list.head(); +} + + +//------------------------------------------------- +// view_notify - handle notification of updates +// to cursor changes +//------------------------------------------------- + +void debug_view_disasm::view_notify(debug_view_notification type) +{ + if (type == VIEW_NOTIFY_CURSOR_CHANGED) + adjust_visible_y_for_cursor(); + + else if (type == VIEW_NOTIFY_SOURCE_CHANGED) + m_expression.set_context(debug_cpu_get_symtable(&downcast<const debug_view_disasm_source *>(m_source)->device())); +} + + +//------------------------------------------------- +// view_char - handle a character typed within +// the current view +//------------------------------------------------- + +void debug_view_disasm::view_char(int chval) +{ + debug_view_xy origcursor = m_cursor; + UINT8 end_buffer = 3; + INT32 temp; + + switch (chval) + { + case DCH_UP: + if (m_cursor.y > 0) + m_cursor.y--; + break; + + case DCH_DOWN: + if (m_cursor.y < m_total.y - 1) + m_cursor.y++; + break; + + case DCH_PUP: + temp = m_cursor.y - (m_visible.y - end_buffer); + if (temp < 0) + m_cursor.y = 0; + else + m_cursor.y = temp; + break; + + case DCH_PDOWN: + temp = m_cursor.y + (m_visible.y - end_buffer); + if (temp > m_total.y - 1) + m_cursor.y = m_total.y - 1; + else + m_cursor.y = temp; + break; + + case DCH_HOME: // set the active column to the PC + { + const debug_view_disasm_source &source = downcast<const debug_view_disasm_source &>(*m_source); + offs_t pc = memory_address_to_byte(source.m_space, cpu_get_pc(&source.m_device)) & source.m_space->logbytemask; + + // figure out which row the pc is on + for (int curline = 0; curline < m_allocated.y; curline++) + if (m_byteaddress[curline] == pc) + m_cursor.y = curline; + break; + } + + case DCH_CTRLHOME: + m_cursor.y = 0; + break; + + case DCH_CTRLEND: + m_cursor.y = m_total.y - 1; + break; + } + + /* send a cursor changed notification */ + if (m_cursor.y != origcursor.y) + { + begin_update(); + view_notify(VIEW_NOTIFY_CURSOR_CHANGED); + m_update_pending = true; + end_update(); + } +} + + +//------------------------------------------------- +// find_pc_backwards - back up the specified +// number of instructions from the given PC +//------------------------------------------------- + +offs_t debug_view_disasm::find_pc_backwards(offs_t targetpc, int numinstrs) +{ + const debug_view_disasm_source &source = downcast<const debug_view_disasm_source &>(*m_source); + + // compute the increment + int minlen = memory_byte_to_address(source.m_space, source.m_disasmintf->min_opcode_bytes()); + if (minlen == 0) minlen = 1; + int maxlen = memory_byte_to_address(source.m_space, source.m_disasmintf->max_opcode_bytes()); + if (maxlen == 0) maxlen = 1; + + // start off numinstrs back + offs_t curpc = targetpc - minlen * numinstrs; + if (curpc > targetpc) + curpc = 0; + + /* loop until we find what we are looking for */ + offs_t targetpcbyte = memory_address_to_byte(source.m_space, targetpc) & source.m_space->logbytemask; + offs_t fillpcbyte = targetpcbyte; + offs_t lastgoodpc = targetpc; + while (1) + { + // fill the buffer up to the target + offs_t curpcbyte = memory_address_to_byte(source.m_space, curpc) & source.m_space->logbytemask; + UINT8 opbuf[1024], argbuf[1024]; + while (curpcbyte < fillpcbyte) + { + fillpcbyte--; + opbuf[1000 + fillpcbyte - targetpcbyte] = debug_read_opcode(source.m_space, fillpcbyte, 1, FALSE); + argbuf[1000 + fillpcbyte - targetpcbyte] = debug_read_opcode(source.m_space, fillpcbyte, 1, TRUE); + } + + // loop until we get past the target instruction + int instcount = 0; + int instlen; + offs_t scanpc; + for (scanpc = curpc; scanpc < targetpc; scanpc += instlen) + { + offs_t scanpcbyte = memory_address_to_byte(source.m_space, scanpc) & source.m_space->logbytemask; + offs_t physpcbyte = scanpcbyte; + + // get the disassembly, but only if mapped + instlen = 1; + if (debug_cpu_translate(source.m_space, TRANSLATE_FETCH, &physpcbyte)) + { + char dasmbuffer[100]; + instlen = source.m_disasmintf->disassemble(dasmbuffer, scanpc, &opbuf[1000 + scanpcbyte - targetpcbyte], &argbuf[1000 + scanpcbyte - targetpcbyte]) & DASMFLAG_LENGTHMASK; + } + + // count this one + instcount++; + } + + // if we ended up right on targetpc, this is a good candidate + if (scanpc == targetpc && instcount <= numinstrs) + lastgoodpc = curpc; + + // we're also done if we go back too far + if (targetpc - curpc >= numinstrs * maxlen) + break; + + // and if we hit 0, we're done + if (curpc == 0) + break; + + // back up one more and try again + curpc -= minlen; + if (curpc > targetpc) + curpc = 0; + } + + return lastgoodpc; +} + + +//------------------------------------------------- +// generate_bytes - generate the opcode byte +// values +//------------------------------------------------- + +void debug_view_disasm::generate_bytes(offs_t pcbyte, int numbytes, int minbytes, char *string, int maxchars, bool encrypted) +{ + const debug_view_disasm_source &source = downcast<const debug_view_disasm_source &>(*m_source); + + // output the first value + int offset = 0; + if (maxchars >= 2 * minbytes) + offset = sprintf(string, "%s", core_i64_hex_format(debug_read_opcode(source.m_space, pcbyte, minbytes, FALSE), minbytes * 2)); + + // output subsequent values + int byte; + for (byte = minbytes; byte < numbytes && offset + 1 + 2 * minbytes < maxchars; byte += minbytes) + offset += sprintf(&string[offset], " %s", core_i64_hex_format(debug_read_opcode(source.m_space, pcbyte + byte, minbytes, encrypted), minbytes * 2)); + + // if we ran out of room, indicate more + string[maxchars - 1] = 0; + if (byte < numbytes && maxchars > 3) + string[maxchars - 2] = string[maxchars - 3] = string[maxchars - 4] = '.'; +} + + +//------------------------------------------------- +// recompute - recompute selected info for the +// disassembly view +//------------------------------------------------- + +bool debug_view_disasm::recompute(offs_t pc, int startline, int lines) +{ + bool changed = false; + const debug_view_disasm_source &source = downcast<const debug_view_disasm_source &>(*m_source); + + // determine how many characters we need for an address and set the divider + m_divider1 = 1 + source.m_space->logaddrchars + 1; + + // assume a fixed number of characters for the disassembly + m_divider2 = m_divider1 + 1 + m_dasm_width + 1; + + // determine how many bytes we might need to display + int minbytes = source.m_disasmintf->min_opcode_bytes(); + int maxbytes = source.m_disasmintf->max_opcode_bytes(); + + // ensure that the PC is aligned to the minimum opcode size + pc &= ~memory_byte_to_address_end(source.m_space, minbytes - 1); + + // set the width of the third column according to display mode + if (m_right_column == DASM_RIGHTCOL_RAW || m_right_column == DASM_RIGHTCOL_ENCRYPTED) + { + int maxbytes_clamped = MIN(maxbytes, DASM_MAX_BYTES); + m_total.x = m_divider2 + 1 + 2 * maxbytes_clamped + (maxbytes_clamped / minbytes - 1) + 1; + } + else if (m_right_column == DASM_RIGHTCOL_COMMENTS) + m_total.x = m_divider2 + 1 + 50; // DEBUG_COMMENT_MAX_LINE_LENGTH + else + m_total.x = m_divider2 + 1; + + // reallocate memory if we don't have enough + if (m_allocated.x < m_total.x || m_allocated.y < m_total.y) + { + // update our values + m_allocated = m_total; + + // allocate address array + auto_free(&m_machine, m_byteaddress); + m_byteaddress = auto_alloc_array(&m_machine, offs_t, m_allocated.y); + + // allocate disassembly buffer + auto_free(&m_machine, m_dasm); + m_dasm = auto_alloc_array(&m_machine, char, m_allocated.x * m_allocated.y); + } + + // iterate over lines + for (int line = 0; line < lines; line++) + { + // convert PC to a byte offset + offs_t pcbyte = memory_address_to_byte(source.m_space, pc) & source.m_space->logbytemask; + + // save a copy of the previous line as a backup if we're only doing one line + int instr = startline + line; + char *destbuf = &m_dasm[instr * m_allocated.x]; + char oldbuf[100]; + if (lines == 1) + strncpy(oldbuf, destbuf, MIN(sizeof(oldbuf), m_allocated.x)); + + // convert back and set the address of this instruction + m_byteaddress[instr] = pcbyte; + sprintf(&destbuf[0], " %s ", core_i64_hex_format(memory_byte_to_address(source.m_space, pcbyte), source.m_space->logaddrchars)); + + // make sure we can translate the address, and then disassemble the result + char buffer[100]; + int numbytes = 0; + offs_t physpcbyte = pcbyte; + if (debug_cpu_translate(source.m_space, TRANSLATE_FETCH_DEBUG, &physpcbyte)) + { + UINT8 opbuf[64], argbuf[64]; + + // fetch the bytes up to the maximum + for (numbytes = 0; numbytes < maxbytes; numbytes++) + { + opbuf[numbytes] = debug_read_opcode(source.m_space, pcbyte + numbytes, 1, FALSE); + argbuf[numbytes] = debug_read_opcode(source.m_space, pcbyte + numbytes, 1, TRUE); + } + + // disassemble the result + pc += numbytes = source.m_disasmintf->disassemble(buffer, pc & source.m_space->logaddrmask, opbuf, argbuf) & DASMFLAG_LENGTHMASK; + } + else + strcpy(buffer, "<unmapped>"); + + // append the disassembly to the buffer + sprintf(&destbuf[m_divider1 + 1], "%-*s ", m_dasm_width, buffer); + + // output the right column + if (m_right_column == DASM_RIGHTCOL_RAW || m_right_column == DASM_RIGHTCOL_ENCRYPTED) + { + // get the bytes + numbytes = memory_address_to_byte(source.m_space, numbytes) & source.m_space->logbytemask; + generate_bytes(pcbyte, numbytes, minbytes, &destbuf[m_divider2], m_allocated.x - m_divider2, m_right_column == DASM_RIGHTCOL_ENCRYPTED); + } + else if (m_right_column == DASM_RIGHTCOL_COMMENTS) + { + // get and add the comment, if present + offs_t comment_address = memory_byte_to_address(source.m_space, m_byteaddress[instr]); + const char *text = debug_comment_get_text(&source.m_device, comment_address, debug_comment_get_opcode_crc32(&source.m_device, comment_address)); + if (text != NULL) + sprintf(&destbuf[m_divider2], "// %.*s", m_allocated.x - m_divider2 - 1, text); + } + + // see if the line changed at all + if (lines == 1 && strncmp(oldbuf, destbuf, MIN(sizeof(oldbuf), m_allocated.x)) != 0) + changed = true; + } + + // update opcode base information + m_last_direct_decrypted = source.m_space->direct.decrypted; + m_last_direct_raw = source.m_space->direct.raw; + m_last_change_count = debug_comment_all_change_count(&m_machine); + + // now longer need to recompute + m_recompute = false; + return changed; +} + + +//------------------------------------------------- +// view_update - update the contents of the +// disassembly view +//------------------------------------------------- + +void debug_view_disasm::view_update() +{ + const debug_view_disasm_source &source = downcast<const debug_view_disasm_source &>(*m_source); + + offs_t pc = cpu_get_pc(&source.m_device); + offs_t pcbyte = memory_address_to_byte(source.m_space, pc) & source.m_space->logbytemask; + + // update our context; if the expression is dirty, recompute + if (m_expression.dirty()) + m_recompute = true; + + // if we're tracking a value, make sure it is visible + if (m_expression.dirty()) + { + UINT64 previous = m_expression.last_value(); + UINT64 result = m_expression.value(); + if (result != previous) + { + offs_t resultbyte = memory_address_to_byte(source.m_space, result) & source.m_space->logbytemask; + + // see if the new result is an address we already have + UINT32 row; + for (row = 0; row < m_allocated.y; row++) + if (m_byteaddress[row] == resultbyte) + break; + + // if we didn't find it, or if it's really close to the bottom, recompute + if (row == m_allocated.y || row >= m_total.y - m_visible.y) + m_recompute = true; + + // otherwise, if it's not visible, adjust the view so it is + else if (row < m_topleft.y || row >= m_topleft.y + m_visible.y - 2) + m_topleft.y = (row > 3) ? row - 3 : 0; + } + } + + // if the opcode base has changed, rework things + if (source.m_space->direct.decrypted != m_last_direct_decrypted || source.m_space->direct.raw != m_last_direct_raw) + m_recompute = true; + + // if the comments have changed, redo it + if (m_last_change_count != debug_comment_all_change_count(&m_machine)) + m_recompute = true; + + // if we need to recompute, do it + bool recomputed_this_time = false; +recompute: + if (m_recompute) + { + // recompute the view + if (m_byteaddress != NULL && m_last_change_count != debug_comment_all_change_count(&m_machine)) + { + // smoosh us against the left column, but not the top row + m_topleft.x = 0; + + // recompute from where we last recomputed! + recompute(memory_byte_to_address(source.m_space, m_byteaddress[0]), 0, m_total.y); + } + else + { + // determine the addresses of what we will display + offs_t backpc = find_pc_backwards((UINT32)m_expression.value(), m_backwards_steps); + + // put ourselves back in the top left + m_topleft.y = 0; + m_topleft.x = 0; + + recompute(backpc, 0, m_total.y); + } + recomputed_this_time = true; + } + + // figure out the row where the PC is and recompute the disassembly + if (pcbyte != m_last_pcbyte) + { + // find the row with the PC on it + for (UINT32 row = 0; row < m_visible.y; row++) + { + UINT32 effrow = m_topleft.y + row; + if (effrow >= m_allocated.y) + break; + if (pcbyte == m_byteaddress[effrow]) + { + // see if we changed + bool changed = recompute(pc, effrow, 1); + if (changed && !recomputed_this_time) + { + m_recompute = true; + goto recompute; + } + + // set the effective row and PC + m_cursor.y = effrow; + view_notify(VIEW_NOTIFY_CURSOR_CHANGED); + } + } + m_last_pcbyte = pcbyte; + } + + // loop over visible rows + debug_view_char *dest = m_viewdata; + for (UINT32 row = 0; row < m_visible.y; row++) + { + UINT32 effrow = m_topleft.y + row; + UINT32 col = 0; + + // if this visible row is valid, add it to the buffer + UINT8 attrib = DCA_NORMAL; + if (effrow < m_allocated.y) + { + // if we're on the line with the PC, recompute and hilight it + if (pcbyte == m_byteaddress[effrow]) + attrib = DCA_CURRENT; + + // if we're on a line with a breakpoint, tag it changed + else + { + const cpu_debug_data *cpuinfo = cpu_get_debug_data(&source.m_device); + for (debug_cpu_breakpoint *bp = cpuinfo->bplist; bp != NULL; bp = bp->next) + if (m_byteaddress[effrow] == (memory_address_to_byte(source.m_space, bp->address) & source.m_space->logbytemask)) + attrib = DCA_CHANGED; + } + + // if we're on the active column and everything is couth, highlight it + if (m_cursor_visible && effrow == m_cursor.y) + attrib |= DCA_SELECTED; + + // get the effective string + const char *data = &m_dasm[effrow * m_allocated.x]; + UINT32 len = (UINT32)strlen(data); + + // copy data + UINT32 effcol = m_topleft.x; + while (col < m_visible.x && effcol < len) + { + dest->byte = data[effcol++]; + dest->attrib = (effcol <= m_divider1 || effcol >= m_divider2) ? (attrib | DCA_ANCILLARY) : attrib; + + // comments are just green for now - maybe they shouldn't even be this? + if (effcol >= m_divider2 && m_right_column == DASM_RIGHTCOL_COMMENTS) + attrib |= DCA_COMMENT; + + dest++; + col++; + } + } + + // fill the rest with blanks + while (col < m_visible.x) + { + dest->byte = ' '; + dest->attrib = (effrow < m_total.y) ? (attrib | DCA_ANCILLARY) : attrib; + dest++; + col++; + } + } +} + + +//------------------------------------------------- +// selected_address - return the PC of the +// currently selected address in the view +//------------------------------------------------- + +offs_t debug_view_disasm::selected_address() +{ + flush_updates(); + return memory_byte_to_address(downcast<const debug_view_disasm_source &>(*m_source).m_space, m_byteaddress[m_cursor.y]); +} + + +//------------------------------------------------- +// set_sexpression - set the expression string +// describing the home address +//------------------------------------------------- + +void debug_view_disasm::set_expression(const char *expression) +{ + begin_update(); + m_expression.set_string(expression); + m_recompute = m_update_pending = true; + end_update(); +} + + +//------------------------------------------------- +// set_right_column - set the contents of the +// right column +//------------------------------------------------- + +void debug_view_disasm::set_right_column(disasm_right_column contents) +{ + begin_update(); + m_right_column = contents; + m_recompute = m_update_pending = true; + end_update(); +} + + +//------------------------------------------------- +// set_backward_steps - set the number of +// instructions displayed before the home address +//------------------------------------------------- + +void debug_view_disasm::set_backward_steps(UINT32 steps) +{ + begin_update(); + m_backwards_steps = steps; + m_recompute = m_update_pending = true; + end_update(); +} + + +//------------------------------------------------- +// set_disasm_width - set the width in characters +// of the main disassembly section +//------------------------------------------------- + +void debug_view_disasm::set_disasm_width(UINT32 width) +{ + begin_update(); + m_dasm_width = width; + m_recompute = m_update_pending = true; + end_update(); +} + + +//------------------------------------------------- +// set_selected_address - set the PC of the +// currently selected address in the view +//------------------------------------------------- + +void debug_view_disasm::set_selected_address(offs_t address) +{ + const debug_view_disasm_source &source = downcast<const debug_view_disasm_source &>(*m_source); + offs_t byteaddress = memory_address_to_byte(source.m_space, address) & source.m_space->logbytemask; + for (int line = 0; line < m_total.y; line++) + if (m_byteaddress[line] == byteaddress) + { + m_cursor.y = line; + set_cursor_position(m_cursor); + break; + } +} diff --git a/src/emu/debug/dvdisasm.h b/src/emu/debug/dvdisasm.h new file mode 100644 index 00000000000..b34c0758e4b --- /dev/null +++ b/src/emu/debug/dvdisasm.h @@ -0,0 +1,146 @@ +/********************************************************************* + + dvdisasm.h + + Disassembly debugger view. + +**************************************************************************** + + 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. + +***************************************************************************/ + +#ifndef __DVDISASM_H__ +#define __DVDISASM_H__ + +#include "debugvw.h" + + +//************************************************************************** +// CONSTANTS +//************************************************************************** + +// selections for what goes into the right-hand column +enum disasm_right_column +{ + DASM_RIGHTCOL_NONE, + DASM_RIGHTCOL_RAW, + DASM_RIGHTCOL_ENCRYPTED, + DASM_RIGHTCOL_COMMENTS +}; + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// a disassembly view_source +class debug_view_disasm_source : public debug_view_source +{ + friend class debug_view_disasm; + + // construction/destruction + debug_view_disasm_source(const char *name, device_t &device); + +public: + // getters + device_t &device() const { return m_device; } + const address_space *space() const { return m_space; } + +private: + // internal state + device_t & m_device; // underlying device + device_disasm_interface *m_disasmintf; // disassembly interface + const address_space *m_space; // address space to display +}; + + +// debug view for disassembly +class debug_view_disasm : public debug_view +{ + friend resource_pool_object<debug_view_disasm>::~resource_pool_object(); + friend class debug_view_manager; + + // construction/destruction + debug_view_disasm(running_machine &machine, debug_view_osd_update_func osdupdate, void *osdprivate); + virtual ~debug_view_disasm(); + +public: + // getters + const char *expression() const { return m_expression.string(); } + disasm_right_column right_column() const { return m_right_column; } + UINT32 backward_steps() const { return m_backwards_steps; } + UINT32 disasm_width() const { return m_dasm_width; } + offs_t selected_address(); + + // setters + void set_expression(const char *expression); + void set_right_column(disasm_right_column contents); + void set_backward_steps(UINT32 steps); + void set_disasm_width(UINT32 width); + void set_selected_address(offs_t address); + +protected: + // view overrides + virtual void view_update(); + virtual void view_notify(debug_view_notification type); + virtual void view_char(int chval); + +private: + // internal helpers + void enumerate_sources(); + offs_t find_pc_backwards(offs_t targetpc, int numinstrs); + void generate_bytes(offs_t pcbyte, int numbytes, int minbytes, char *string, int maxchars, bool encrypted); + bool recompute(offs_t pc, int startline, int lines); + + // internal state + disasm_right_column m_right_column; // right column contents + UINT32 m_backwards_steps; // number of backwards steps + UINT32 m_dasm_width; // width of the disassembly area + UINT8 * m_last_direct_raw; // last direct raw value + UINT8 * m_last_direct_decrypted;// last direct decrypted value + UINT32 m_last_change_count; // last comment change count + offs_t m_last_pcbyte; // last PC byte value + int m_divider1, m_divider2; // left and right divider columns + int m_divider3; // comment divider column + debug_view_expression m_expression; // expression-related information + debug_view_xy m_allocated; // allocated rows/columns + offs_t * m_byteaddress; // addresses of the instructions + char * m_dasm; // disassembled instructions + + // constants + static const int DEFAULT_DASM_LINES = 1000; + static const int DEFAULT_DASM_WIDTH = 50; + static const int DASM_MAX_BYTES = 16; +}; + + +#endif diff --git a/src/emu/debug/dvmemory.c b/src/emu/debug/dvmemory.c new file mode 100644 index 00000000000..8344c721882 --- /dev/null +++ b/src/emu/debug/dvmemory.c @@ -0,0 +1,813 @@ +/********************************************************************* + + dvmemory.c + + Memory debugger view. + +**************************************************************************** + + 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. + +***************************************************************************/ + +#include "emu.h" +#include "debugvw.h" +#include "dvmemory.h" +#include "debugcpu.h" +#include <ctype.h> + + + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +const debug_view_memory::memory_view_pos debug_view_memory::s_memory_pos_table[9] = +{ + /* 0 bytes per chunk: */ { 0, { 0 } }, + /* 1 byte per chunk: 00 11 22 33 44 55 66 77 */ { 3, { 0x04, 0x00, 0x80 } }, + /* 2 bytes per chunk: 0011 2233 4455 6677 */ { 6, { 0x8c, 0x0c, 0x08, 0x04, 0x00, 0x80 } }, + /* 3 bytes per chunk: */ { 0, { 0 } }, + /* 4 bytes per chunk: 00112233 44556677 */ { 12, { 0x9c, 0x9c, 0x1c, 0x18, 0x14, 0x10, 0x0c, 0x08, 0x04, 0x00, 0x80, 0x80 } }, + /* 5 bytes per chunk: */ { 0, { 0 } }, + /* 6 bytes per chunk: */ { 0, { 0 } }, + /* 7 bytes per chunk: */ { 0, { 0 } }, + /* 8 bytes per chunk: 0011223344556677 */ { 24, { 0xbc, 0xbc, 0xbc, 0xbc, 0x3c, 0x38, 0x34, 0x30, 0x2c, 0x28, 0x24, 0x20, 0x1c, 0x18, 0x14, 0x10, 0x0c, 0x08, 0x04, 0x00, 0x80, 0x80, 0x80, 0x80 } } +}; + + + +//************************************************************************** +// DEBUG VIEW MEMORY SOURCE +//************************************************************************** + +//------------------------------------------------- +// debug_view_memory_source - constructors +//------------------------------------------------- + +debug_view_memory_source::debug_view_memory_source(const char *name, const address_space &space) + : debug_view_source(name, space.cpu), + m_space(&space), + m_memintf(dynamic_cast<device_memory_interface *>(space.cpu)), + m_base(NULL), + m_length(0), + m_offsetxor(0), + m_endianness(space.endianness), + m_prefsize(space.dbits / 8) +{ +} + +debug_view_memory_source::debug_view_memory_source(const char *name, const region_info ®ion) + : debug_view_source(name), + m_space(NULL), + m_memintf(NULL), + m_base(region), + m_length(region.bytes()), + m_offsetxor(NATIVE_ENDIAN_VALUE_LE_BE(region.width() - 1, 0)), + m_endianness(region.endianness()), + m_prefsize(MIN(region.width(), 8)) +{ +} + +debug_view_memory_source::debug_view_memory_source(const char *name, void *base, int element_size, int num_elements) + : debug_view_source(name), + m_space(NULL), + m_memintf(NULL), + m_base(base), + m_length(element_size * num_elements), + m_offsetxor(0), + m_endianness(ENDIANNESS_NATIVE), + m_prefsize(MIN(element_size, 8)) +{ +} + + + +//************************************************************************** +// DEBUG VIEW MEMORY +//************************************************************************** + +//------------------------------------------------- +// debug_view_memory - constructor +//------------------------------------------------- + +debug_view_memory::debug_view_memory(running_machine &machine, debug_view_osd_update_func osdupdate, void *osdprivate) + : debug_view(machine, DVT_MEMORY, osdupdate, osdprivate), + m_expression(machine), + m_chunks_per_row(16), + m_bytes_per_chunk(1), + m_reverse_view(false), + m_ascii_view(true), + m_no_translation(false), + m_maxaddr(0), + m_bytes_per_row(16), + m_byte_offset(0) +{ + // fail if no available sources + enumerate_sources(); + if (m_source_list.count() == 0) + throw std::bad_alloc(); + + // configure the view + m_supports_cursor = true; +} + + +//------------------------------------------------- +// enumerate_sources - enumerate all possible +// sources for a memory view +//------------------------------------------------- + +void debug_view_memory::enumerate_sources() +{ + // start with an empty list + m_source_list.reset(); + astring name; + + // first add all the devices' address spaces + device_memory_interface *memintf = NULL; + for (bool gotone = m_machine.devicelist.first(memintf); gotone; gotone = memintf->next(memintf)) + for (int spacenum = 0; spacenum < ADDRESS_SPACES; spacenum++) + { + const address_space *space = memintf->space(spacenum); + if (space != NULL) + { + name.printf("%s '%s' %s space memory", memintf->device().name(), memintf->device().tag(), space->name); + m_source_list.append(*auto_alloc(&m_machine, debug_view_memory_source(name, *space))); + } + } + + // then add all the memory regions + for (const region_info *region = m_machine.regionlist.first(); region != NULL; region = region->next()) + { + name.printf("Region '%s'", region->name.cstr()); + m_source_list.append(*auto_alloc(&m_machine, debug_view_memory_source(name, *region))); + } + + // finally add all global array symbols + for (int itemnum = 0; itemnum < 10000; itemnum++) + { + // stop when we run out of items + UINT32 valsize, valcount; + void *base; + const char *itemname = state_save_get_indexed_item(&m_machine, itemnum, &base, &valsize, &valcount); + if (itemname == NULL) + break; + + // if this is a single-entry global, add it + if (valcount > 1 && strstr(itemname, "globals/")) + { + name.cpy(strrchr(itemname, '/') + 1); + m_source_list.append(*auto_alloc(&m_machine, debug_view_memory_source(name, base, valsize, valcount))); + } + } + m_source = m_source_list.head(); +} + + +//------------------------------------------------- +// view_notify - handle notification of updates +// to cursor changes +//------------------------------------------------- + +void debug_view_memory::view_notify(debug_view_notification type) +{ + if (type == VIEW_NOTIFY_CURSOR_CHANGED) + { + // normalize the cursor + set_cursor_pos(get_cursor_pos()); + } + else if (type == VIEW_NOTIFY_SOURCE_CHANGED) + { + // update for the new source + const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source); + m_chunks_per_row = m_bytes_per_chunk * m_chunks_per_row / source.m_prefsize; + m_bytes_per_chunk = source.m_prefsize; + if (source.m_space != NULL) + m_expression.set_context(debug_cpu_get_symtable(source.m_space->cpu)); + else + m_expression.set_context(NULL); + } +} + + +//------------------------------------------------- +// view_update - update the contents of the +// memory view +//------------------------------------------------- + +void debug_view_memory::view_update() +{ + const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source); + + // if we need to recompute, do it now + if (needs_recompute()) + recompute(); + + // get positional data + const memory_view_pos &posdata = s_memory_pos_table[m_bytes_per_chunk]; + + // loop over visible rows + for (UINT32 row = 0; row < m_visible.y; row++) + { + debug_view_char *destmin = m_viewdata + row * m_visible.x; + debug_view_char *destmax = destmin + m_visible.x; + debug_view_char *destrow = destmin - m_topleft.x; + UINT32 effrow = m_topleft.y + row; + + // reset the line of data; section 1 is normal, others are ancillary, cursor is selected + debug_view_char *dest = destmin; + for (int ch = 0; ch < m_visible.x; ch++, dest++) + { + UINT32 effcol = m_topleft.x + ch; + dest->byte = ' '; + dest->attrib = DCA_ANCILLARY; + if (m_section[1].contains(effcol)) + { + dest->attrib = DCA_NORMAL; + if (m_cursor_visible && effrow == m_cursor.y && effcol == m_cursor.x) + dest->attrib |= DCA_SELECTED; + } + } + + // if this visible row is valid, add it to the buffer + if (effrow < m_total.y) + { + offs_t addrbyte = m_byte_offset + effrow * m_bytes_per_row; + offs_t address = (source.m_space != NULL) ? memory_byte_to_address(source.m_space, addrbyte) : addrbyte; + char addrtext[20]; + + // generate the address + sprintf(addrtext, m_addrformat, address); + dest = destrow + m_section[0].m_pos + 1; + for (int ch = 0; addrtext[ch] != 0 && ch < m_section[0].m_width - 1; ch++, dest++) + if (dest >= destmin && dest < destmax) + dest->byte = addrtext[ch]; + + // generate the data + for (int chunknum = 0; chunknum < m_chunks_per_row; chunknum++) + { + int chunkindex = m_reverse_view ? (m_chunks_per_row - 1 - chunknum) : chunknum; + + UINT64 chunkdata; + bool ismapped = read(m_bytes_per_chunk, addrbyte + chunknum * m_bytes_per_chunk, chunkdata); + dest = destrow + m_section[1].m_pos + 1 + chunkindex * posdata.m_spacing; + for (int ch = 0; ch < posdata.m_spacing; ch++, dest++) + if (dest >= destmin && dest < destmax) + { + UINT8 shift = posdata.m_shift[ch]; + if (shift < 64) + dest->byte = ismapped ? "0123456789ABCDEF"[(chunkdata >> shift) & 0x0f] : '*'; + } + } + + // generate the ASCII data + if (m_section[2].m_width > 0) + { + dest = destrow + m_section[2].m_pos + 1; + for (int ch = 0; ch < m_bytes_per_row; ch++, dest++) + if (dest >= destmin && dest < destmax) + { + UINT64 chval; + bool ismapped = read(1, addrbyte + ch, chval); + dest->byte = (ismapped && isprint(chval)) ? chval : '.'; + } + } + } + } +} + + +//------------------------------------------------- +// view_char - handle a character typed within +// the current view +//------------------------------------------------- + +void debug_view_memory::view_char(int chval) +{ + // get the position + cursor_pos pos = get_cursor_pos(); + + // handle the incoming key + switch (chval) + { + case DCH_UP: + if (pos.m_address >= m_byte_offset + m_bytes_per_row) + pos.m_address -= m_bytes_per_row; + break; + + case DCH_DOWN: + if (pos.m_address <= m_maxaddr - m_bytes_per_row) + pos.m_address += m_bytes_per_row; + break; + + case DCH_PUP: + for (UINT32 delta = (m_visible.y - 2) * m_bytes_per_row; delta > 0; delta -= m_bytes_per_row) + if (pos.m_address >= m_byte_offset + delta) + { + pos.m_address -= delta; + break; + } + break; + + case DCH_PDOWN: + for (UINT32 delta = (m_visible.y - 2) * m_bytes_per_row; delta > 0; delta -= m_bytes_per_row) + if (pos.m_address <= m_maxaddr - delta) + { + pos.m_address += delta; + break; + } + break; + + case DCH_HOME: + pos.m_address -= pos.m_address % m_bytes_per_row; + pos.m_shift = (m_bytes_per_chunk * 8) - 4; + break; + + case DCH_CTRLHOME: + pos.m_address = m_byte_offset; + pos.m_shift = (m_bytes_per_chunk * 8) - 4; + break; + + case DCH_END: + pos.m_address += (m_bytes_per_row - (pos.m_address % m_bytes_per_row) - 1); + pos.m_shift = 0; + break; + + case DCH_CTRLEND: + pos.m_address = m_maxaddr; + pos.m_shift = 0; + break; + + case DCH_CTRLLEFT: + if (pos.m_address >= m_byte_offset + m_bytes_per_chunk) + pos.m_address -= m_bytes_per_chunk; + break; + + case DCH_CTRLRIGHT: + if (pos.m_address <= m_maxaddr - m_bytes_per_chunk) + pos.m_address += m_bytes_per_chunk; + break; + + default: + { + static const char hexvals[] = "0123456789abcdef"; + char *hexchar = (char *)strchr(hexvals, tolower(chval)); + if (hexchar == NULL) + break; + + UINT64 data; + bool ismapped = read(m_bytes_per_chunk, pos.m_address, data); + if (!ismapped) + break; + + data &= ~((UINT64)0x0f << pos.m_shift); + data |= (UINT64)(hexchar - hexvals) << pos.m_shift; + write(m_bytes_per_chunk, pos.m_address, data); + // fall through... + } + + case DCH_RIGHT: + if (pos.m_shift == 0 && pos.m_address != m_maxaddr) + { + pos.m_shift = m_bytes_per_chunk * 8 - 4; + pos.m_address += m_bytes_per_chunk; + } + else + pos.m_shift -= 4; + break; + + case DCH_LEFT: + if (pos.m_shift == m_bytes_per_chunk * 8 - 4 && pos.m_address != m_byte_offset) + { + pos.m_shift = 0; + pos.m_address -= m_bytes_per_chunk; + } + else + pos.m_shift += 4; + break; + } + + // set a new position + begin_update(); + set_cursor_pos(pos); + m_update_pending = true; + end_update(); +} + + +//------------------------------------------------- +// recompute - recompute the internal data and +// structure of the memory view +//------------------------------------------------- + +void debug_view_memory::recompute() +{ + const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source); + + // get the current cursor position + cursor_pos pos = get_cursor_pos(); + + // determine the maximum address and address format string from the raw information + int addrchars; + if (source.m_space != NULL) + { + m_maxaddr = m_no_translation ? source.m_space->bytemask : source.m_space->logbytemask; + addrchars = m_no_translation ? source.m_space->addrchars : source.m_space->logaddrchars; + } + else + { + m_maxaddr = source.m_length - 1; + addrchars = m_addrformat.printf("%X", m_maxaddr); + } + + // generate an 8-byte aligned format for the address + if (!m_reverse_view) + m_addrformat.printf("%*s%%0%dX", 8 - addrchars, "", addrchars); + else + m_addrformat.printf("%%0%dX%*s", addrchars, 8 - addrchars, ""); + + // if we are viewing a space with a minimum chunk size, clamp the bytes per chunk + if (source.m_space != NULL && source.m_space->ashift < 0) + { + UINT32 min_bytes_per_chunk = 1 << -source.m_space->ashift; + while (m_bytes_per_chunk < min_bytes_per_chunk) + { + m_bytes_per_chunk *= 2; + m_chunks_per_row /= 2; + } + m_chunks_per_row = MAX(1, m_chunks_per_row); + } + + // recompute the byte offset based on the most recent expression result + m_bytes_per_row = m_bytes_per_chunk * m_chunks_per_row; + m_byte_offset = m_expression.value() % m_bytes_per_row; + + // compute the section widths + m_section[0].m_width = 1 + 8 + 1; + m_section[1].m_width = 1 + 3 * m_bytes_per_row + 1; + m_section[2].m_width = m_ascii_view ? (1 + m_bytes_per_row + 1) : 0; + + // compute the section positions + if (!m_reverse_view) + { + m_section[0].m_pos = 0; + m_section[1].m_pos = m_section[0].m_pos + m_section[0].m_width; + m_section[2].m_pos = m_section[1].m_pos + m_section[1].m_width; + m_total.x = m_section[2].m_pos + m_section[2].m_width; + } + else + { + m_section[2].m_pos = 0; + m_section[1].m_pos = m_section[2].m_pos + m_section[2].m_width; + m_section[0].m_pos = m_section[1].m_pos + m_section[1].m_width; + m_total.x = m_section[0].m_pos + m_section[0].m_width; + } + + // derive total sizes from that + m_total.y = ((UINT64)m_maxaddr - (UINT64)m_byte_offset + (UINT64)m_bytes_per_row - 1) / m_bytes_per_row; + + // reset the current cursor position + set_cursor_pos(pos); +} + + +//------------------------------------------------- +// needs_recompute - determine if anything has +// changed that requires a recomputation +//------------------------------------------------- + +bool debug_view_memory::needs_recompute() +{ + bool recompute = m_recompute; + + // handle expression changes + if (m_expression.dirty()) + { + recompute = true; + m_topleft.y = (m_expression.value() - m_byte_offset) / m_bytes_per_row; + m_topleft.y = MAX(m_topleft.y, 0); + m_topleft.y = MIN(m_topleft.y, m_total.y - 1); + + const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source); + offs_t resultbyte; + if (source.m_space != NULL) + resultbyte = memory_address_to_byte(source.m_space, m_expression.value()) & source.m_space->logbytemask; + else + resultbyte = m_expression.value(); + + set_cursor_pos(cursor_pos(resultbyte, m_bytes_per_chunk * 8 - 4)); + } + + // expression is clean at this point, and future recomputation is not necessary + m_recompute = false; + return recompute; +} + + +//------------------------------------------------- +// get_cursor_pos - return the cursor position as +// an address and a shift value +//------------------------------------------------- + +debug_view_memory::cursor_pos debug_view_memory::get_cursor_pos() +{ + // start with the base address for this row + cursor_pos pos; + pos.m_address = m_byte_offset + m_cursor.y * m_bytes_per_chunk * m_chunks_per_row; + + // determine the X position within the middle section, clamping as necessary + const memory_view_pos &posdata = s_memory_pos_table[m_bytes_per_chunk]; + int xposition = m_cursor.x - m_section[1].m_pos - 1; + if (xposition < 0) + xposition = 0; + else if (xposition >= posdata.m_spacing * m_chunks_per_row) + xposition = posdata.m_spacing * m_chunks_per_row - 1; + + // compute chunk number and offset within that chunk + int chunknum = xposition / posdata.m_spacing; + int chunkoffs = xposition % posdata.m_spacing; + + // reverse the chunknum if we're reversed + if (m_reverse_view) + chunknum = m_chunks_per_row - 1 - chunknum; + + // compute the address and shift + pos.m_address += chunknum * m_bytes_per_chunk; + pos.m_shift = posdata.m_shift[chunkoffs] & 0x7f; + return pos; +} + + +//------------------------------------------------- +// set_cursor_pos - set the cursor position as a +// function of an address and a shift value +//------------------------------------------------- + +void debug_view_memory::set_cursor_pos(cursor_pos pos) +{ + const memory_view_pos &posdata = s_memory_pos_table[m_bytes_per_chunk]; + + // offset the address by the byte offset + if (pos.m_address < m_byte_offset) + pos.m_address = m_byte_offset; + pos.m_address -= m_byte_offset; + + // compute the Y coordinate and chunk index + m_cursor.y = pos.m_address / m_bytes_per_row; + int chunknum = (pos.m_address % m_bytes_per_row) / m_bytes_per_chunk; + + // reverse the chunknum if we're reversed + if (m_reverse_view) + chunknum = m_chunks_per_row - 1 - chunknum; + + // scan within the chunk to find the shift + for (m_cursor.x = 0; m_cursor.x < posdata.m_spacing; m_cursor.x++) + if (posdata.m_shift[m_cursor.x] == pos.m_shift) + break; + + // add in the chunk offset and shift to the right of divider1 + m_cursor.x += m_section[1].m_pos + 1 + posdata.m_spacing * chunknum; + + // clamp to the window bounds + m_cursor.x = MIN(m_cursor.x, m_total.x); + m_cursor.y = MIN(m_cursor.y, m_total.y); + + // scroll if out of range + adjust_visible_x_for_cursor(); + adjust_visible_y_for_cursor(); +} + + +//------------------------------------------------- +// read - generic memory view data reader +//------------------------------------------------- + +bool debug_view_memory::read(UINT8 size, offs_t offs, UINT64 &data) +{ + const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source); + + // if no raw data, just use the standard debug routines + if (source.m_space != NULL) + { + offs_t dummyaddr = offs; + + bool ismapped = m_no_translation ? true : source.m_memintf->translate(source.m_space->spacenum, TRANSLATE_READ_DEBUG, dummyaddr); + data = ~(UINT64)0; + if (ismapped) + { + switch (size) + { + case 1: data = debug_read_byte(source.m_space, offs, !m_no_translation); break; + case 2: data = debug_read_word(source.m_space, offs, !m_no_translation); break; + case 4: data = debug_read_dword(source.m_space, offs, !m_no_translation); break; + case 8: data = debug_read_qword(source.m_space, offs, !m_no_translation); break; + } + } + return ismapped; + } + + // if larger than a byte, reduce by half and recurse + if (size > 1) + { + size /= 2; + + UINT64 data0, data1; + bool ismapped = read(size, offs + 0 * size, data0); + ismapped |= read(size, offs + 1 * size, data1); + + if (source.m_endianness == ENDIANNESS_LITTLE) + data = data0 | (data1 << (size * 8)); + else + data = data1 | (data0 << (size * 8)); + return ismapped; + } + + // all 0xff if out of bounds + offs ^= source.m_offsetxor; + if (offs >= source.m_length) + return false; + data = *((UINT8 *)source.m_base + offs); + return true; +} + + +//------------------------------------------------- +// write - generic memory view data writer +//------------------------------------------------- + +void debug_view_memory::write(UINT8 size, offs_t offs, UINT64 data) +{ + const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source); + + // if no raw data, just use the standard debug routines + if (source.m_space != NULL) + { + switch (size) + { + case 1: debug_write_byte(source.m_space, offs, data, !m_no_translation); break; + case 2: debug_write_word(source.m_space, offs, data, !m_no_translation); break; + case 4: debug_write_dword(source.m_space, offs, data, !m_no_translation); break; + case 8: debug_write_qword(source.m_space, offs, data, !m_no_translation); break; + } + return; + } + + // if larger than a byte, reduce by half and recurse + if (size > 1) + { + size /= 2; + if (source.m_endianness == ENDIANNESS_LITTLE) + { + write(size, offs + 0 * size, data); + write(size, offs + 1 * size, data >> (8 * size)); + } + else + { + write(size, offs + 1 * size, data); + write(size, offs + 0 * size, data >> (8 * size)); + } + return; + } + + // ignore if out of bounds + offs ^= source.m_offsetxor; + if (offs >= source.m_length) + return; + *((UINT8 *)source.m_base + offs) = data; + +// hack for FD1094 editing +#ifdef FD1094_HACK + if (source.m_base == *m_machine.region("user2")) + { + extern void fd1094_regenerate_key(running_machine *machine); + fd1094_regenerate_key(&m_machine); + } +#endif +} + + +//------------------------------------------------- +// set_expression - set the expression string +// describing the home address +//------------------------------------------------- + +void debug_view_memory::set_expression(const char *expression) +{ + begin_update(); + m_expression.set_string(expression); + m_recompute = m_update_pending = true; + end_update(); +} + + +//------------------------------------------------- +// set_bytes_per_chunk - specify the number of +// bytes displayed per chunk +//------------------------------------------------- + +void debug_view_memory::set_bytes_per_chunk(UINT8 chunkbytes) +{ + const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source); + + cursor_pos pos = begin_update_and_get_cursor_pos(); + pos.m_address += (pos.m_shift / 8) ^ ((source.m_endianness == ENDIANNESS_LITTLE) ? 0 : (m_bytes_per_chunk - 1)); + pos.m_shift %= 8; + + m_bytes_per_chunk = chunkbytes; + m_chunks_per_row = m_bytes_per_row / chunkbytes; + m_recompute = m_update_pending = true; + + pos.m_shift += 8 * ((pos.m_address % m_bytes_per_chunk) ^ ((source.m_endianness == ENDIANNESS_LITTLE) ? 0 : (m_bytes_per_chunk - 1))); + pos.m_address -= pos.m_address % m_bytes_per_chunk; + + m_recompute = m_update_pending = true; + end_update_and_set_cursor_pos(pos); +} + + +//------------------------------------------------- +// set_chunks_per_row - specify the number of +// chunks displayed across a row +//------------------------------------------------- + +void debug_view_memory::set_chunks_per_row(UINT32 rowchunks) +{ + if (rowchunks < 1) + return; + + cursor_pos pos = begin_update_and_get_cursor_pos(); + m_chunks_per_row = rowchunks; + m_recompute = m_update_pending = true; + end_update_and_set_cursor_pos(pos); +} + + +//------------------------------------------------- +// set_reverse - specify true if the memory view +// is displayed reverse +//------------------------------------------------- + +void debug_view_memory::set_reverse(bool reverse) +{ + cursor_pos pos = begin_update_and_get_cursor_pos(); + m_reverse_view = reverse; + m_recompute = m_update_pending = true; + end_update_and_set_cursor_pos(pos); +} + + +//------------------------------------------------- +// set_ascii - specify TRUE if the memory view +// should display an ASCII representation +//------------------------------------------------- + +void debug_view_memory::set_ascii(bool ascii) +{ + cursor_pos pos = begin_update_and_get_cursor_pos(); + m_ascii_view = ascii; + m_recompute = m_update_pending = true; + end_update_and_set_cursor_pos(pos); +} + + +//------------------------------------------------- +// set_physical - specify true if the memory view +// should display physical addresses versus +// logical addresses +//------------------------------------------------- + +void debug_view_memory::set_physical(bool physical) +{ + cursor_pos pos = begin_update_and_get_cursor_pos(); + m_no_translation = physical; + m_recompute = m_update_pending = true; + end_update_and_set_cursor_pos(pos); +} diff --git a/src/emu/debug/dvmemory.h b/src/emu/debug/dvmemory.h new file mode 100644 index 00000000000..ee1bfa7080d --- /dev/null +++ b/src/emu/debug/dvmemory.h @@ -0,0 +1,160 @@ +/********************************************************************* + + dvmemory.h + + Memory debugger view. + +**************************************************************************** + + 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. + +***************************************************************************/ + +#ifndef __DVMEMORY_H__ +#define __DVMEMORY_H__ + +#include "dvstate.h" + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// a memory view_source +class debug_view_memory_source : public debug_view_source +{ + friend class debug_view_memory; + + debug_view_memory_source(const char *name, const address_space &space); + debug_view_memory_source(const char *name, const region_info ®ion); + debug_view_memory_source(const char *name, void *base, int element_size, int num_elements); + +public: + const address_space *space() const { return m_space; } + +private: + const address_space *m_space; // address space we reference (if any) + device_memory_interface *m_memintf; // pointer to the memory interface of the device + void * m_base; // pointer to memory base + offs_t m_length; // length of memory + offs_t m_offsetxor; // XOR to apply to offsets + endianness_t m_endianness; // endianness of memory + UINT8 m_prefsize; // preferred bytes per chunk +}; + + +// debug view for memory +class debug_view_memory : public debug_view +{ + friend resource_pool_object<debug_view_memory>::~resource_pool_object(); + friend class debug_view_manager; + + // construction/destruction + debug_view_memory(running_machine &machine, debug_view_osd_update_func osdupdate, void *osdprivate); + +public: + // getters + const char *expression() { return m_expression.string(); } + UINT8 bytes_per_chunk() { flush_updates(); return m_bytes_per_chunk; } + UINT8 chunks_per_row() { flush_updates(); return m_chunks_per_row; } + bool reverse() const { return m_reverse_view; } + bool ascii() const { return m_ascii_view; } + bool physical() const { return m_no_translation; } + + // setters + void set_expression(const char *expression); + void set_bytes_per_chunk(UINT8 chunkbytes); + void set_chunks_per_row(UINT32 rowchunks); + void set_reverse(bool reverse); + void set_ascii(bool reverse); + void set_physical(bool physical); + +protected: + // view overrides + virtual void view_notify(debug_view_notification type); + virtual void view_update(); + virtual void view_char(int chval); + +private: + struct cursor_pos + { + cursor_pos(offs_t address = 0, UINT8 shift = 0) : m_address(address), m_shift(shift) { } + offs_t m_address; + UINT8 m_shift; + }; + + // internal helpers + void enumerate_sources(); + void recompute(); + bool needs_recompute(); + + // cursor position management + cursor_pos get_cursor_pos(); + void set_cursor_pos(cursor_pos pos); + cursor_pos begin_update_and_get_cursor_pos() { begin_update(); return get_cursor_pos(); } + void end_update_and_set_cursor_pos(cursor_pos pos) { set_cursor_pos(pos); end_update(); } + + // memory access + bool read(UINT8 size, offs_t offs, UINT64 &data); + void write(UINT8 size, offs_t offs, UINT64 data); + + // internal state + debug_view_expression m_expression; // expression describing the start address + UINT32 m_chunks_per_row; // number of chunks displayed per line + UINT8 m_bytes_per_chunk; // bytes per chunk + bool m_reverse_view; // reverse-endian view? + bool m_ascii_view; // display ASCII characters? + bool m_no_translation; // don't run addresses through the cpu translation hook + offs_t m_maxaddr; // (derived) maximum address to display + UINT32 m_bytes_per_row; // (derived) number of bytes displayed per line + UINT32 m_byte_offset; // (derived) offset of starting visible byte + astring m_addrformat; // (derived) format string to use to print addresses + + struct section + { + bool contains(int x) const { return x >= m_pos && x < m_pos + m_width; } + INT32 m_pos; /* starting position */ + INT32 m_width; /* width of this section */ + }; + section m_section[3]; // (derived) 3 sections to manage + + struct memory_view_pos + { + UINT8 m_spacing; /* spacing between each entry */ + UINT8 m_shift[24]; /* shift for each character */ + }; + static const memory_view_pos s_memory_pos_table[9]; // table for rendering at different chunk sizes + + // constants + static const int MEM_MAX_LINE_WIDTH = 1024; +}; + + +#endif diff --git a/src/emu/debug/dvstate.c b/src/emu/debug/dvstate.c new file mode 100644 index 00000000000..332683b23bf --- /dev/null +++ b/src/emu/debug/dvstate.c @@ -0,0 +1,370 @@ +/********************************************************************* + + dvstate.c + + State debugger view. + +**************************************************************************** + + 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. + +***************************************************************************/ + +#include "emu.h" +#include "debugvw.h" +#include "dvstate.h" + + + +//************************************************************************** +// DEBUG VIEW STATE SOURCE +//************************************************************************** + +//------------------------------------------------- +// debug_view_state_source - constructor +//------------------------------------------------- + +debug_view_state_source::debug_view_state_source(const char *name, device_t &device) + : debug_view_source(name, &device), + m_device(device), + m_stateintf(dynamic_cast<device_state_interface *>(&device)), + m_execintf(dynamic_cast<device_execute_interface *>(&device)) +{ +} + + + +//************************************************************************** +// DEBUG VIEW STATE +//************************************************************************** + +//------------------------------------------------- +// debug_view_state - constructor +//------------------------------------------------- + +debug_view_state::debug_view_state(running_machine &machine, debug_view_osd_update_func osdupdate, void *osdprivate) + : debug_view(machine, DVT_STATE, osdupdate, osdprivate), + m_divider(0), + m_last_update(0), + m_state_list(NULL) +{ + // fail if no available sources + enumerate_sources(); + if (m_source_list.count() == 0) + throw std::bad_alloc(); +} + + +//------------------------------------------------- +// ~debug_view_state - destructor +//------------------------------------------------- + +debug_view_state::~debug_view_state() +{ + reset(); +} + + +//------------------------------------------------- +// enumerate_sources - enumerate all possible +// sources for a registers view +//------------------------------------------------- + +void debug_view_state::enumerate_sources() +{ + // start with an empty list + m_source_list.reset(); + + // iterate over devices that have state interfaces + device_state_interface *state = NULL; + astring name; + for (bool gotone = m_machine.devicelist.first(state); gotone; gotone = state->next(state)) + { + name.printf("%s '%s'", state->device().name(), state->device().tag()); + m_source_list.append(*auto_alloc(&m_machine, debug_view_state_source(name, state->device()))); + } + + // reset the source to a known good entry + m_source = m_source_list.head(); +} + + +//------------------------------------------------- +// reset - delete all of our state items +//------------------------------------------------- + +void debug_view_state::reset() +{ + // free all items in the state list + while (m_state_list != NULL) + { + state_item *oldhead = m_state_list; + m_state_list = oldhead->m_next; + auto_free(&m_machine, oldhead); + } +} + + +//------------------------------------------------- +// recompute - recompute all info for the +// registers view +//------------------------------------------------- + +void debug_view_state::recompute() +{ + const debug_view_state_source &source = downcast<const debug_view_state_source &>(*m_source); + + // start with a blank list + reset(); + + // add a cycles entry: cycles:99999999 + state_item **tailptr = &m_state_list; + *tailptr = auto_alloc(&m_machine, state_item(REG_CYCLES, "cycles", 8)); + tailptr = &(*tailptr)->m_next; + + // add a beam entry: beamx:123 + *tailptr = auto_alloc(&m_machine, state_item(REG_BEAMX, "beamx", 3)); + tailptr = &(*tailptr)->m_next; + + // add a beam entry: beamy:456 + *tailptr = auto_alloc(&m_machine, state_item(REG_BEAMY, "beamy", 3)); + tailptr = &(*tailptr)->m_next; + + // add a beam entry: frame:123456 + *tailptr = auto_alloc(&m_machine, state_item(REG_FRAME, "frame", 6)); + tailptr = &(*tailptr)->m_next; + + // add a flags entry: flags:xxxxxxxx + *tailptr = auto_alloc(&m_machine, state_item(STATE_GENFLAGS, "flags", source.m_stateintf->state_string_max_length(STATE_GENFLAGS))); + tailptr = &(*tailptr)->m_next; + + // add a divider entry + *tailptr = auto_alloc(&m_machine, state_item(REG_DIVIDER, "", 0)); + tailptr = &(*tailptr)->m_next; + + // add all registers into it + for (const device_state_entry *entry = source.m_stateintf->state_first(); entry != NULL; entry = entry->next()) + if (entry->visible()) + { + *tailptr = auto_alloc(&m_machine, state_item(entry->index(), entry->symbol(), source.m_stateintf->state_string_max_length(entry->index()))); + tailptr = &(*tailptr)->m_next; + } + + // count the entries and determine the maximum tag and value sizes + int count = 0; + int maxtaglen = 0; + int maxvallen = 0; + for (state_item *item = m_state_list; item != NULL; item = item->m_next) + { + count++; + maxtaglen = MAX(maxtaglen, item->m_symbol.len()); + maxvallen = MAX(maxvallen, item->m_vallen); + } + + // set the current divider and total cols + m_divider = 1 + maxtaglen + 1; + m_total.x = 1 + maxtaglen + 2 + maxvallen + 1; + m_total.y = count; + m_topleft.x = 0; + m_topleft.y = 0; + + // no longer need to recompute + m_recompute = false; +} + + +//------------------------------------------------- +// view_notify - handle notification of updates +// to cursor changes +//------------------------------------------------- + +void debug_view_state::view_notify(debug_view_notification type) +{ + if (type == VIEW_NOTIFY_SOURCE_CHANGED) + m_recompute = true; +} + + +//------------------------------------------------- +// view_update - update the contents of the +// register view +//------------------------------------------------- + +void debug_view_state::view_update() +{ + // if our assumptions changed, revisit them + if (m_recompute) + recompute(); + + // get cycle count if we have an execute interface + const debug_view_state_source &source = downcast<const debug_view_state_source &>(*m_source); + UINT64 total_cycles = 0; + if (source.m_execintf != NULL) + total_cycles = source.m_execintf->total_cycles(); + + // find the first entry + state_item *curitem = m_state_list; + for (int index = 0; curitem != NULL && index < m_topleft.y; index++) + curitem = curitem->m_next; + + // loop over visible rows + screen_device *screen = m_machine.primary_screen; + debug_view_char *dest = m_viewdata; + for (UINT32 row = 0; row < m_visible.y; row++) + { + UINT32 col = 0; + + // if this visible row is valid, add it to the buffer + if (curitem != NULL) + { + UINT32 effcol = m_topleft.x; + UINT8 attrib = DCA_NORMAL; + UINT32 len = 0; + astring valstr; + + // get the effective string + if (curitem->m_index >= REG_FRAME && curitem->m_index <= REG_DIVIDER) + { + curitem->m_lastval = curitem->m_currval; + switch (curitem->m_index) + { + case REG_DIVIDER: + curitem->m_vallen = 0; + curitem->m_symbol.reset(); + for (int i = 0; i < m_total.x; i++) + curitem->m_symbol.cat("-"); + break; + + case REG_CYCLES: + if (source.m_execintf != NULL) + { + curitem->m_currval = source.m_execintf->cycles_remaining(); + valstr.printf("%-8d", (UINT32)curitem->m_currval); + } + break; + + case REG_BEAMX: + if (screen != NULL) + { + curitem->m_currval = screen->hpos(); + valstr.printf("%3d", (UINT32)curitem->m_currval); + } + break; + + case REG_BEAMY: + if (screen != NULL) + { + curitem->m_currval = screen->vpos(); + valstr.printf("%3d", (UINT32)curitem->m_currval); + } + break; + + case REG_FRAME: + if (screen != NULL) + { + curitem->m_currval = screen->frame_number(); + valstr.printf("%3d", (UINT32)curitem->m_currval); + } + break; + } + } + else + { + if (m_last_update != total_cycles) + curitem->m_lastval = curitem->m_currval; + curitem->m_currval = source.m_stateintf->state_value(curitem->m_index); + source.m_stateintf->state_string(curitem->m_index, valstr); + } + + // see if we changed + if (curitem->m_lastval != curitem->m_currval) + attrib = DCA_CHANGED; + + // build up a string + char temp[256]; + if (curitem->m_symbol.len() < m_divider - 1) + { + memset(&temp[len], ' ', m_divider - 1 - curitem->m_symbol.len()); + len += m_divider - 1 - curitem->m_symbol.len(); + } + + memcpy(&temp[len], curitem->m_symbol.cstr(), curitem->m_symbol.len()); + len += curitem->m_symbol.len(); + + temp[len++] = ' '; + temp[len++] = ' '; + + memcpy(&temp[len], valstr.cstr(), curitem->m_vallen); + len += curitem->m_vallen; + + temp[len++] = ' '; + temp[len] = 0; + + // copy data + while (col < m_visible.x && effcol < len) + { + dest->byte = temp[effcol++]; + dest->attrib = attrib | ((effcol <= m_divider) ? DCA_ANCILLARY : DCA_NORMAL); + dest++; + col++; + } + + // advance to the next item + curitem = curitem->m_next; + } + + // fill the rest with blanks + while (col < m_visible.x) + { + dest->byte = ' '; + dest->attrib = DCA_NORMAL; + dest++; + col++; + } + } + + // remember the last update + m_last_update = total_cycles; +} + + +//------------------------------------------------- +// state_item - constructor +//------------------------------------------------- + +debug_view_state::state_item::state_item(int index, const char *name, UINT8 valuechars) + : m_next(NULL), + m_lastval(0), + m_currval(0), + m_index(index), + m_vallen(valuechars), + m_symbol(name) +{ +} diff --git a/src/emu/debug/dvstate.h b/src/emu/debug/dvstate.h new file mode 100644 index 00000000000..b30c973728c --- /dev/null +++ b/src/emu/debug/dvstate.h @@ -0,0 +1,117 @@ +/********************************************************************* + + dvstate.h + + State debugger view. + +**************************************************************************** + + 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. + +***************************************************************************/ + +#ifndef __DVSTATE_H__ +#define __DVSTATE_H__ + +#include "debugvw.h" + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// data sources for state views +class debug_view_state_source : public debug_view_source +{ + friend class debug_view_state; + + // construction/destruction + debug_view_state_source(const char *name, device_t &device); + +public: + // getters + device_t &device() const { return m_device; } + +private: + // internal state + device_t & m_device; // underlying device + device_state_interface *m_stateintf; // state interface + device_execute_interface *m_execintf; // execution interface +}; + + +// debug view for state +class debug_view_state : public debug_view +{ + friend resource_pool_object<debug_view_state>::~resource_pool_object(); + friend class debug_view_manager; + + // construction/destruction + debug_view_state(running_machine &machine, debug_view_osd_update_func osdupdate, void *osdprivate); + virtual ~debug_view_state(); + +protected: + // view overrides + virtual void view_update(); + virtual void view_notify(debug_view_notification type); + +private: + struct state_item + { + state_item(int index, const char *name, UINT8 valuechars); + + state_item * m_next; // next item + UINT64 m_lastval; // last value + UINT64 m_currval; // current value + int m_index; // index + UINT8 m_vallen; // number of value chars + astring m_symbol; // symbol + }; + + // internal helpers + void enumerate_sources(); + void reset(); + void recompute(); + + // internal state + int m_divider; // dividing column + UINT64 m_last_update; // execution counter at last update + state_item * m_state_list; // state data + + // constants + static const int REG_DIVIDER = -10; + static const int REG_CYCLES = -11; + static const int REG_BEAMX = -12; + static const int REG_BEAMY = -13; + static const int REG_FRAME = -14; +}; + + +#endif diff --git a/src/emu/debug/dvtext.c b/src/emu/debug/dvtext.c new file mode 100644 index 00000000000..8e42b182f86 --- /dev/null +++ b/src/emu/debug/dvtext.c @@ -0,0 +1,184 @@ +/********************************************************************* + + dvtext.c + + Debugger simple text-based views. + +**************************************************************************** + + 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. + +***************************************************************************/ + +#include "emu.h" +#include "debugvw.h" +#include "dvtext.h" +#include "debugcon.h" + + + +//************************************************************************** +// DEBUG VIEW TEXTBUF +//************************************************************************** + +//------------------------------------------------- +// debug_view_textbuf - constructor +//------------------------------------------------- + +debug_view_textbuf::debug_view_textbuf(running_machine &machine, debug_view_type type, debug_view_osd_update_func osdupdate, void *osdprivate, text_buffer &textbuf) + : debug_view(machine, type, osdupdate, osdprivate), + m_textbuf(textbuf), + m_at_bottom(true), + m_topseq(0) +{ +} + + +//------------------------------------------------- +// ~debug_view_textbuf - destructor +//------------------------------------------------- + +debug_view_textbuf::~debug_view_textbuf() +{ +} + + +//------------------------------------------------- +// view_update - update a text buffer-based view +//------------------------------------------------- + +void debug_view_textbuf::view_update() +{ + // update the console info + m_total.x = text_buffer_max_width(&m_textbuf); + m_total.y = text_buffer_num_lines(&m_textbuf); + if (m_total.x < 80) + m_total.x = 80; + + // determine the starting sequence number + UINT32 curseq = 0; + if (!m_at_bottom) + { + curseq = m_topseq; + if (!text_buffer_get_seqnum_line(&m_textbuf, curseq)) + m_at_bottom = true; + } + if (m_at_bottom) + { + curseq = text_buffer_line_index_to_seqnum(&m_textbuf, m_total.y - 1); + if (m_total.y < m_visible.y) + curseq -= m_total.y - 1; + else + curseq -= m_visible.y - 1; + } + m_topleft.y = curseq - text_buffer_line_index_to_seqnum(&m_textbuf, 0); + + // loop over visible rows + debug_view_char *dest = m_viewdata; + for (UINT32 row = 0; row < m_visible.y; row++) + { + const char *line = text_buffer_get_seqnum_line(&m_textbuf, curseq++); + UINT32 col = 0; + + // if this visible row is valid, add it to the buffer + if (line != NULL) + { + size_t len = strlen(line); + UINT32 effcol = m_topleft.x; + + // copy data + while (col < m_visible.x && effcol < len) + { + dest->byte = line[effcol++]; + dest->attrib = DCA_NORMAL; + dest++; + col++; + } + } + + // fill the rest with blanks + while (col < m_visible.x) + { + dest->byte = ' '; + dest->attrib = DCA_NORMAL; + dest++; + col++; + } + } +} + + +//------------------------------------------------- +// view_notify - handle notification of updates +// to visible area +//------------------------------------------------- + +void debug_view_textbuf::view_notify(debug_view_notification type) +{ + if (type == VIEW_NOTIFY_VISIBLE_CHANGED) + { + // if the bottom line is visible, just track the bottom + m_at_bottom = (m_total.y >= m_topleft.y && m_total.y <= m_topleft.y + m_visible.y); + + /* otherwise, track the seqence number of the top line */ + if (!m_at_bottom) + m_topseq = text_buffer_line_index_to_seqnum(&m_textbuf, m_topleft.y); + } +} + + + +//************************************************************************** +// DEBUG VIEW CONSOLE +//************************************************************************** + +//------------------------------------------------- +// debug_view_console - constructor +//------------------------------------------------- + +debug_view_console::debug_view_console(running_machine &machine, debug_view_osd_update_func osdupdate, void *osdprivate) + : debug_view_textbuf(machine, DVT_CONSOLE, osdupdate, osdprivate, *debug_console_get_textbuf()) +{ +} + + + +//************************************************************************** +// DEBUG VIEW LOG +//************************************************************************** + +//------------------------------------------------- +// debug_view_log - constructor +//------------------------------------------------- + +debug_view_log::debug_view_log(running_machine &machine, debug_view_osd_update_func osdupdate, void *osdprivate) + : debug_view_textbuf(machine, DVT_LOG, osdupdate, osdprivate, *debug_errorlog_get_textbuf()) +{ +} diff --git a/src/emu/debug/dvtext.h b/src/emu/debug/dvtext.h new file mode 100644 index 00000000000..dcaa02eaa74 --- /dev/null +++ b/src/emu/debug/dvtext.h @@ -0,0 +1,90 @@ +/********************************************************************* + + dvtext.h + + Debugger simple text-based views. + +**************************************************************************** + + 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. + +***************************************************************************/ + +#ifndef __DVTEXT_H__ +#define __DVTEXT_H__ + +#include "debugvw.h" +#include "textbuf.h" + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// debug_view_textbuf contains data specific to a textbuffer view +class debug_view_textbuf : public debug_view +{ + friend class debug_view_manager; + +protected: + // construction/destruction + debug_view_textbuf(running_machine &machine, debug_view_type type, debug_view_osd_update_func osdupdate, void *osdprivate, text_buffer &textbuf); + virtual ~debug_view_textbuf(); + +protected: + // view overrides + virtual void view_update(); + virtual void view_notify(debug_view_notification type); + +private: + // internal state + text_buffer & m_textbuf; /* pointer to the text buffer */ + bool m_at_bottom; /* are we tracking new stuff being added? */ + UINT32 m_topseq; /* sequence number of the top line */ +}; + + +// debug_view_console describes a console view +class debug_view_console : public debug_view_textbuf +{ + friend class debug_view_manager; + debug_view_console(running_machine &machine, debug_view_osd_update_func osdupdate, void *osdprivate); +}; + + +// debug_view_textbuf describes an error log view +class debug_view_log : public debug_view_textbuf +{ + friend class debug_view_manager; + debug_view_log(running_machine &machine, debug_view_osd_update_func osdupdate, void *osdprivate); +}; + + +#endif |
