mame/src/emu/debug/dvdisasm.cpp
Vas Crabb 88ce545cdd misc cleanup:
* Got rid of some more simple_list in core debugger code
* Fixed a buffer overrun in wavwrite (buffer half requried size)
* Slightly reduced dependencies and overhead in wavwrite
* Made new disassembly windows in Qt debugger default to current CPU
2019-11-18 05:08:36 +11:00

576 lines
16 KiB
C++

// 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.empty())
throw std::bad_alloc();
// count the number of comments
int total_comments = 0;
for(auto &source : m_source_list)
{
const debug_view_disasm_source &dasmsource = downcast<const debug_view_disasm_source &>(*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.clear();
// 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.emplace_back(std::make_unique<debug_view_disasm_source>(name.c_str(), dasm.device()));
}
// reset the source to a known good entry
if (!m_source_list.empty())
set_source(*m_source_list[0]);
}
//-------------------------------------------------
// 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) && (m_cursor_visible == true))
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;
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<const debug_view_disasm_source &>(*m_source);
offs_t pc = source.device()->state().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<const debug_view_disasm_source &>(*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<device_disasm_interface &>(*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;
}
const debug_view_disasm_source &source = downcast<const debug_view_disasm_source &>(*m_source);
generate_from_address(buffer, m_expression.value() & source.m_space.logaddrmask());
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(const device_debug::breakpoint &bp : source.device()->debug()->breakpoint_list())
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<const debug_view_disasm_source &>(*m_source);
debug_disasm_buffer buffer(*source.device());
offs_t pc = source.device()->state().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<const debug_view_disasm_source &>(*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();
}
}