/********************************************************************* debugvw.c Debugger view engine. Copyright Nicola Salmoria and the MAME Team. Visit http://mamedev.org for licensing and usage restrictions. *********************************************************************/ #include "driver.h" #include "debugvw.h" #include "debugcmd.h" #include "debugcmt.h" #include "debugcpu.h" #include "debugcon.h" #include "express.h" #include "textbuf.h" #include /*************************************************************************** CONSTANTS ***************************************************************************/ #define DEFAULT_DASM_LINES (1000) #define DEFAULT_DASM_WIDTH (50) #define DASM_MAX_BYTES (16) #define MEM_MAX_LINE_WIDTH (1024) /*************************************************************************** TYPE DEFINITIONS ***************************************************************************/ /* debug_view_callbacks contains calbacks specific to a given view */ typedef struct _debug_view_callbacks debug_view_callbacks; struct _debug_view_callbacks { int (*alloc)(debug_view *); /* allocate memory */ void (*free)(debug_view *); /* free memory */ void (*update)(debug_view *); /* update contents */ void (*getprop)(debug_view *, UINT32, debug_property_info *value); /* get property */ void (*setprop)(debug_view *, UINT32, debug_property_info value); /* set property */ }; /* debug_view describes a single text-based view */ /* typedef struct _debug_view debug_view -- defined in debugvw.h */ struct _debug_view { debug_view * next; /* link to the next view */ UINT8 type; /* type of view */ void * extra_data; /* extra view-specific data */ debug_view_callbacks cb; /* callback for this view */ void * osd_private_data; /* OSD-managed private data */ /* visibility info */ UINT32 visible_rows; /* number of currently visible rows */ UINT32 visible_cols; /* number of currently visible columns */ UINT32 total_rows; /* total number of rows */ UINT32 total_cols; /* total number of columns */ UINT32 top_row; /* current top row */ UINT32 left_col; /* current left column */ UINT32 supports_cursor; /* does this view support a cursor? */ UINT32 cursor_visible; /* is the cursor visible? */ UINT32 cursor_row; /* cursor row */ UINT32 cursor_col; /* cursor column */ /* update info */ UINT8 update_level; /* update level; updates when this hits 0 */ UINT8 update_pending; /* true if there is a pending update */ void (*update_func)(debug_view *);/* callback for the 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 */ UINT32 regnum; /* index */ UINT8 tagstart; /* starting tag char */ UINT8 taglen; /* number of tag chars */ UINT8 valstart; /* starting value char */ UINT8 vallen; /* number of value chars */ }; typedef struct _debug_view_registers debug_view_registers; struct _debug_view_registers { UINT8 recompute; /* do we need to recompute the layout the next change? */ UINT8 cpunum; /* target CPU number */ int divider; /* dividing column */ UINT32 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 { UINT8 recompute; /* do we need to recompute the layout the next change? */ UINT8 cpunum; /* target CPU number */ UINT8 right_column; /* right column? */ UINT32 backwards_steps; /* number of backwards steps */ UINT32 dasm_width; /* width of the disassembly area */ UINT8 * last_opcode_base; /* last opcode base */ UINT8 * last_opcode_arg_base; /* last opcode arg base */ UINT32 last_change_count; /* last comment change count */ offs_t last_pcbyte; /* last PC byte value */ UINT32 active_address; /* the address cursor_row is pointing to */ int divider1, divider2; /* left and right divider columns */ int divider3; /* comment divider column */ UINT8 live_tracking; /* track the value of the live expression? */ UINT64 last_result; /* last result from the expression */ parsed_expression *expression; /* expression to compute */ char * expression_string; /* copy of the expression string */ UINT8 expression_dirty; /* true if the expression needs to be re-evaluated */ UINT32 allocated_rows; /* allocated rows */ UINT32 allocated_cols; /* allocated columns */ offs_t * address; /* 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 { UINT8 recompute; /* do we need to recompute the layout the next change? */ UINT8 cpunum; /* target CPU number */ int divider1, divider2; /* left and right divider columns */ UINT8 spacenum; /* target address space */ UINT8 bytes_per_chunk; /* bytes per unit */ UINT16 chunks_displayed; /* number of chunks displayed per line */ UINT8 reverse_view; /* reverse-endian view? */ UINT8 ascii_view; /* display ASCII characters? */ UINT8 live_tracking; /* track the value of the live expression? */ UINT8 byte_offset; /* byte offset within each row */ UINT8 no_translation; /* don't run addresses through the cpu translation hook */ UINT64 last_result; /* last result from the expression */ parsed_expression *expression; /* expression to compute */ char * expression_string; /* copy of the expression string */ UINT8 expression_dirty; /* true if the expression needs to be re-evaluated */ void * raw_base; /* base of raw memory view (overrides CPU/space) */ UINT32 raw_length; /* length of raw memory view */ UINT8 raw_offset_xor; /* xor to apply to offsets */ UINT8 raw_little_endian; /* little endian data? */ }; /* 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 */ }; /*************************************************************************** LOCAL VARIABLES ***************************************************************************/ static debug_view *first_view; /*************************************************************************** FUNCTION PROTOTYPES ***************************************************************************/ static int console_alloc(debug_view *view); static int log_alloc(debug_view *view); static int registers_alloc(debug_view *view); static void registers_free(debug_view *view); static void registers_update(debug_view *view); static void registers_getprop(debug_view *view, UINT32 property, debug_property_info *value); static void registers_setprop(debug_view *view, UINT32 property, debug_property_info value); static int disasm_alloc(debug_view *view); static void disasm_free(debug_view *view); static void disasm_update(debug_view *view); static void disasm_getprop(debug_view *view, UINT32 property, debug_property_info *value); static void disasm_setprop(debug_view *view, UINT32 property, debug_property_info value); static int memory_alloc(debug_view *view); static void memory_free(debug_view *view); static void memory_update(debug_view *view); static void memory_getprop(debug_view *view, UINT32 property, debug_property_info *value); static void memory_setprop(debug_view *view, UINT32 property, debug_property_info value); static int textbuf_alloc(debug_view *view, text_buffer *textbuf); static void textbuf_free(debug_view *view); static void textbuf_update(debug_view *view); static void textbuf_getprop(debug_view *view, UINT32 property, debug_property_info *value); static void textbuf_setprop(debug_view *view, UINT32 property, debug_property_info value); static const debug_view_callbacks callback_table[] = { { NULL, NULL, NULL, NULL, NULL }, { console_alloc, textbuf_free, textbuf_update, textbuf_getprop, textbuf_setprop }, { registers_alloc, registers_free, registers_update, registers_getprop, registers_setprop }, { disasm_alloc, disasm_free, disasm_update, disasm_getprop, disasm_setprop }, { memory_alloc, memory_free, memory_update, memory_getprop, memory_setprop }, { log_alloc, textbuf_free, textbuf_update, textbuf_getprop, textbuf_setprop } }; /*************************************************************************** Initialization and shutdown ***************************************************************************/ /*------------------------------------------------- debug_view_init - initializes the view system -------------------------------------------------*/ void debug_view_init(running_machine *machine) { /* reset the initial list */ first_view = NULL; add_exit_callback(machine, debug_view_exit); } /*------------------------------------------------- debug_view_exit - exits the view system -------------------------------------------------*/ void debug_view_exit(running_machine *machine) { /* kill all the views */ while (first_view != NULL) debug_view_free(first_view); } /*************************************************************************** View creation/deletion ***************************************************************************/ /*------------------------------------------------- debug_view_alloc - allocate a new debug view -------------------------------------------------*/ debug_view *debug_view_alloc(int type) { debug_view *view; /* allocate memory for the view */ view = malloc(sizeof(*view)); if (!view) return NULL; memset(view, 0, sizeof(*view)); /* set the view type information */ view->type = type; view->cb = callback_table[type]; /* set up some reasonable defaults */ view->visible_rows = 10; view->visible_cols = 10; view->total_rows = 10; view->total_cols = 10; /* allocate memory for the buffer */ view->viewdata_size = view->visible_rows*view->visible_cols; view->viewdata = malloc(sizeof(view->viewdata[0]) * view->viewdata_size); if (!view->viewdata) { free(view); return NULL; } /* allocate extra memory */ if (view->cb.alloc && !(*view->cb.alloc)(view)) { free(view->viewdata); free(view); return NULL; } /* link it in */ view->next = first_view; first_view = view; return view; } /*------------------------------------------------- debug_view_free - free a debug view -------------------------------------------------*/ void debug_view_free(debug_view *view) { debug_view *curview, *prevview; /* find the view */ for (prevview = NULL, curview = first_view; curview != NULL; prevview = curview, curview = curview->next) if (curview == view) { /* unlink */ if (prevview != NULL) prevview->next = curview->next; else first_view = curview->next; /* free memory */ if (view->cb.free) (*view->cb.free)(view); if (view->viewdata) free(view->viewdata); free(view); break; } } /*************************************************************************** Property management ***************************************************************************/ /*------------------------------------------------- debug_view_get_property - return the value of a given property -------------------------------------------------*/ void debug_view_get_property(debug_view *view, int property, debug_property_info *value) { switch (property) { case DVP_VISIBLE_ROWS: value->i = view->visible_rows; break; case DVP_VISIBLE_COLS: value->i = view->visible_cols; break; case DVP_TOTAL_ROWS: value->i = view->total_rows; break; case DVP_TOTAL_COLS: value->i = view->total_cols; break; case DVP_TOP_ROW: value->i = view->top_row; break; case DVP_LEFT_COL: value->i = view->left_col; break; case DVP_UPDATE_CALLBACK: value->f = (genf *) view->update_func; break; case DVP_VIEW_DATA: value->p = (void *) view->viewdata; break; case DVP_SUPPORTS_CURSOR: value->i = view->supports_cursor; break; case DVP_CURSOR_VISIBLE: value->i = view->cursor_visible; break; case DVP_CURSOR_ROW: value->i = view->cursor_row; break; case DVP_CURSOR_COL: value->i = view->cursor_col; break; case DVP_OSD_PRIVATE: value->p = view->osd_private_data; break; default: if (view->cb.getprop) (*view->cb.getprop)(view, property, value); else fatalerror("Attempt to get invalid property %d on debug view type %d", property, view->type); break; } } /*------------------------------------------------- debug_view_set_property - set the value of a given property -------------------------------------------------*/ void debug_view_set_property(debug_view *view, int property, debug_property_info value) { switch (property) { case DVP_VISIBLE_ROWS: if (value.i != view->visible_rows) { debug_view_begin_update(view); view->visible_rows = value.i; view->update_pending = TRUE; debug_view_end_update(view); } break; case DVP_VISIBLE_COLS: if (value.i != view->visible_cols) { debug_view_begin_update(view); view->visible_cols = value.i; view->update_pending = TRUE; debug_view_end_update(view); } break; case DVP_TOTAL_ROWS: if (value.i != view->total_rows) { debug_view_begin_update(view); view->total_rows = value.i; view->update_pending = TRUE; debug_view_end_update(view); } break; case DVP_TOTAL_COLS: if (value.i != view->total_cols) { debug_view_begin_update(view); view->total_cols = value.i; view->update_pending = TRUE; debug_view_end_update(view); } break; case DVP_TOP_ROW: if (value.i != view->top_row) { debug_view_begin_update(view); view->top_row = value.i; view->update_pending = TRUE; debug_view_end_update(view); } break; case DVP_LEFT_COL: if (value.i != view->left_col) { debug_view_begin_update(view); view->left_col = value.i; view->update_pending = TRUE; debug_view_end_update(view); } break; case DVP_UPDATE_CALLBACK: debug_view_begin_update(view); view->update_func = (void (*)(debug_view *)) value.f; view->update_pending = TRUE; debug_view_end_update(view); break; case DVP_VIEW_DATA: /* read-only */ break; case DVP_SUPPORTS_CURSOR: /* read-only */ break; case DVP_CURSOR_VISIBLE: if (value.i != view->cursor_visible) { debug_view_begin_update(view); view->cursor_visible = value.i; view->update_pending = TRUE; debug_view_end_update(view); } break; case DVP_CURSOR_ROW: if (value.i != view->cursor_row) { debug_view_begin_update(view); view->cursor_row = value.i; view->update_pending = TRUE; debug_view_end_update(view); } if (view->type == DVT_DISASSEMBLY) { debug_view_disasm *dasmdata = view->extra_data; dasmdata->active_address = dasmdata->address[view->cursor_row]; } break; case DVP_CURSOR_COL: if (value.i != view->cursor_col) { debug_view_begin_update(view); view->cursor_col = value.i; view->update_pending = TRUE; debug_view_end_update(view); } break; case DVP_OSD_PRIVATE: view->osd_private_data = value.p; break; default: if (view->cb.setprop) (*view->cb.setprop)(view, property, value); else fatalerror("Attempt to set invalid property %d on debug view type %d", property, view->type); 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 */ view->update_pending = 0; /* resize the viewdata if needed */ size = view->visible_rows*view->visible_cols; if (size > view->viewdata_size) { view->viewdata_size = size; view->viewdata = realloc(view->viewdata, sizeof(view->viewdata[0]) * view->viewdata_size); } /* update the view */ if (view->cb.update) (*view->cb.update)(view); /* update the owner */ if (view->update_func) (*view->update_func)(view); } } /* decrement the level */ view->update_level--; } /*------------------------------------------------- debug_view_update_all - force all views to refresh -------------------------------------------------*/ void debug_view_update_all(void) { debug_view *view; /* loop over each view and force an update */ for (view = first_view; 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(int type) { debug_view *view; /* loop over each view and force an update */ for (view = first_view; view != NULL; view = view->next) if (view->type == type) { debug_view_begin_update(view); view->update_pending = TRUE; debug_view_end_update(view); } } /*************************************************************************** Console view ***************************************************************************/ /*------------------------------------------------- console_alloc - allocate memory for the log view -------------------------------------------------*/ static int console_alloc(debug_view *view) { return textbuf_alloc(view, debug_console_get_textbuf()); } /*************************************************************************** Log view ***************************************************************************/ /*------------------------------------------------- log_alloc - allocate memory for the log view -------------------------------------------------*/ static int log_alloc(debug_view *view) { return textbuf_alloc(view, debug_errorlog_get_textbuf()); } /*************************************************************************** Generic text buffer view ***************************************************************************/ /*------------------------------------------------- textbuf_alloc - allocate memory for a text buffer view -------------------------------------------------*/ static int textbuf_alloc(debug_view *view, text_buffer *textbuf) { debug_view_textbuf *textdata; /* allocate memory */ textdata = malloc(sizeof(*textdata)); if (!textdata) return 0; memset(textdata, 0, sizeof(*textdata)); /* by default we track live */ textdata->textbuf = textbuf; textdata->at_bottom = TRUE; /* stash the extra data pointer */ view->extra_data = textdata; return 1; } /*------------------------------------------------- textbuf_free - free memory for the log view -------------------------------------------------*/ static void textbuf_free(debug_view *view) { debug_view_textbuf *textdata = view->extra_data; /* free any memory we callocated */ if (textdata) free(textdata); view->extra_data = NULL; } /*------------------------------------------------- textbuf_update - update the log view -------------------------------------------------*/ static void textbuf_update(debug_view *view) { debug_view_textbuf *textdata = view->extra_data; debug_view_char *dest = view->viewdata; UINT32 curseq = 0, row; /* update the console info */ view->total_rows = text_buffer_num_lines(textdata->textbuf); view->total_cols = text_buffer_max_width(textdata->textbuf); if (view->total_cols < 80) view->total_cols = 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_rows - 1); if (view->total_rows < view->visible_rows) curseq -= view->total_rows - 1; else curseq -= view->visible_rows - 1; } view->top_row = curseq - text_buffer_line_index_to_seqnum(textdata->textbuf, 0); /* loop over visible rows */ for (row = 0; row < view->visible_rows; row++) { 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->left_col; /* copy data */ while (col < view->visible_cols && effcol < len) { dest->byte = line[effcol++]; dest->attrib = DCA_NORMAL; dest++; col++; } } /* fill the rest with blanks */ while (col < view->visible_cols) { dest->byte = ' '; dest->attrib = DCA_NORMAL; dest++; col++; } } } /*------------------------------------------------- textbuf_getprop - return the value of a given property -------------------------------------------------*/ static void textbuf_getprop(debug_view *view, UINT32 property, debug_property_info *value) { debug_view_textbuf *textdata = view->extra_data; switch (property) { case DVP_TEXTBUF_LINE_LOCK: value->i = textdata->at_bottom ? (UINT32)-1 : textdata->topseq - text_buffer_line_index_to_seqnum(textdata->textbuf, 0); break; default: fatalerror("Attempt to get invalid property %d on debug view type %d", property, view->type); break; } } /*------------------------------------------------- textbuf_setprop - set the value of a given property -------------------------------------------------*/ static void textbuf_setprop(debug_view *view, UINT32 property, debug_property_info value) { debug_view_textbuf *textdata = view->extra_data; switch (property) { case DVP_TEXTBUF_LINE_LOCK: if (value.i == (UINT32)-1) { if (!textdata->at_bottom) { debug_view_begin_update(view); textdata->at_bottom = TRUE; view->update_pending = TRUE; debug_view_end_update(view); } } else { UINT32 seq = text_buffer_line_index_to_seqnum(textdata->textbuf, value.i); if (seq != textdata->topseq || textdata->at_bottom) { debug_view_begin_update(view); textdata->topseq = seq; textdata->at_bottom = FALSE; view->update_pending = TRUE; debug_view_end_update(view); } } break; default: fatalerror("Attempt to set invalid property %d on debug view type %d", property, view->type); break; } } /*************************************************************************** Registers view ***************************************************************************/ /*------------------------------------------------- registers_alloc - allocate memory for the registers view -------------------------------------------------*/ static int registers_alloc(debug_view *view) { debug_view_registers *regdata; /* allocate memory */ regdata = malloc(sizeof(*regdata)); if (!regdata) return 0; memset(regdata, 0, sizeof(*regdata)); /* initialize */ regdata->recompute = TRUE; /* stash the extra data pointer */ view->extra_data = regdata; return 1; } /*------------------------------------------------- registers_free - free memory for the registers view -------------------------------------------------*/ static void registers_free(debug_view *view) { debug_view_registers *regdata = view->extra_data; /* free any memory we callocated */ if (regdata) free(regdata); view->extra_data = NULL; } /*------------------------------------------------- add_register - adds a register to the registers view -------------------------------------------------*/ static void add_register(debug_view *view, int regnum, const char *str) { debug_view_registers *regdata = view->extra_data; int tagstart, taglen, valstart, vallen; const char *colon; colon = strchr(str, ':'); /* if no colon, mark everything as tag */ if (!colon) { tagstart = 0; taglen = (int)strlen(str); valstart = 0; vallen = 0; } /* otherwise, break the string at the colon */ else { tagstart = 0; taglen = colon - str; valstart = (colon + 1) - str; vallen = (int)strlen(colon + 1); } /* now trim spaces */ while (isspace(str[tagstart]) && taglen > 0) tagstart++, taglen--; while (isspace(str[tagstart + taglen - 1]) && taglen > 0) taglen--; while (isspace(str[valstart]) && vallen > 0) valstart++, vallen--; while (isspace(str[valstart + vallen - 1]) && vallen > 0) vallen--; if (str[valstart] == '!') valstart++, vallen--; /* note the register number and info */ regdata->reg[view->total_rows].lastval = regdata->reg[view->total_rows].currval = cpunum_get_reg(regdata->cpunum, regnum); regdata->reg[view->total_rows].regnum = regnum; regdata->reg[view->total_rows].tagstart = tagstart; regdata->reg[view->total_rows].taglen = taglen; regdata->reg[view->total_rows].valstart = valstart; regdata->reg[view->total_rows].vallen = vallen; view->total_rows++; /* adjust the divider and total cols, if necessary */ regdata->divider = MAX(regdata->divider, 1 + taglen + 1); view->total_cols = MAX(view->total_cols, 1 + taglen + 2 + vallen + 1); } /*------------------------------------------------- registers_recompute - recompute all info for the registers view -------------------------------------------------*/ static void registers_recompute(debug_view *view) { debug_view_registers *regdata = view->extra_data; const int *list = cpunum_debug_register_list(regdata->cpunum); int regnum, maxtaglen, maxvallen; /* reset the view parameters */ view->top_row = 0; view->left_col = 0; view->total_rows = 0; view->total_cols = 0; regdata->divider = 0; /* add a cycles entry: cycles:99999999 */ regdata->reg[view->total_rows].lastval = regdata->reg[view->total_rows].currval = 0; regdata->reg[view->total_rows].regnum = MAX_REGS + 1; regdata->reg[view->total_rows].tagstart = 0; regdata->reg[view->total_rows].taglen = 6; regdata->reg[view->total_rows].valstart = 7; regdata->reg[view->total_rows].vallen = 8; maxtaglen = regdata->reg[view->total_rows].taglen; maxvallen = regdata->reg[view->total_rows].vallen; view->total_rows++; /* add a beam entry: beamx:123 */ regdata->reg[view->total_rows].lastval = regdata->reg[view->total_rows].currval = 0; regdata->reg[view->total_rows].regnum = MAX_REGS + 2; regdata->reg[view->total_rows].tagstart = 0; regdata->reg[view->total_rows].taglen = 5; regdata->reg[view->total_rows].valstart = 6; regdata->reg[view->total_rows].vallen = 3; maxtaglen = MAX(maxtaglen, regdata->reg[view->total_rows].taglen); maxvallen = MAX(maxvallen, regdata->reg[view->total_rows].vallen); view->total_rows++; /* add a beam entry: beamy:456 */ regdata->reg[view->total_rows].lastval = regdata->reg[view->total_rows].currval = 0; regdata->reg[view->total_rows].regnum = MAX_REGS + 3; regdata->reg[view->total_rows].tagstart = 0; regdata->reg[view->total_rows].taglen = 5; regdata->reg[view->total_rows].valstart = 6; regdata->reg[view->total_rows].vallen = 3; maxtaglen = MAX(maxtaglen, regdata->reg[view->total_rows].taglen); maxvallen = MAX(maxvallen, regdata->reg[view->total_rows].vallen); view->total_rows++; /* add a flags entry: flags:xxxxxxxx */ regdata->reg[view->total_rows].lastval = regdata->reg[view->total_rows].currval = 0; regdata->reg[view->total_rows].regnum = MAX_REGS + 4; regdata->reg[view->total_rows].tagstart = 0; regdata->reg[view->total_rows].taglen = 5; regdata->reg[view->total_rows].valstart = 6; regdata->reg[view->total_rows].vallen = (UINT32)strlen(cpunum_flags(regdata->cpunum)); maxtaglen = MAX(maxtaglen, regdata->reg[view->total_rows].taglen); maxvallen = MAX(maxvallen, regdata->reg[view->total_rows].vallen); view->total_rows++; /* add a divider entry */ regdata->reg[view->total_rows].lastval = regdata->reg[view->total_rows].currval = 0; regdata->reg[view->total_rows].regnum = MAX_REGS; view->total_rows++; /* set the current divider and total cols */ regdata->divider = 1 + maxtaglen + 1; view->total_cols = 1 + maxtaglen + 2 + maxvallen + 1; /* add all registers into it */ for (regnum = 0; regnum < MAX_REGS; regnum++) { const char *str = NULL; int regid; /* identify the register id */ regid = list ? list[regnum] : regnum; if (regid < 0) break; /* retrieve the string for this register */ str = cpunum_reg_string(regdata->cpunum, regid); /* did we get a string? */ if (str && str[0] != '\0' && str[0] != '~') add_register(view, regid, str); } /* no longer need to recompute */ regdata->recompute = FALSE; } /*------------------------------------------------- registers_update - update the contents of the register view -------------------------------------------------*/ static void registers_update(debug_view *view) { UINT32 execution_counter = debug_get_execution_counter(); debug_view_registers *regdata = view->extra_data; debug_view_char *dest = view->viewdata; UINT32 row, i; int scrnum = 0; /* cannot update if no active CPU */ if (cpu_getactivecpu() < 0) return; /* if our assumptions changed, revisit them */ if (regdata->recompute) registers_recompute(view); /* loop over visible rows */ for (row = 0; row < view->visible_rows; row++) { UINT32 effrow = view->top_row + row; UINT32 col = 0; /* if this visible row is valid, add it to the buffer */ if (effrow < view->total_rows) { debug_view_register *reg = ®data->reg[effrow]; UINT32 effcol = view->left_col; char temp[256], dummy[100]; UINT8 attrib = DCA_NORMAL; UINT32 len = 0; char *data = dummy; /* get the effective string */ dummy[0] = 0; if (reg->regnum >= MAX_REGS) { reg->lastval = reg->currval; switch (reg->regnum) { case MAX_REGS: reg->tagstart = reg->valstart = reg->vallen = 0; reg->taglen = view->total_cols; for (i = 0; i < view->total_cols; i++) dummy[i] = '-'; dummy[i] = 0; break; case MAX_REGS + 1: sprintf(dummy, "cycles:%-8d", activecpu_get_icount()); reg->currval = activecpu_get_icount(); break; case MAX_REGS + 2: if (video_screen_exists(scrnum)) sprintf(dummy, "beamx:%3d", video_screen_get_hpos(scrnum)); break; case MAX_REGS + 3: if (video_screen_exists(scrnum)) sprintf(dummy, "beamy:%3d", video_screen_get_vpos(scrnum)); break; case MAX_REGS + 4: sprintf(dummy, "flags:%s", activecpu_flags()); break; } } else { data = (char *)cpunum_reg_string(regdata->cpunum, reg->regnum); if (regdata->last_update != execution_counter) reg->lastval = reg->currval; reg->currval = cpunum_get_reg(regdata->cpunum, reg->regnum); } /* see if we changed */ if (reg->lastval != reg->currval) attrib = DCA_CHANGED; /* build up a string */ if (reg->taglen < regdata->divider - 1) { memset(&temp[len], ' ', regdata->divider - 1 - reg->taglen); len += regdata->divider - 1 - reg->taglen; } memcpy(&temp[len], &data[reg->tagstart], reg->taglen); len += reg->taglen; temp[len++] = ' '; temp[len++] = ' '; memcpy(&temp[len], &data[reg->valstart], reg->vallen); len += reg->vallen; temp[len++] = ' '; temp[len] = 0; /* copy data */ while (col < view->visible_cols && 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_cols) { dest->byte = ' '; dest->attrib = DCA_NORMAL; dest++; col++; } } /* remember the last update */ regdata->last_update = execution_counter; } /*------------------------------------------------- registers_getprop - return the value of a given property -------------------------------------------------*/ static void registers_getprop(debug_view *view, UINT32 property, debug_property_info *value) { debug_view_registers *regdata = view->extra_data; switch (property) { case DVP_REGS_CPUNUM: value->i = regdata->cpunum; break; default: fatalerror("Attempt to get invalid property %d on debug view type %d", property, view->type); break; } } /*------------------------------------------------- registers_getprop - set the value of a given property -------------------------------------------------*/ static void registers_setprop(debug_view *view, UINT32 property, debug_property_info value) { debug_view_registers *regdata = view->extra_data; switch (property) { case DVP_REGS_CPUNUM: if (value.i != regdata->cpunum) { debug_view_begin_update(view); regdata->cpunum = value.i; regdata->recompute = TRUE; view->update_pending = TRUE; debug_view_end_update(view); } break; default: fatalerror("Attempt to set invalid property %d on debug view type %d", property, view->type); break; } } /*************************************************************************** Disassembly view ***************************************************************************/ /*------------------------------------------------- disasm_alloc - allocate disasm for the disassembly view -------------------------------------------------*/ static int disasm_alloc(debug_view *view) { debug_view_disasm *dasmdata; int total_comments = 0; int i; /* allocate disasm */ dasmdata = malloc(sizeof(*dasmdata)); if (!dasmdata) return 0; memset(dasmdata, 0, sizeof(*dasmdata)); /* count the number of comments */ for (i = 0; i < cpu_gettotalcpu(); i++) total_comments += debug_comment_get_count(i); /* initialize */ dasmdata->recompute = TRUE; dasmdata->right_column = (total_comments > 0) ? DVP_DASM_RIGHTCOL_COMMENTS : DVP_DASM_RIGHTCOL_RAW; dasmdata->backwards_steps = 3; dasmdata->dasm_width = DEFAULT_DASM_WIDTH; dasmdata->active_address = 0; /* stash the extra data pointer */ view->total_rows = DEFAULT_DASM_LINES; view->extra_data = dasmdata; /* we support cursors */ view->supports_cursor = TRUE; return 1; } /*------------------------------------------------- disasm_free - free disasm for the disassembly view -------------------------------------------------*/ static void disasm_free(debug_view *view) { debug_view_disasm *dasmdata = view->extra_data; /* free any disasm we callocated */ if (dasmdata) { if (dasmdata->expression) expression_free(dasmdata->expression); if (dasmdata->expression_string) free(dasmdata->expression_string); if (dasmdata->address) free(dasmdata->address); if (dasmdata->dasm) free(dasmdata->dasm); free(dasmdata); } view->extra_data = NULL; } /*------------------------------------------------- disasm_back_up - back up the specified number of instructions from the given PC -------------------------------------------------*/ static offs_t disasm_back_up(int cpunum, const debug_cpu_info *cpuinfo, offs_t startpc, int numinstrs) { int minlen = BYTE2ADDR(activecpu_min_instruction_bytes(), cpuinfo, ADDRESS_SPACE_PROGRAM); int maxlen = BYTE2ADDR(activecpu_max_instruction_bytes(), cpuinfo, ADDRESS_SPACE_PROGRAM); UINT32 addrmask = cpuinfo->space[ADDRESS_SPACE_PROGRAM].logaddrmask; offs_t curpc, lastgoodpc = startpc, temppc; 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 = startpc - minlen * numinstrs; if (curpc > startpc) curpc = 0; /* prefetch the opcode bytes */ for (temppc = curpc; temppc < startpc; temppc++) { opbuf[1000 + temppc - startpc] = debug_read_opcode(temppc, 1, FALSE); argbuf[1000 + temppc - startpc] = debug_read_opcode(temppc, 1, TRUE); } /* loop until we hit it */ while (1) { offs_t testpc, nextcurpc, instlen, instcount = 0; /* loop until we get past the target instruction */ for (testpc = curpc; testpc < startpc; testpc += instlen) { /* convert PC to a byte offset */ offs_t pcbyte = ADDR2BYTE_MASKED(testpc, cpuinfo, ADDRESS_SPACE_PROGRAM); /* get the disassembly, but only if mapped */ instlen = 1; if (!cpuinfo->translate || (*cpuinfo->translate)(ADDRESS_SPACE_PROGRAM, &pcbyte)) instlen = activecpu_dasm(dasmbuffer, testpc & addrmask, &opbuf[1000 + testpc - startpc], &argbuf[1000 + testpc - startpc]) & DASMFLAG_LENGTHMASK; /* count this one */ instcount++; } /* if we ended up right on startpc, this is a good candidate */ if (testpc == startpc && instcount <= numinstrs) lastgoodpc = curpc; /* we're also done if we go back too far */ if (startpc - curpc >= numinstrs * maxlen) break; /* and if we hit 0, we're done */ if (curpc == 0) break; /* back up one more and try again */ nextcurpc = curpc - minlen; if (nextcurpc > startpc) nextcurpc = 0; /* prefetch the opcode bytes */ for (temppc = nextcurpc; temppc < curpc; temppc++) { opbuf[1000 + temppc - startpc] = debug_read_opcode(temppc, 1, FALSE); argbuf[1000 + temppc - startpc] = debug_read_opcode(temppc, 1, TRUE); } /* update curpc once we're done fetching */ curpc = nextcurpc; } return lastgoodpc; } /*------------------------------------------------- disasm_generate_bytes - generate the opcode byte values -------------------------------------------------*/ static void disasm_generate_bytes(offs_t pcbyte, int numbytes, const debug_cpu_info *cpuinfo, int minbytes, char *string, int maxchars, int encrypted) { int byte, offset = 0; UINT64 val; switch (minbytes) { case 1: if (maxchars >= 2) offset = sprintf(string, "%02X", (UINT32)debug_read_opcode(pcbyte, 1, FALSE)); for (byte = 1; byte < numbytes && offset + 3 < maxchars; byte++) offset += sprintf(&string[offset], " %02X", (UINT32)debug_read_opcode(pcbyte + byte, 1, encrypted)); break; case 2: if (maxchars >= 4) offset = sprintf(string, "%04X", (UINT32)debug_read_opcode(pcbyte, 2, FALSE)); for (byte = 2; byte < numbytes && offset + 5 < maxchars; byte += 2) offset += sprintf(&string[offset], " %04X", (UINT32)debug_read_opcode(pcbyte + byte, 2, encrypted)); break; case 4: if (maxchars >= 8) offset = sprintf(string, "%08X", (UINT32)debug_read_opcode(pcbyte, 4, FALSE)); for (byte = 4; byte < numbytes && offset + 9 < maxchars; byte += 4) offset += sprintf(&string[offset], " %08X", (UINT32)debug_read_opcode(pcbyte + byte, 4, encrypted)); break; case 8: val = debug_read_opcode(pcbyte, 8, FALSE); if (maxchars >= 16) offset = sprintf(string, "%08X%08X", (UINT32)(val >> 32), (UINT32)val); for (byte = 8; byte < numbytes && offset + 17 < maxchars; byte += 8) { val = debug_read_opcode(pcbyte + byte, 8, encrypted); offset += sprintf(&string[offset], " %08X%08X", (UINT32)(val >> 32), (UINT32)val); } break; default: fatalerror("disasm_generate_bytes: unknown size = %d", minbytes); break; } /* 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_recompute - recompute selected info for the disassembly view -------------------------------------------------*/ static int disasm_recompute(debug_view *view, offs_t pc, int startline, int lines, int original_cpunum) { debug_view_disasm *dasmdata = view->extra_data; const debug_cpu_info *cpuinfo = debug_get_cpu_info(dasmdata->cpunum); int chunksize, minbytes, maxbytes, maxbytes_clamped; int changed = FALSE; UINT32 addrmask; int line; /* switch to the context of the CPU in question */ addrmask = cpuinfo->space[ADDRESS_SPACE_PROGRAM].logaddrmask; /* determine how many characters we need for an address and set the divider */ dasmdata->divider1 = 1 + cpuinfo->space[ADDRESS_SPACE_PROGRAM].logchars + 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 */ minbytes = activecpu_min_instruction_bytes(); maxbytes = activecpu_max_instruction_bytes(); /* set the width of the third column according to display mode */ if (dasmdata->right_column == DVP_DASM_RIGHTCOL_RAW || dasmdata->right_column == DVP_DASM_RIGHTCOL_ENCRYPTED) { chunksize = activecpu_databus_width(ADDRESS_SPACE_PROGRAM) / 8; maxbytes_clamped = maxbytes; if (maxbytes_clamped > DASM_MAX_BYTES) maxbytes_clamped = DASM_MAX_BYTES; view->total_cols = dasmdata->divider2 + 1 + 2 * maxbytes_clamped + (maxbytes_clamped / minbytes - 1) + 1; } else if (dasmdata->right_column == DVP_DASM_RIGHTCOL_COMMENTS) view->total_cols = dasmdata->divider2 + 1 + 50; /* DEBUG_COMMENT_MAX_LINE_LENGTH */ else view->total_cols = dasmdata->divider2 + 1; /* reallocate memory if we don't have enough */ if (dasmdata->allocated_rows < view->total_rows || dasmdata->allocated_cols < view->total_cols) { /* update our values */ dasmdata->allocated_rows = view->total_rows; dasmdata->allocated_cols = view->total_cols; /* allocate address array */ if (dasmdata->address) free(dasmdata->address); dasmdata->address = malloc_or_die(sizeof(dasmdata->address[0]) * dasmdata->allocated_rows); /* allocate disassembly buffer */ if (dasmdata->dasm) free(dasmdata->dasm); dasmdata->dasm = malloc_or_die(sizeof(dasmdata->dasm[0]) * dasmdata->allocated_rows * dasmdata->allocated_cols); } /* iterate over lines */ for (line = 0; line < lines; line++) { offs_t pcbyte, tempaddr; char buffer[100]; int numbytes = 0; int instr = startline + line; char oldbuf[100]; char *destbuf = &dasmdata->dasm[instr * dasmdata->allocated_cols]; /* convert PC to a byte offset */ pcbyte = ADDR2BYTE_MASKED(pc, cpuinfo, ADDRESS_SPACE_PROGRAM); /* 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_cols)); /* convert back and set the address of this instruction */ dasmdata->address[instr] = pcbyte; // ! This might make more sense as the following : BYTE2ADDR(pcbyte, cpuinfo, ADDRESS_SPACE_PROGRAM); ! sprintf(&destbuf[0], " %0*X ", cpuinfo->space[ADDRESS_SPACE_PROGRAM].logchars, BYTE2ADDR(pcbyte, cpuinfo, ADDRESS_SPACE_PROGRAM)); /* make sure we can translate the address */ tempaddr = pcbyte; if (!cpuinfo->translate || (*cpuinfo->translate)(ADDRESS_SPACE_PROGRAM, &tempaddr)) { UINT8 opbuf[64], argbuf[64]; /* fetch the bytes up to the maximum */ for (numbytes = 0; numbytes < maxbytes; numbytes++) { opbuf[numbytes] = debug_read_opcode(pcbyte + numbytes, 1, FALSE); argbuf[numbytes] = debug_read_opcode(pcbyte + numbytes, 1, TRUE); } /* disassemble the result */ pc += numbytes = activecpu_dasm(buffer, pc & addrmask, opbuf, argbuf) & DASMFLAG_LENGTHMASK; } else sprintf(buffer, ""); sprintf(&destbuf[dasmdata->divider1 + 1], "%-*s ", dasmdata->dasm_width, buffer); if (dasmdata->right_column == DVP_DASM_RIGHTCOL_RAW || dasmdata->right_column == DVP_DASM_RIGHTCOL_ENCRYPTED) { /* get the bytes */ numbytes = ADDR2BYTE(numbytes, cpuinfo, ADDRESS_SPACE_PROGRAM); disasm_generate_bytes(pcbyte, numbytes, cpuinfo, minbytes, &destbuf[dasmdata->divider2], dasmdata->allocated_cols - dasmdata->divider2, dasmdata->right_column == DVP_DASM_RIGHTCOL_ENCRYPTED); } else if (dasmdata->right_column == DVP_DASM_RIGHTCOL_COMMENTS) { offs_t comment_address = BYTE2ADDR(dasmdata->address[instr], cpuinfo, ADDRESS_SPACE_PROGRAM) ; /* get and add the comment */ if (debug_comment_get_text(cpu_getactivecpu(), comment_address, debug_comment_get_opcode_crc32(comment_address)) != 0x00) { int i ; char bob[DEBUG_COMMENT_MAX_LINE_LENGTH] ; char pre[8] ; // Stick in the 'comment' symbol sprintf(pre, "// ") ; for (i = 0; i < strlen(pre); i++) destbuf[dasmdata->divider2+i] = pre[i] ; // Stick in the comment itself strcpy(bob, debug_comment_get_text(cpu_getactivecpu(), comment_address, debug_comment_get_opcode_crc32(comment_address))) ; for (i = 0; i < (dasmdata->allocated_cols - dasmdata->divider2 - strlen(pre) - 1); i++) destbuf[dasmdata->divider2+i+strlen(pre)] = bob[i] ; } else sprintf(&destbuf[dasmdata->divider2], " "); } /* see if the line changed at all */ if (lines == 1 && strncmp(oldbuf, destbuf, MIN(sizeof(oldbuf), dasmdata->allocated_cols)) != 0) changed = TRUE; } /* reset the opcode base */ if (dasmdata->cpunum == original_cpunum) memory_set_opbase(activecpu_get_physical_pc_byte()); /* update opcode base information */ dasmdata->last_opcode_base = opcode_base; dasmdata->last_opcode_arg_base = opcode_arg_base; dasmdata->last_change_count = debug_comment_all_change_count(); /* now longer need to recompute */ dasmdata->recompute = FALSE; return changed; } /*------------------------------------------------- disasm_update - update the contents of the disassembly view -------------------------------------------------*/ static void disasm_update(debug_view *view) { debug_view_disasm *dasmdata = view->extra_data; const debug_cpu_info *cpuinfo = debug_get_cpu_info(dasmdata->cpunum); offs_t pc = cpunum_get_reg(dasmdata->cpunum, REG_PC); offs_t pcbyte = ADDR2BYTE_MASKED(pc, cpuinfo, ADDRESS_SPACE_PROGRAM); debug_view_char *dest = view->viewdata; int original_cpunum = cpu_getactivecpu(); int recomputed_this_time = FALSE; EXPRERR exprerr; UINT32 row; /* switch to the CPU's context */ cpuintrf_push_context(dasmdata->cpunum); /* if our expression is dirty, fix it */ if (dasmdata->expression_dirty && dasmdata->expression_string) { parsed_expression *expr; /* parse the new expression */ exprerr = expression_parse(dasmdata->expression_string, debug_get_cpu_info(dasmdata->cpunum)->symtable, &expr); /* if it worked, update the expression */ if (exprerr == EXPRERR_NONE) { if (dasmdata->expression) expression_free(dasmdata->expression); dasmdata->expression = expr; } } /* if we're tracking a value, make sure it is visible */ if (dasmdata->expression && (dasmdata->live_tracking || dasmdata->expression_dirty)) { UINT64 result; /* recompute the value of the expression */ exprerr = expression_execute(dasmdata->expression, &result); if (exprerr == EXPRERR_NONE && result != dasmdata->last_result) { offs_t resultbyte = ADDR2BYTE_MASKED(result, cpuinfo, ADDRESS_SPACE_PROGRAM); /* update the result */ dasmdata->last_result = result; /* see if the new result is an address we already have */ for (row = 0; row < dasmdata->allocated_rows; row++) if (dasmdata->address[row] == resultbyte) break; /* if we didn't find it, or if it's really close to the bottom, recompute */ if (row == dasmdata->allocated_rows || row >= view->total_rows - view->visible_rows) dasmdata->recompute = TRUE; /* otherwise, if it's not visible, adjust the view so it is */ else if (row < view->top_row || row >= view->top_row + view->visible_rows - 2) view->top_row = (row > 3) ? row - 3 : 0; } /* no longer dirty */ dasmdata->expression_dirty = FALSE; } /* if the opcode base has changed, rework things */ if (opcode_base != dasmdata->last_opcode_base || opcode_arg_base != dasmdata->last_opcode_arg_base) dasmdata->recompute = TRUE; /* if the comments have changed, redo it */ if (dasmdata->last_change_count != debug_comment_all_change_count()) dasmdata->recompute = TRUE; /* if we need to recompute, do it */ recompute: if (dasmdata->recompute) { /* determine the addresses of what we will display */ offs_t backpc = disasm_back_up(dasmdata->cpunum, cpuinfo, (UINT32)dasmdata->last_result, dasmdata->backwards_steps); /* recompute the view */ if (dasmdata->last_change_count != debug_comment_all_change_count()) { /* smoosh us against the left column, but not the top row */ view->left_col = 0; /* recompute from where we last recomputed! */ disasm_recompute(view, BYTE2ADDR(dasmdata->address[0], cpuinfo, ADDRESS_SPACE_PROGRAM), 0, view->total_rows, original_cpunum); } else { /* put ourselves back in the top left */ view->top_row = 0; view->left_col = 0; disasm_recompute(view, backpc, 0, view->total_rows, original_cpunum); } 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_rows; row++) { UINT32 effrow = view->top_row + row; if (effrow >= dasmdata->allocated_rows) break; if (pcbyte == dasmdata->address[effrow]) { /* see if we changed */ int changed = disasm_recompute(view, pc, effrow, 1, original_cpunum); if (changed && !recomputed_this_time) { dasmdata->recompute = TRUE; goto recompute; } /* set the effective row and PC */ view->cursor_row = effrow; dasmdata->active_address = pcbyte; } } dasmdata->last_pcbyte = pcbyte; } /* loop over visible rows */ for (row = 0; row < view->visible_rows; row++) { UINT32 effrow = view->top_row + 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_rows) { const char *data = &dasmdata->dasm[effrow * dasmdata->allocated_cols]; UINT32 effcol = view->left_col; UINT32 len = 0; /* if we're on the line with the PC, recompute and hilight it */ if (pcbyte == dasmdata->address[effrow]) attrib = DCA_CURRENT; /* if we're on a line with a breakpoint, tag it changed */ else { for (bp = cpuinfo->first_bp; bp; bp = bp->next) if (dasmdata->address[effrow] == ADDR2BYTE_MASKED(bp->address, cpuinfo, ADDRESS_SPACE_PROGRAM)) attrib = DCA_CHANGED; } /* if we're on the active column and everything is couth, highlight it */ if (view->cursor_visible && effrow == view->cursor_row) { if (dasmdata->active_address == dasmdata->address[effrow]) attrib |= DCA_SELECTED; } /* get the effective string */ len = (UINT32)strlen(data); /* copy data */ while (col < view->visible_cols && 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 == DVP_DASM_RIGHTCOL_COMMENTS) attrib |= DCA_COMMENT; dest++; col++; } } /* fill the rest with blanks */ while (col < view->visible_cols) { dest->byte = ' '; dest->attrib = (effrow < view->total_rows) ? (attrib | DCA_ANCILLARY) : attrib; dest++; col++; } } /* restore the original CPU context */ cpuintrf_pop_context(); } /*------------------------------------------------- disasm_handle_char - handle a character typed within the current view -------------------------------------------------*/ static void disasm_handle_char(debug_view *view, char chval) { debug_view_disasm *dasmdata = view->extra_data; UINT8 end_buffer = 3; INT32 temp; switch (chval) { case DCH_UP: if (view->cursor_row > 0) view->cursor_row--; break; case DCH_DOWN: if (view->cursor_row < view->total_rows - 1) view->cursor_row++; break; case DCH_PUP: temp = view->cursor_row - (view->visible_rows - end_buffer); if (temp < 0) view->cursor_row = 0; else view->cursor_row = temp; break; case DCH_PDOWN: temp = view->cursor_row + (view->visible_rows - end_buffer); if (temp > (view->total_rows - 1)) view->cursor_row = (view->total_rows - 1); else view->cursor_row = temp; break; case DCH_HOME: /* set the active column to the PC */ { const debug_cpu_info *cpuinfo = debug_get_cpu_info(dasmdata->cpunum); offs_t pc = cpunum_get_reg(dasmdata->cpunum, REG_PC); int i; pc = ADDR2BYTE_MASKED(pc, cpuinfo, ADDRESS_SPACE_PROGRAM); /* figure out which row the pc is on */ for (i = 0; i < dasmdata->allocated_rows; i++) { if (dasmdata->address[i] == pc) view->cursor_row = i; } break; } case DCH_CTRLHOME: view->cursor_row = 0; break; case DCH_CTRLEND: view->cursor_row = view->total_rows - 1; break; } /* get the address under the cursor_row */ dasmdata->active_address = dasmdata->address[view->cursor_row]; /* scroll if out of range */ if (view->cursor_row < view->top_row) view->top_row = view->cursor_row; if (view->cursor_row >= view->top_row + view->visible_rows - end_buffer) view->top_row = view->cursor_row - view->visible_rows + end_buffer; } /*------------------------------------------------- disasm_getprop - return the value of a given property -------------------------------------------------*/ static void disasm_getprop(debug_view *view, UINT32 property, debug_property_info *value) { debug_view_disasm *dasmdata = view->extra_data; switch (property) { case DVP_DASM_CPUNUM: value->i = dasmdata->cpunum; break; case DVP_DASM_EXPRESSION: value->s = dasmdata->expression_string; break; case DVP_DASM_TRACK_LIVE: value->i = dasmdata->live_tracking; break; case DVP_DASM_RIGHT_COLUMN: value->i = dasmdata->right_column; break; case DVP_DASM_BACKWARD_STEPS: value->i = dasmdata->backwards_steps; break; case DVP_DASM_WIDTH: value->i = dasmdata->dasm_width; break; case DVP_DASM_ACTIVE_ADDRESS: value->i = dasmdata->active_address; break; default: fatalerror("Attempt to get invalid property %d on debug view type %d", property, view->type); break; } } /*------------------------------------------------- disasm_setprop - set the value of a given property -------------------------------------------------*/ static void disasm_setprop(debug_view *view, UINT32 property, debug_property_info value) { debug_view_disasm *dasmdata = view->extra_data; switch (property) { case DVP_DASM_CPUNUM: if (value.i != dasmdata->cpunum) { debug_view_begin_update(view); dasmdata->cpunum = value.i; /* we need to recompute the expression in the context of the new CPU */ dasmdata->expression_dirty = TRUE; dasmdata->recompute = TRUE; view->update_pending = TRUE; debug_view_end_update(view); } break; case DVP_DASM_EXPRESSION: debug_view_begin_update(view); /* free the old expression string and allocate a new one */ if (dasmdata->expression_string) free(dasmdata->expression_string); dasmdata->expression_string = malloc(strlen(value.s) + 1); if (dasmdata->expression_string) strcpy(dasmdata->expression_string, value.s); /* update everything as a result */ dasmdata->expression_dirty = TRUE; dasmdata->recompute = TRUE; view->update_pending = TRUE; debug_view_end_update(view); break; case DVP_DASM_TRACK_LIVE: if (value.i != dasmdata->live_tracking) { debug_view_begin_update(view); dasmdata->live_tracking = value.i; view->update_pending = TRUE; debug_view_end_update(view); } break; case DVP_DASM_RIGHT_COLUMN: if (value.i != dasmdata->right_column && value.i >= DVP_DASM_RIGHTCOL_NONE && value.i <= DVP_DASM_RIGHTCOL_COMMENTS) { debug_view_begin_update(view); dasmdata->right_column = value.i; dasmdata->recompute = TRUE; view->update_pending = TRUE; debug_view_end_update(view); } break; case DVP_DASM_BACKWARD_STEPS: if (value.i != dasmdata->backwards_steps) { debug_view_begin_update(view); dasmdata->backwards_steps = value.i; dasmdata->recompute = TRUE; view->update_pending = TRUE; debug_view_end_update(view); } break; case DVP_DASM_WIDTH: if (value.i != dasmdata->dasm_width) { debug_view_begin_update(view); dasmdata->dasm_width = value.i; dasmdata->recompute = TRUE; view->update_pending = TRUE; debug_view_end_update(view); } break; case DVP_CHARACTER: debug_view_begin_update(view); disasm_handle_char(view, value.i); view->update_pending = TRUE; debug_view_end_update(view); break; case DVP_DASM_ACTIVE_ADDRESS: debug_view_begin_update(view); dasmdata->active_address = value.i; view->update_pending = TRUE; debug_view_end_update(view); break; default: fatalerror("Attempt to set invalid property %d on debug view type %d", property, view->type); break; } } /*------------------------------------------------- debug_disasm_update_all - force all disasm views to update -------------------------------------------------*/ void debug_disasm_update_all(void) { debug_view *view; /* this is brute force */ for (view = first_view; view != NULL; view = view->next) if (view->type == DVT_DISASSEMBLY) { debug_view_disasm *dasmdata = view->extra_data; debug_view_begin_update(view); view->update_pending = TRUE; dasmdata->recompute = TRUE; dasmdata->last_pcbyte = ~0; debug_view_end_update(view); } } /*************************************************************************** Memory view ***************************************************************************/ /* 00000000 00 11 22 33 44 55 66 77-88 99 aa bb cc dd ee ff 0123456789abcdef 00000000 0011 2233 4455 6677- 8899 aabb ccdd eeff 0123456789abcdef 00000000 00112233 44556677 - 8899aabb ccddeeff 0123456789abcdef 00000000 0011223344556677 - 8899aabbccddeeff 0123456789abcdef */ /*------------------------------------------------- memory_alloc - allocate memory for the memory view -------------------------------------------------*/ static int memory_alloc(debug_view *view) { debug_view_memory *memdata; /* allocate memory */ memdata = malloc(sizeof(*memdata)); if (!memdata) return 0; memset(memdata, 0, sizeof(*memdata)); /* by default we track live */ memdata->live_tracking = TRUE; memdata->ascii_view = TRUE; /* stash the extra data pointer */ view->extra_data = memdata; /* we support cursors */ view->supports_cursor = TRUE; /* start out with 16 bytes in a single column */ memdata->bytes_per_chunk = 1; memdata->chunks_displayed = 16; return 1; } /*------------------------------------------------- memory_free - free memory for the memory view -------------------------------------------------*/ static void memory_free(debug_view *view) { debug_view_memory *memdata = view->extra_data; /* free any memory we callocated */ if (memdata) { if (memdata->expression) expression_free(memdata->expression); if (memdata->expression_string) free(memdata->expression_string); free(memdata); } view->extra_data = NULL; } /*------------------------------------------------- memory_get_cursor_pos - return the cursor position as an address and a shift value -------------------------------------------------*/ static int memory_get_cursor_pos(debug_view *view, offs_t *address, UINT8 *shift) { debug_view_memory *memdata = view->extra_data; int curx = view->cursor_col, cury = view->cursor_row; UINT32 bytes_per_row; int modval; /* if not in the middle section, punt */ if (curx <= memdata->divider1) curx = memdata->divider1 + 1; if (curx >= memdata->divider2) curx = memdata->divider2 - 1; curx -= memdata->divider1; /* compute the base address */ bytes_per_row = memdata->chunks_displayed * memdata->bytes_per_chunk; *address = bytes_per_row * cury + memdata->byte_offset; /* the rest depends on the current format */ switch (memdata->bytes_per_chunk) { default: case 1: modval = curx % 3; if (modval == 0) modval = 1; modval -= 1; if (!memdata->reverse_view) *address += curx / 3; else *address += 15 - curx / 3; *shift = 8-4 - 4 * modval; break; case 2: modval = curx % 6; if (modval <= 1) modval = 2; modval -= 2; if (!memdata->reverse_view) *address += 2 * (curx / 6); else *address += 14 - 2 * (curx / 6); *shift = 16-4 - 4 * modval; break; case 4: modval = curx % 12; if (modval <= 2) modval = 3; if (modval == 11) modval = 10; modval -= 3; if (!memdata->reverse_view) *address += 4 * (curx / 12); else *address += 12 - 4 * (curx / 12); *shift = 32-4 - 4 * modval; break; case 8: modval = curx % 24; if (modval <= 4) modval = 5; if (modval >= 21) modval = 20; modval -= 5; if (!memdata->reverse_view) *address += 8 * (curx / 24); else *address += 8 - 8 * (curx / 24); *shift = 64-4 - 4 * modval; break; } return 1; } /*------------------------------------------------- memory_set_cursor_pos - set the cursor position as a function of an address and a shift value -------------------------------------------------*/ static void memory_set_cursor_pos(debug_view *view, offs_t address, UINT8 shift) { debug_view_memory *memdata = view->extra_data; UINT32 bytes_per_row; int curx, cury; UINT8 end_buffer = 2; /* offset the address by the byte offset */ address -= memdata->byte_offset; /* compute the y coordinate */ bytes_per_row = memdata->chunks_displayed * memdata->bytes_per_chunk; cury = address / bytes_per_row; /* the rest depends on the current format */ /* non-reverse case */ if (!memdata->reverse_view) { switch (memdata->bytes_per_chunk) { default: case 1: curx = memdata->divider1 + 1 + 3 * (address % bytes_per_row) + (1 - (shift / 4)); break; case 2: curx = memdata->divider1 + 2 + 6 * ((address % bytes_per_row) / 2) + (3 - (shift / 4)); break; case 4: curx = memdata->divider1 + 3 + 12 * ((address % bytes_per_row) / 4) + (7 - (shift / 4)); break; case 8: curx = memdata->divider1 + 5 + 24 * ((address % bytes_per_row) / 8) + (15 - (shift / 4)); break; } } else { switch (memdata->bytes_per_chunk) { default: case 1: curx = memdata->divider1 + 1 + 3 * (15 - address % bytes_per_row) + (1 - (shift / 4)); break; case 2: curx = memdata->divider1 + 2 + 6 * (7 - (address % bytes_per_row) / 2) + (3 - (shift / 4)); break; case 4: curx = memdata->divider1 + 3 + 12 * (3 - (address % bytes_per_row) / 4) + (7 - (shift / 4)); break; case 8: curx = memdata->divider1 + 5 + 24 * (1 - (address % bytes_per_row) / 8) + (15 - (shift / 4)); break; } } /* set the position, clamping to the window bounds */ view->cursor_col = (curx < 0) ? 0 : (curx >= view->total_cols) ? (view->total_cols - end_buffer) : curx; view->cursor_row = (cury < 0) ? 0 : (cury >= view->total_rows) ? (view->total_rows - end_buffer) : cury; /* scroll if out of range */ if (view->cursor_row < view->top_row) view->top_row = view->cursor_row; if (view->cursor_row >= view->top_row + view->visible_rows - end_buffer) view->top_row = view->cursor_row - view->visible_rows + end_buffer; } /*------------------------------------------------- memory_read_byte - generic byte reader -------------------------------------------------*/ static UINT8 memory_read_byte(debug_view_memory *memdata, offs_t offs, int apply_translation) { /* if no raw data, just use the standard debug routines */ if (memdata->raw_base == NULL) return debug_read_byte(memdata->spacenum, offs, apply_translation); /* all 0xff if out of bounds */ offs ^= memdata->raw_offset_xor; if (offs >= memdata->raw_length) return 0xff; return *((UINT8 *)memdata->raw_base + offs); } /*------------------------------------------------- memory_read_word - generic word reader -------------------------------------------------*/ static UINT16 memory_read_word(debug_view_memory *memdata, offs_t offs, int apply_translation) { /* if no raw data, just use the standard debug routines */ if (memdata->raw_base == NULL) return debug_read_word(memdata->spacenum, offs, apply_translation); /* otherwise, decompose into bytes */ if (memdata->raw_little_endian) return memory_read_byte(memdata, offs + 0, apply_translation) | (memory_read_byte(memdata, offs + 1, apply_translation) << 8); else return memory_read_byte(memdata, offs + 1, apply_translation) | (memory_read_byte(memdata, offs + 0, apply_translation) << 8); } /*------------------------------------------------- memory_read_dword - generic dword reader -------------------------------------------------*/ static UINT32 memory_read_dword(debug_view_memory *memdata, offs_t offs, int apply_translation) { /* if no raw data, just use the standard debug routines */ if (memdata->raw_base == NULL) return debug_read_dword(memdata->spacenum, offs, apply_translation); /* otherwise, decompose into words */ if (memdata->raw_little_endian) return memory_read_word(memdata, offs + 0, apply_translation) | (memory_read_word(memdata, offs + 2, apply_translation) << 16); else return memory_read_word(memdata, offs + 2, apply_translation) | (memory_read_word(memdata, offs + 0, apply_translation) << 16); } /*------------------------------------------------- memory_read_qword - generic qword reader -------------------------------------------------*/ static UINT64 memory_read_qword(debug_view_memory *memdata, offs_t offs, int apply_translation) { /* if no raw data, just use the standard debug routines */ if (memdata->raw_base == NULL) return debug_read_qword(memdata->spacenum, offs, apply_translation); /* otherwise, decompose into dwords */ if (memdata->raw_little_endian) return memory_read_dword(memdata, offs + 0, apply_translation) | ((UINT64)memory_read_dword(memdata, offs + 4, apply_translation) << 32); else return memory_read_dword(memdata, offs + 4, apply_translation) | ((UINT64)memory_read_dword(memdata, offs + 0, apply_translation) << 32); } /*------------------------------------------------- memory_write_byte - generic byte writer -------------------------------------------------*/ static void memory_write_byte(debug_view_memory *memdata, offs_t offs, UINT8 data, int apply_translation) { /* if no raw data, just use the standard debug routines */ if (memdata->raw_base == NULL) { debug_write_byte(memdata->spacenum, offs, data, apply_translation); return; } /* ignore if out of bounds */ offs ^= memdata->raw_offset_xor; if (offs >= memdata->raw_length) return; *((UINT8 *)memdata->raw_base + offs) = data; /* hack for FD1094 editing */ #ifdef FD1094_HACK if (memdata->raw_base == memory_region(REGION_USER2)) { extern void fd1094_regenerate_key(void); fd1094_regenerate_key(); } #endif } /*------------------------------------------------- memory_write_word - generic word writer -------------------------------------------------*/ static void memory_write_word(debug_view_memory *memdata, offs_t offs, UINT16 data, int apply_translation) { /* if no raw data, just use the standard debug routines */ if (memdata->raw_base == NULL) { debug_write_word(memdata->spacenum, offs, data, apply_translation); return; } /* otherwise, decompose into bytes */ if (memdata->raw_little_endian) { memory_write_byte(memdata, offs + 0, data, apply_translation); memory_write_byte(memdata, offs + 1, data >> 8, apply_translation); } else { memory_write_byte(memdata, offs + 1, data, apply_translation); memory_write_byte(memdata, offs + 0, data >> 8, apply_translation); } } /*------------------------------------------------- memory_write_dword - generic dword writer -------------------------------------------------*/ static void memory_write_dword(debug_view_memory *memdata, offs_t offs, UINT32 data, int apply_translation) { /* if no raw data, just use the standard debug routines */ if (memdata->raw_base == NULL) { debug_write_dword(memdata->spacenum, offs, data, apply_translation); return; } /* otherwise, decompose into words */ if (memdata->raw_little_endian) { memory_write_word(memdata, offs + 0, data, apply_translation); memory_write_word(memdata, offs + 2, data >> 16, apply_translation); } else { memory_write_word(memdata, offs + 2, data, apply_translation); memory_write_word(memdata, offs + 0, data >> 16, apply_translation); } } /*------------------------------------------------- memory_write_qword - generic qword writer -------------------------------------------------*/ static void memory_write_qword(debug_view_memory *memdata, offs_t offs, UINT64 data, int apply_translation) { /* if no raw data, just use the standard debug routines */ if (memdata->raw_base == NULL) { debug_write_qword(memdata->spacenum, offs, data, apply_translation); return; } /* otherwise, decompose into dwords */ if (memdata->raw_little_endian) { memory_write_dword(memdata, offs + 0, data, apply_translation); memory_write_dword(memdata, offs + 4, data >> 32, apply_translation); } else { memory_write_dword(memdata, offs + 4, data, apply_translation); memory_write_dword(memdata, offs + 0, data >> 32, apply_translation); } } /*------------------------------------------------- memory_handle_char - handle a character typed within the current view -------------------------------------------------*/ static void memory_handle_char(debug_view *view, char chval) { debug_view_memory *memdata = view->extra_data; const debug_cpu_info *cpuinfo = debug_get_cpu_info(memdata->cpunum); static const char hexvals[] = "0123456789abcdef"; char *hexchar = strchr(hexvals, tolower(chval)); UINT32 bytes_per_row; UINT32 tempaddr; offs_t maxaddr; offs_t address; UINT8 shift; /* determine the max address */ maxaddr = memdata->raw_base ? (memdata->raw_length - 1) : cpuinfo->space[memdata->spacenum].logbytemask; /* get the position */ if (!memory_get_cursor_pos(view, &address, &shift)) return; /* handle the incoming key */ /* up/down work the same regardless */ bytes_per_row = memdata->chunks_displayed * memdata->bytes_per_chunk; switch (chval) { case DCH_UP: if (view->cursor_row > 0) address -= bytes_per_row; break; case DCH_DOWN: if (view->cursor_row < view->total_rows - 1) address += bytes_per_row; break; case DCH_PUP: tempaddr = address - (bytes_per_row * (view->visible_rows-2)) ; if (tempaddr > address) /* unsigned wraparound */ address = address % bytes_per_row; else address = tempaddr; break; case DCH_PDOWN: tempaddr = address + (bytes_per_row * (view->visible_rows-2)) ; if (tempaddr > maxaddr) address = (maxaddr - (bytes_per_row-1)) + (address % bytes_per_row); else address = tempaddr; break; case DCH_HOME: address -= address % bytes_per_row; shift = (memdata->bytes_per_chunk * 8) - 1; break; case DCH_CTRLHOME: address = 0; shift = (memdata->bytes_per_chunk * 8) - 1; break; case DCH_END: address += (bytes_per_row - (address % bytes_per_row) - 1); shift = 0; break; case DCH_CTRLEND: address = maxaddr; shift = 0; break; case DCH_CTRLRIGHT: if (address < maxaddr-memdata->bytes_per_chunk) address += memdata->bytes_per_chunk; break; case DCH_CTRLLEFT: if (address >= memdata->bytes_per_chunk) address -= memdata->bytes_per_chunk; break; } /* switch off of the current chunk size */ cpuintrf_push_context(memdata->cpunum); switch (memdata->bytes_per_chunk) { default: case 1: if (hexchar) memory_write_byte(memdata, address, (memory_read_byte(memdata, address, !memdata->no_translation) & ~(0xf << shift)) | ((hexchar - hexvals) << shift), !memdata->no_translation); if (hexchar || chval == DCH_RIGHT) { if (shift == 0) { shift = 8-4; if (address != maxaddr) address++; } else shift -= 4; } else if (chval == DCH_LEFT) { if (shift == 8-4) { shift = 0; if (address != 0) address--; } else shift += 4; } break; case 2: if (hexchar) memory_write_word(memdata, address, (memory_read_word(memdata, address, !memdata->no_translation) & ~(0xf << shift)) | ((hexchar - hexvals) << shift), !memdata->no_translation); if (hexchar || chval == DCH_RIGHT) { if (shift == 0) { shift = 16-4; if (address != maxaddr) address += 2; } else shift -= 4; } else if (chval == DCH_LEFT) { if (shift == 16-4) { shift = 0; if (address != 0) address -= 2; } else shift += 4; } break; case 4: if (hexchar) memory_write_dword(memdata, address, (memory_read_dword(memdata, address, !memdata->no_translation) & ~(0xf << shift)) | ((hexchar - hexvals) << shift), !memdata->no_translation); if (hexchar || chval == DCH_RIGHT) { if (shift == 0) { shift = 32-4; if (address != maxaddr) address += 4; } else shift -= 4; } else if (chval == DCH_LEFT) { if (shift == 32-4) { shift = 0; if (address != 0) address -= 4; } else shift += 4; } break; case 8: if (hexchar) memory_write_qword(memdata, address, (memory_read_qword(memdata, address, !memdata->no_translation) & ~((UINT64)0xf << shift)) | ((UINT64)(hexchar - hexvals) << shift), !memdata->no_translation); if (hexchar || chval == DCH_RIGHT) { if (shift == 0) { shift = 64-4; if (address != maxaddr) address += 8; } else shift -= 4; } else if (chval == DCH_LEFT) { if (shift == 64-4) { shift = 0; if (address != 0) address -= 8; } else shift += 4; } break; } cpuintrf_pop_context(); /* set a new position */ memory_set_cursor_pos(view, address, shift); } /*------------------------------------------------- memory_update - update the contents of the register view -------------------------------------------------*/ static void memory_update(debug_view *view) { debug_view_memory *memdata = view->extra_data; const debug_cpu_info *cpuinfo = debug_get_cpu_info(memdata->cpunum); debug_view_char *dest = view->viewdata; char addrformat[16]; EXPRERR exprerr; UINT8 addrchars; UINT32 maxaddr; offs_t addrmask; UINT32 row; UINT32 memory_display_width; UINT32 bytes_per_line; /* switch to the CPU's context */ if (memdata->raw_base == NULL) cpuintrf_push_context(memdata->cpunum); /* determine maximum address and number of characters for that */ if (memdata->raw_base != NULL) { maxaddr = memdata->raw_length - 1; sprintf(addrformat, "%X", maxaddr); addrchars = (UINT8)strlen(addrformat); addrmask = maxaddr; for (row = 0; row < 32; row++) addrmask |= addrmask >> row; } else { maxaddr = cpuinfo->space[memdata->spacenum].logbytemask; addrchars = cpuinfo->space[memdata->spacenum].logchars; addrmask = cpuinfo->space[memdata->spacenum].logbytemask; /* clamp the bytes per chunk */ while (memdata->bytes_per_chunk < (1 << cpuinfo->space[memdata->spacenum].addr2byte_lshift)) { memdata->bytes_per_chunk *= 2; memdata->chunks_displayed /= 2; if (memdata->chunks_displayed == 0) memdata->chunks_displayed = 1; } } /* determine how many characters we need for an address and set the divider */ sprintf(addrformat, " %*s%%0%dX ", 8 - addrchars, "", addrchars); /* determine how wide the memory display area is */ bytes_per_line = memdata->chunks_displayed * memdata->bytes_per_chunk; memory_display_width = 1 + (bytes_per_line * 3) + 1; /* characters + spaces */ /* compute total displayed rows and columns */ view->total_rows = (maxaddr / bytes_per_line) + 1; view->total_cols = (1 + 8 + 1) + memory_display_width; view->total_cols += memdata->ascii_view ? 1 + bytes_per_line : 0 ; /* +1 ??? */ /* 77 : 59; */ /* set up the dividers */ memdata->divider1 = 1 + 8 + 1; memdata->divider2 = memdata->divider1 + memory_display_width; if (memdata->reverse_view) { int temp = view->total_cols + 1 - memdata->divider2; memdata->divider2 = view->total_cols + 1 - memdata->divider1; memdata->divider1 = temp; } /* if our expression is dirty, fix it */ if (memdata->expression_dirty && memdata->expression_string) { parsed_expression *expr; /* parse the new expression */ exprerr = expression_parse(memdata->expression_string, debug_get_cpu_info(memdata->cpunum)->symtable, &expr); /* if it worked, update the expression */ if (exprerr == EXPRERR_NONE) { if (memdata->expression) expression_free(memdata->expression); memdata->expression = expr; memdata->expression_dirty = FALSE; memdata->recompute = TRUE; } } /* if we're tracking a value, make sure it is visible */ if (memdata->expression && (memdata->live_tracking || memdata->recompute)) { UINT64 result; /* recompute the value of the expression */ exprerr = expression_execute(memdata->expression, &result); /* reset the row number */ if (result != memdata->last_result || memdata->expression_dirty || memdata->cpunum != memdata->cpunum) { memdata->last_result = result; if (memdata->raw_base == NULL) result = ADDR2BYTE_MASKED(memdata->last_result, cpuinfo, memdata->spacenum); view->top_row = result / bytes_per_line; memdata->byte_offset = result % bytes_per_line; view->cursor_row = view->top_row; } memdata->recompute = FALSE; } /* loop over visible rows */ for (row = 0; row < view->visible_rows; row++) { UINT32 effrow = view->top_row + row; offs_t addrbyte = effrow * bytes_per_line + memdata->byte_offset; UINT8 attrib = DCA_NORMAL; UINT32 col = 0; /* if this visible row is valid, add it to the buffer */ if (effrow < view->total_rows) { UINT32 effcol = view->left_col; UINT32 len = 0; char data[MEM_MAX_LINE_WIDTH]; int i; /* generate the string */ if (!memdata->reverse_view) { len = sprintf(&data[len], addrformat, BYTE2ADDR(addrbyte & addrmask, cpuinfo, memdata->spacenum)); len += sprintf(&data[len], " "); switch (memdata->bytes_per_chunk) { default: case 1: for (i = 0; i < memdata->chunks_displayed; i++) { offs_t curaddr = addrbyte + i; if (curaddr > maxaddr) len += sprintf(&data[len], " "); else if (memdata->raw_base != NULL || memdata->no_translation || cpuinfo->translate == NULL || (*cpuinfo->translate)(memdata->spacenum, &curaddr)) len += sprintf(&data[len], "%02X ", memory_read_byte(memdata, addrbyte + i, !memdata->no_translation)); else len += sprintf(&data[len], "** "); } break; case 2: for (i = 0; i < memdata->chunks_displayed; i++) { offs_t curaddr = addrbyte + 2 * i; if (curaddr > maxaddr) len += sprintf(&data[len], " "); else if (memdata->raw_base != NULL || memdata->no_translation || cpuinfo->translate == NULL || (*cpuinfo->translate)(memdata->spacenum, &curaddr)) len += sprintf(&data[len], " %04X ", memory_read_word(memdata, addrbyte + 2 * i, !memdata->no_translation)); else len += sprintf(&data[len], " **** "); } break; case 4: for (i = 0; i < memdata->chunks_displayed; i++) { offs_t curaddr = addrbyte + 4 * i; if (curaddr > maxaddr) len += sprintf(&data[len], " "); else if (memdata->raw_base != NULL || memdata->no_translation || cpuinfo->translate == NULL || (*cpuinfo->translate)(memdata->spacenum, &curaddr)) len += sprintf(&data[len], " %08X ", memory_read_dword(memdata, addrbyte + 4 * i, !memdata->no_translation)); else len += sprintf(&data[len], " ******** "); } break; case 8: for (i = 0; i < memdata->chunks_displayed; i++) { offs_t curaddr = addrbyte + 8 * i; if (curaddr > maxaddr) len += sprintf(&data[len], " "); else if (memdata->raw_base != NULL || memdata->no_translation || cpuinfo->translate == NULL || (*cpuinfo->translate)(memdata->spacenum, &curaddr)) { UINT64 qword = memory_read_qword(memdata, addrbyte + 8 * i, !memdata->no_translation); len += sprintf(&data[len], " %08X%08X ", (UINT32)(qword >> 32), (UINT32)qword); } else len += sprintf(&data[len], " **************** "); } break; } len += sprintf(&data[len], " "); if (memdata->ascii_view) { for (i = 0; i < bytes_per_line; i++) { if (addrbyte + i <= maxaddr) { char c = memory_read_byte(memdata, addrbyte + i, !memdata->no_translation); len += sprintf(&data[len], "%c", isprint((UINT8)c) ? c : '.'); } else len += sprintf(&data[len], " "); } len += sprintf(&data[len], " "); } } else { len = sprintf(&data[len], " "); if (memdata->ascii_view) { for (i = 0; i < bytes_per_line; i++) { if (addrbyte + i <= maxaddr) { char c = memory_read_byte(memdata, addrbyte + i, !memdata->no_translation); len += sprintf(&data[len], "%c", isprint((UINT8)c) ? c : '.'); } else len += sprintf(&data[len], " "); } len += sprintf(&data[len], " "); } len += sprintf(&data[len], " "); switch (memdata->bytes_per_chunk) { default: case 1: for (i = memdata->chunks_displayed - 1; i >= 0; i--) { offs_t curaddr = addrbyte + i; if (curaddr > maxaddr) len += sprintf(&data[len], " "); else if (memdata->raw_base != NULL || memdata->no_translation || cpuinfo->translate == NULL || (*cpuinfo->translate)(memdata->spacenum, &curaddr)) len += sprintf(&data[len], "%02X ", memory_read_byte(memdata, addrbyte + i, !memdata->no_translation)); else len += sprintf(&data[len], "** "); } break; case 2: for (i = memdata->chunks_displayed - 1; i >= 0; i--) { offs_t curaddr = addrbyte + 2 * i; if (curaddr > maxaddr) len += sprintf(&data[len], " "); else if (memdata->raw_base != NULL || memdata->no_translation || cpuinfo->translate == NULL || (*cpuinfo->translate)(memdata->spacenum, &curaddr)) len += sprintf(&data[len], " %04X ", memory_read_word(memdata, addrbyte + 2 * i, !memdata->no_translation)); else len += sprintf(&data[len], " **** "); } break; case 4: for (i = memdata->chunks_displayed - 1; i >= 0; i--) { offs_t curaddr = addrbyte + 4 * i; if (curaddr > maxaddr) len += sprintf(&data[len], " "); else if (memdata->raw_base != NULL || memdata->no_translation || cpuinfo->translate == NULL || (*cpuinfo->translate)(memdata->spacenum, &curaddr)) len += sprintf(&data[len], " %08X ", memory_read_dword(memdata, addrbyte + 4 * i, !memdata->no_translation)); else len += sprintf(&data[len], " ******** "); } break; case 8: for (i = memdata->chunks_displayed - 1; i >= 0; i--) { offs_t curaddr = addrbyte + 8 * i; if (curaddr > maxaddr) len += sprintf(&data[len], " "); else if (memdata->raw_base != NULL || memdata->no_translation || cpuinfo->translate == NULL || (*cpuinfo->translate)(memdata->spacenum, &curaddr)) { UINT64 qword = memory_read_qword(memdata, addrbyte + 8 * i, !memdata->no_translation); len += sprintf(&data[len], " %08X%08X ", (UINT32)(qword >> 32), (UINT32)qword); } else len += sprintf(&data[len], " **************** "); } break; } len += sprintf(&data[len], addrformat, BYTE2ADDR(addrbyte & addrmask, cpuinfo, memdata->spacenum)); } /* copy data */ while (col < view->visible_cols && effcol < len) { dest->byte = data[effcol++]; if (effcol <= memdata->divider1 || effcol >= memdata->divider2) dest->attrib = attrib | DCA_ANCILLARY; else if (view->cursor_visible && effcol - 1 == view->cursor_col && effrow == view->cursor_row && dest->byte != ' ') dest->attrib = attrib | DCA_SELECTED; else dest->attrib = attrib; dest++; col++; } } /* fill the rest with blanks */ while (col < view->visible_cols) { dest->byte = ' '; dest->attrib = (effrow < view->total_rows) ? (attrib | DCA_ANCILLARY) : attrib; dest++; col++; } } /* restore the context */ if (memdata->raw_base == NULL) cpuintrf_pop_context(); } /*------------------------------------------------- memory_getprop - return the value of a given property -------------------------------------------------*/ static void memory_getprop(debug_view *view, UINT32 property, debug_property_info *value) { debug_view_memory *memdata = view->extra_data; switch (property) { case DVP_MEM_EXPRESSION: value->s = memdata->expression_string; break; case DVP_MEM_TRACK_LIVE: value->i = memdata->live_tracking; break; case DVP_MEM_NO_TRANSLATION: value->i = memdata->no_translation; break; case DVP_MEM_CPUNUM: value->i = memdata->cpunum; break; case DVP_MEM_SPACENUM: value->i = memdata->spacenum; break; case DVP_MEM_BYTES_PER_CHUNK: value->i = memdata->bytes_per_chunk; break; case DVP_MEM_REVERSE_VIEW: value->i = memdata->reverse_view; break; case DVP_MEM_ASCII_VIEW: value->i = memdata->ascii_view; break; case DVP_MEM_RAW_BASE: value->p = memdata->raw_base; break; case DVP_MEM_RAW_LENGTH: value->i = memdata->raw_length; break; case DVP_MEM_RAW_OFFSET_XOR: value->i = memdata->raw_offset_xor; break; case DVP_MEM_RAW_LITTLE_ENDIAN: value->i = memdata->raw_little_endian; break; case DVP_MEM_WIDTH: value->i = memdata->chunks_displayed; break; default: fatalerror("Attempt to get invalid property %d on debug view type %d", property, view->type); break; } } /*------------------------------------------------- memory_setprop - set the value of a given property -------------------------------------------------*/ static void memory_setprop(debug_view *view, UINT32 property, debug_property_info value) { debug_view_memory *memdata = view->extra_data; switch (property) { case DVP_MEM_EXPRESSION: debug_view_begin_update(view); /* free the old expression and allocate a new copy */ if (memdata->expression_string) free(memdata->expression_string); memdata->expression_string = malloc(strlen(value.s) + 1); if (memdata->expression_string) strcpy(memdata->expression_string, value.s); memdata->expression_dirty = TRUE; view->update_pending = TRUE; debug_view_end_update(view); break; case DVP_MEM_NO_TRANSLATION: if (value.i != memdata->no_translation) { debug_view_begin_update(view); memdata->no_translation = value.i; view->update_pending = TRUE; debug_view_end_update(view); } break; case DVP_MEM_TRACK_LIVE: if (value.i != memdata->live_tracking) { debug_view_begin_update(view); memdata->live_tracking = value.i; view->update_pending = TRUE; debug_view_end_update(view); } break; case DVP_MEM_CPUNUM: if (value.i != memdata->cpunum) { debug_view_begin_update(view); memdata->cpunum = value.i; view->update_pending = TRUE; memdata->expression_dirty = TRUE; debug_view_end_update(view); } break; case DVP_MEM_SPACENUM: if (value.i != memdata->spacenum) { debug_view_begin_update(view); memdata->spacenum = value.i; view->update_pending = TRUE; debug_view_end_update(view); } break; case DVP_MEM_BYTES_PER_CHUNK: if (value.i != memdata->bytes_per_chunk) { debug_view_begin_update(view); /* Change chunks_displayed based on the new bytes_per_chunk */ memdata->chunks_displayed = memdata->chunks_displayed * memdata->bytes_per_chunk / value.i; if (memdata->chunks_displayed <= 0) memdata->chunks_displayed = 1; memdata->bytes_per_chunk = value.i; view->update_pending = TRUE; debug_view_end_update(view); } break; case DVP_MEM_REVERSE_VIEW: if (value.i != memdata->reverse_view) { debug_view_begin_update(view); memdata->reverse_view = value.i; view->update_pending = TRUE; debug_view_end_update(view); } break; case DVP_MEM_ASCII_VIEW: if (value.i != memdata->ascii_view) { debug_view_begin_update(view); memdata->ascii_view = value.i; view->update_pending = TRUE; debug_view_end_update(view); } break; case DVP_MEM_RAW_BASE: if (value.p != memdata->raw_base) { debug_view_begin_update(view); memdata->raw_base = value.p; memdata->expression_dirty = TRUE; view->update_pending = TRUE; debug_view_end_update(view); } break; case DVP_MEM_RAW_LENGTH: if (value.i != memdata->raw_length) { debug_view_begin_update(view); memdata->raw_length = value.i; view->update_pending = TRUE; debug_view_end_update(view); } break; case DVP_MEM_RAW_OFFSET_XOR: if (value.i != memdata->raw_offset_xor) { debug_view_begin_update(view); memdata->raw_offset_xor = value.i; view->update_pending = TRUE; debug_view_end_update(view); } break; case DVP_MEM_RAW_LITTLE_ENDIAN: if (value.i != memdata->raw_little_endian) { debug_view_begin_update(view); memdata->raw_little_endian = value.i; view->update_pending = TRUE; debug_view_end_update(view); } break; case DVP_CHARACTER: debug_view_begin_update(view); memory_handle_char(view, value.i); view->update_pending = TRUE; debug_view_end_update(view); break; case DVP_MEM_WIDTH: if (value.i != memdata->chunks_displayed) { /* lower bounds check - maybe upper bounds check someday? */ if (value.i < 1) break; debug_view_begin_update(view); memdata->chunks_displayed = value.i; view->update_pending = TRUE; debug_view_end_update(view); } break; default: fatalerror("Attempt to set invalid property %d on debug view type %d", property, view->type); break; } }