diff options
Diffstat (limited to 'trunk/src/emu/debug/dvdisasm.c')
-rw-r--r-- | trunk/src/emu/debug/dvdisasm.c | 706 |
1 files changed, 706 insertions, 0 deletions
diff --git a/trunk/src/emu/debug/dvdisasm.c b/trunk/src/emu/debug/dvdisasm.c new file mode 100644 index 00000000000..a8041abc168 --- /dev/null +++ b/trunk/src/emu/debug/dvdisasm.c @@ -0,0 +1,706 @@ +/********************************************************************* + + 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 "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 += dasmsource.m_device.debug()->comment_count(); + } + + // 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(machine(), m_byteaddress); + auto_free(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 + disasm_interface_iterator iter(machine().root_device()); + astring name; + for (device_disasm_interface *dasm = iter.first(); dasm != NULL; dasm = iter.next()) + { + name.printf("%s '%s'", dasm->device().name(), dasm->device().tag()); + m_source_list.append(*auto_alloc(machine(), debug_view_disasm_source(name, dasm->device()))); + } + + // reset the source to a known good entry + set_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(&downcast<const debug_view_disasm_source *>(m_source)->device().debug()->symtable()); +} + + +//------------------------------------------------- +// 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 = source.m_space->address_to_byte(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 = source.m_space->byte_to_address(source.m_disasmintf->min_opcode_bytes()); + if (minlen == 0) minlen = 1; + int maxlen = source.m_space->byte_to_address(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 = source.m_space->address_to_byte(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 = source.m_space->address_to_byte(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 = source.m_space->address_to_byte(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_device.debug()->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); + int char_num = source.is_octal() ? 3 : 2; + // output the first value + int offset = 0; + if (maxchars >= char_num * minbytes) + offset = sprintf(string, "%s", core_i64_format(debug_read_opcode(source.m_space, pcbyte, minbytes, FALSE), minbytes * char_num, source.is_octal())); + + // output subsequent values + int byte; + for (byte = minbytes; byte < numbytes && offset + 1 + char_num * minbytes < maxchars; byte += minbytes) + offset += sprintf(&string[offset], " %s", core_i64_format(debug_read_opcode(source.m_space, pcbyte + byte, minbytes, encrypted), minbytes * char_num, source.is_octal())); + + // if we ran out of room, indicate more + string[maxchars - 1] = 0; + if (byte < numbytes && maxchars > (char_num*2 -1)) + string[maxchars - char_num] = string[maxchars - char_num - 1] = string[maxchars - char_num -2] = '.'; +} + + +//------------------------------------------------- +// 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); + int char_num = source.is_octal() ? 3 : 2; + + // determine how many characters we need for an address and set the divider + m_divider1 = 1 + (source.m_space->logaddrchars()/2*char_num) + 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 &= ~source.m_space->byte_to_address_end(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 + char_num * 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(machine(), m_byteaddress); + m_byteaddress = auto_alloc_array(machine(), offs_t, m_allocated.y); + + // allocate disassembly buffer + auto_free(machine(), m_dasm); + m_dasm = auto_alloc_array(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 = source.m_space->address_to_byte(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_format(source.m_space->byte_to_address(pcbyte), source.m_space->logaddrchars()/2*char_num, source.is_octal())); + + // 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_device.debug()->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 = source.m_space->address_to_byte(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 = source.m_space->byte_to_address(m_byteaddress[instr]); + const char *text = source.m_device.debug()->comment_text(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 = source.m_device.debug()->comment_change_count(); + + // 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 = source.m_space->address_to_byte(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 + UINT64 previous = m_expression.last_value(); + UINT64 result = m_expression.value(); + if (result != previous) + { + offs_t resultbyte = source.m_space->address_to_byte(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 != source.m_device.debug()->comment_change_count()) + 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 != source.m_device.debug()->comment_change_count()) + { + // smoosh us against the left column, but not the top row + m_topleft.x = 0; + + // recompute from where we last recomputed! + recompute(source.m_space->byte_to_address(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 + { + for (device_debug::breakpoint *bp = source.m_device.debug()->breakpoint_first(); bp != NULL; bp = bp->next()) + if (m_byteaddress[effrow] == (source.m_space->address_to_byte(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 downcast<const debug_view_disasm_source &>(*m_source).m_space->byte_to_address(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 = source.m_space->address_to_byte(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; + } +} |