mirror of
https://github.com/holub/mame
synced 2025-05-04 13:33:05 +03:00

* 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
576 lines
16 KiB
C++
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();
|
|
}
|
|
}
|