// license:BSD-3-Clause // copyright-holders:Aaron Giles, Olivier Galibert /********************************************************************* dvdisasm.c Disassembly debugger view. ***************************************************************************/ #include "emu.h" #include "debugvw.h" #include "dvdisasm.h" #include "debugcpu.h" #include "debugger.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_space(device.memory().space(AS_PROGRAM)), m_decrypted_space(device.memory().has_space(AS_OPCODES) ? device.memory().space(AS_OPCODES) : device.memory().space(AS_PROGRAM)) { } //************************************************************************** // DEBUG VIEW DISASM //************************************************************************** const int debug_view_disasm::DEFAULT_DASM_LINES, debug_view_disasm::DEFAULT_DASM_WIDTH, debug_view_disasm::DASM_MAX_BYTES; //------------------------------------------------- // 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_previous_pc(1), m_expression(machine) { // 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) { const debug_view_disasm_source &dasmsource = downcast(source); total_comments += dasmsource.device()->debug()->comment_count(); } // configure the view m_total.y = DEFAULT_DASM_LINES; m_supports_cursor = true; } //------------------------------------------------- // ~debug_view_disasm - destructor //------------------------------------------------- debug_view_disasm::~debug_view_disasm() { } //------------------------------------------------- // 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 std::string name; for(device_disasm_interface &dasm : disasm_interface_iterator(machine().root_device())) { name = string_format("%s '%s'", dasm.device().name(), dasm.device().tag()); if(dasm.device().memory().space_config(AS_PROGRAM)!=nullptr) m_source_list.append(*global_alloc(debug_view_disasm_source(name.c_str(), dasm.device()))); } // reset the source to a known good entry set_source(*m_source_list.first()); } //------------------------------------------------- // 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(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; u8 end_buffer = 3; s32 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(*m_source); offs_t pc = source.device()->safe_pcbase() & source.m_space.logaddrmask(); // figure out which row the pc is on for(unsigned int curline = 0; curline < m_dasm.size(); curline++) if(m_dasm[curline].m_address == 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(); } } //------------------------------------------------- // view_click - handle a mouse click within the // current view //------------------------------------------------- void debug_view_disasm::view_click(const int button, const debug_view_xy& pos) { const debug_view_xy origcursor = m_cursor; m_cursor = pos; /* cursor popup|toggle */ bool cursorVisible = true; if(m_cursor.y == origcursor.y) { cursorVisible = !m_cursor_visible; } /* send a cursor changed notification */ begin_update(); m_cursor_visible = cursorVisible; view_notify(VIEW_NOTIFY_CURSOR_CHANGED); m_update_pending = true; end_update(); } void debug_view_disasm::generate_from_address(debug_disasm_buffer &buffer, offs_t address) { m_dasm.clear(); for(int i=0; i != m_total.y; i++) { std::string dasm; offs_t size; offs_t next_address; u32 info; buffer.disassemble(address, dasm, next_address, size, info); m_dasm.emplace_back(address, size, dasm); address = next_address; } } bool debug_view_disasm::generate_with_pc(debug_disasm_buffer &buffer, offs_t pc) { // Consider that instructions are 64 bytes max const debug_view_disasm_source &source = downcast(*m_source); int shift = source.m_space.addr_shift(); offs_t backwards_offset; if(shift < 0) backwards_offset = 64 >> -shift; else if(shift == 0) backwards_offset = 64; else backwards_offset = 64 << shift; m_dasm.clear(); offs_t address = (pc - m_backwards_steps*backwards_offset) & source.m_space.logaddrmask(); // Handle wrap at 0 if(address > pc) address = 0; util::disasm_interface *intf = dynamic_cast(*source.device()).get_disassembler(); if(intf->interface_flags() & util::disasm_interface::NONLINEAR_PC) { offs_t lpc = intf->pc_real_to_linear(pc); while(intf->pc_real_to_linear(address) < lpc) { std::string dasm; offs_t size; offs_t next_address; u32 info; buffer.disassemble(address, dasm, next_address, size, info); m_dasm.emplace_back(address, size, dasm); if(intf->pc_real_to_linear(address) > intf->pc_real_to_linear(next_address)) return false; address = next_address; } } else { while(address < pc) { std::string dasm; offs_t size; offs_t next_address; u32 info; buffer.disassemble(address, dasm, next_address, size, info); m_dasm.emplace_back(address, size, dasm); if(address > next_address) return false; address = next_address; } } if(address != pc) return false; if(m_dasm.size() > m_backwards_steps) m_dasm.erase(m_dasm.begin(), m_dasm.begin() + (m_dasm.size() - m_backwards_steps)); while(m_dasm.size() < m_total.y) { std::string dasm; offs_t size; offs_t next_address; u32 info; buffer.disassemble(address, dasm, next_address, size, info); m_dasm.emplace_back(address, size, dasm); address = next_address; } return true; } int debug_view_disasm::address_position(offs_t pc) const { for(int i=0; i != int(m_dasm.size()); i++) if(m_dasm[i].m_address == pc) return i; return -1; } void debug_view_disasm::generate_dasm(debug_disasm_buffer &buffer, offs_t pc) { bool pc_changed = pc != m_previous_pc; m_previous_pc = pc; if(strcmp(m_expression.string(), "curpc")) { if(m_expression.dirty()) { m_topleft.x = 0; m_topleft.y = 0; } generate_from_address(buffer, m_expression.value()); return; } if(address_position(pc) != -1) { generate_from_address(buffer, m_dasm[0].m_address); int pos = address_position(pc); if(pos != -1) { if(!pc_changed) return; if(pos >= m_topleft.y && pos < m_topleft.y + m_visible.y - 2) return; if(pos < m_total.y - m_visible.y) { m_topleft.x = 0; m_topleft.y = pos - m_backwards_steps; return; } } } m_topleft.x = 0; m_topleft.y = 0; if(generate_with_pc(buffer, pc)) return; generate_from_address(buffer, pc); } void debug_view_disasm::complete_information(const debug_view_disasm_source &source, debug_disasm_buffer &buffer, offs_t pc) { for(auto &dasm : m_dasm) { offs_t adr = dasm.m_address; dasm.m_tadr = buffer.pc_to_string(adr); dasm.m_topcodes = buffer.data_to_string(adr, dasm.m_size, true); dasm.m_tparams = buffer.data_to_string(adr, dasm.m_size, false); dasm.m_is_pc = adr == pc; dasm.m_is_bp = false; for(device_debug::breakpoint *bp = source.device()->debug()->breakpoint_first(); bp != nullptr; bp = bp->next()) if(adr ==(bp->address() & source.m_space.logaddrmask())) { dasm.m_is_bp = true; break; } dasm.m_is_visited = source.device()->debug()->track_pc_visited(adr); const char *comment = source.device()->debug()->comment_text(adr); if(comment) dasm.m_comment = comment; } } //------------------------------------------------- // view_update - update the contents of the // disassembly view //------------------------------------------------- void debug_view_disasm::view_update() { const debug_view_disasm_source &source = downcast(*m_source); debug_disasm_buffer buffer(*source.device()); offs_t pc = source.device()->safe_pcbase() & source.m_space.logaddrmask(); generate_dasm(buffer, pc); complete_information(source, buffer, pc); redraw(); } //------------------------------------------------- // print - print a string in the disassembly view //------------------------------------------------- void debug_view_disasm::print(int row, std::string text, int start, int end, u8 attrib) { int view_end = end - m_topleft.x; if(view_end < 0) return; int string_0 = start - m_topleft.x; if(string_0 >= m_visible.x) return; int view_start = string_0 > 0 ? string_0 : 0; debug_view_char *dest = &m_viewdata[row * m_visible.x + view_start]; if(view_end >= m_visible.x) view_end = m_visible.x; for(int pos = view_start; pos < view_end; pos++) { int spos = pos - string_0; if(spos >= int(text.size())) *dest++ = { ' ', attrib }; else *dest++ = { u8(text[spos]), attrib }; } } //------------------------------------------------- // redraw - update the view from the data //------------------------------------------------- void debug_view_disasm::redraw() { // determine how many characters we need for an address and set the divider int m_divider1 = 1 + m_dasm[0].m_tadr.size() + 1; // assume a fixed number of characters for the disassembly int m_divider2 = m_divider1 + 1 + m_dasm_width + 1; // set the width of the third column to max comment length m_total.x = m_divider2 + 1 + 50; // DEBUG_COMMENT_MAX_LINE_LENGTH // loop over visible rows for(u32 row = 0; row < m_visible.y; row++) { u32 effrow = m_topleft.y + row; // if this visible row is valid, add it to the buffer u8 attrib = DCA_NORMAL; if(effrow < m_dasm.size()) { // if we're on the line with the PC, hilight it if(m_dasm[effrow].m_is_pc) attrib = DCA_CURRENT; // if we're on a line with a breakpoint, tag it changed else if(m_dasm[effrow].m_is_bp) 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; // if we've visited this pc, mark it as such if(m_dasm[effrow].m_is_visited) attrib |= DCA_VISITED; print(row, ' ' + m_dasm[effrow].m_tadr, 0, m_divider1, attrib | DCA_ANCILLARY); print(row, ' ' + m_dasm[effrow].m_dasm, m_divider1, m_divider2, attrib); if(m_right_column == DASM_RIGHTCOL_RAW || m_right_column == DASM_RIGHTCOL_ENCRYPTED) { std::string text = ' ' +(m_right_column == DASM_RIGHTCOL_RAW ? m_dasm[effrow].m_topcodes : m_dasm[effrow].m_tparams); print(row, text, m_divider2, m_visible.x, attrib | DCA_ANCILLARY); if(int(text.size()) > m_visible.x - m_divider2) { int base = m_total.x - 3; if(base < m_divider2) base = m_divider2; print(row, "...", base, m_visible.x, attrib | DCA_ANCILLARY); } } else if(!m_dasm[effrow].m_comment.empty()) print(row, " // " + m_dasm[effrow].m_comment, m_divider2, m_visible.x, attrib | DCA_COMMENT | DCA_ANCILLARY); else print(row, "", m_divider2, m_visible.x, attrib | DCA_COMMENT | DCA_ANCILLARY); } } } //------------------------------------------------- // selected_address - return the PC of the // currently selected address in the view //------------------------------------------------- offs_t debug_view_disasm::selected_address() { flush_updates(); return m_dasm[m_cursor.y].m_address; } //------------------------------------------------- // set_sexpression - set the expression string // describing the home address //------------------------------------------------- void debug_view_disasm::set_expression(const std::string &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(u32 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(u32 width) { begin_update(); m_dasm_width = width; 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(*m_source); address = address & source.m_space.logaddrmask(); for(int line = 0; line < m_total.y; line++) if(m_dasm[line].m_address == address) { m_cursor.y = line; set_cursor_position(m_cursor); break; } } //------------------------------------------------- // set_source - set the current subview //------------------------------------------------- void debug_view_disasm::set_source(const debug_view_source &source) { if(&source != m_source) { debug_view::set_source(source); m_dasm.clear(); } }