mame/src/emu/debug/dvmemory.cpp

1031 lines
30 KiB
C++

// license:BSD-3-Clause
// copyright-holders:Aaron Giles
/*********************************************************************
dvmemory.c
Memory debugger view.
***************************************************************************/
#include "emu.h"
#include "dvmemory.h"
#include "debugcpu.h"
#include "debugger.h"
#include <algorithm>
#include <ctype.h>
#include <tuple>
//**************************************************************************
// GLOBAL VARIABLES
//**************************************************************************
const debug_view_memory::memory_view_pos debug_view_memory::s_memory_pos_table[12] =
{
/* 0 bytes per chunk: */ { 0, { 0 } },
/* 1 byte per chunk: 00 11 22 33 44 55 66 77 */ { 3, { 0x04, 0x00, 0x80 } },
/* 2 bytes per chunk: 0011 2233 4455 6677 */ { 6, { 0x8c, 0x0c, 0x08, 0x04, 0x00, 0x80 } },
/* 3 bytes per chunk: */ { 0, { 0 } },
/* 4 bytes per chunk: 00112233 44556677 */ { 12, { 0x9c, 0x9c, 0x1c, 0x18, 0x14, 0x10, 0x0c, 0x08, 0x04, 0x00, 0x80, 0x80 } },
/* 5 bytes per chunk: */ { 0, { 0 } },
/* 6 bytes per chunk: */ { 0, { 0 } },
/* 7 bytes per chunk: */ { 0, { 0 } },
/* 8 bytes per chunk: 0011223344556677 */ { 24, { 0xbc, 0xbc, 0xbc, 0xbc, 0x3c, 0x38, 0x34, 0x30, 0x2c, 0x28, 0x24, 0x20, 0x1c, 0x18, 0x14, 0x10, 0x0c, 0x08, 0x04, 0x00, 0x80, 0x80, 0x80, 0x80 } },
/* 32 bit floating point: */ { 16, { 0 } },
/* 64 bit floating point: */ { 32, { 0 } },
/* 80 bit floating point: */ { 32, { 0 } },
};
//**************************************************************************
// DEBUG VIEW MEMORY SOURCE
//**************************************************************************
//-------------------------------------------------
// debug_view_memory_source - constructors
//-------------------------------------------------
debug_view_memory_source::debug_view_memory_source(const char *name, address_space &space)
: debug_view_source(name, &space.device()),
m_space(&space),
m_memintf(dynamic_cast<device_memory_interface *>(&space.device())),
m_base(nullptr),
m_length(0),
m_offsetxor(0),
m_endianness(space.endianness()),
m_prefsize(space.data_width() / 8)
{
}
debug_view_memory_source::debug_view_memory_source(const char *name, memory_region &region)
: debug_view_source(name),
m_space(nullptr),
m_memintf(nullptr),
m_base(region.base()),
m_length(region.bytes()),
m_offsetxor(ENDIAN_VALUE_NE_NNE(region.endianness(), 0, region.bytewidth() - 1)),
m_endianness(region.endianness()),
m_prefsize(std::min<u8>(region.bytewidth(), 8))
{
}
debug_view_memory_source::debug_view_memory_source(const char *name, void *base, int element_size, int num_elements)
: debug_view_source(name),
m_space(nullptr),
m_memintf(nullptr),
m_base(base),
m_length(element_size * num_elements),
m_offsetxor(0),
m_endianness(ENDIANNESS_NATIVE),
m_prefsize(std::min(element_size, 8))
{
}
//**************************************************************************
// DEBUG VIEW MEMORY
//**************************************************************************
//-------------------------------------------------
// debug_view_memory - constructor
//-------------------------------------------------
debug_view_memory::debug_view_memory(running_machine &machine, debug_view_osd_update_func osdupdate, void *osdprivate)
: debug_view(machine, DVT_MEMORY, osdupdate, osdprivate),
m_expression(machine),
m_chunks_per_row(16),
m_bytes_per_chunk(1),
m_steps_per_chunk(1),
m_data_format(1),
m_reverse_view(false),
m_ascii_view(true),
m_no_translation(false),
m_edit_enabled(true),
m_maxaddr(0),
m_bytes_per_row(16),
m_byte_offset(0)
{
// hack: define some sane init values
// that don't hurt the initial computation of top_left
// in set_cursor_pos()
m_section[0].m_pos = 0;
m_section[0].m_width = 1 + 8 + 1;
m_section[1].m_pos = m_section[0].m_pos + m_section[0].m_width;
// fail if no available sources
enumerate_sources();
if (m_source_list.count() == 0)
throw std::bad_alloc();
// configure the view
m_supports_cursor = true;
}
//-------------------------------------------------
// enumerate_sources - enumerate all possible
// sources for a memory view
//-------------------------------------------------
void debug_view_memory::enumerate_sources()
{
// start with an empty list
m_source_list.reset();
std::string name;
// first add all the devices' address spaces
for (device_memory_interface &memintf : memory_interface_iterator(machine().root_device()))
for (int spacenum = 0; spacenum < memintf.max_space_count(); ++spacenum)
if (memintf.has_space(spacenum))
{
address_space &space = memintf.space(spacenum);
name = string_format("%s '%s' %s space memory", memintf.device().name(), memintf.device().tag(), space.name());
m_source_list.append(*global_alloc(debug_view_memory_source(name.c_str(), space)));
}
// then add all the memory regions
for (auto &region : machine().memory().regions())
{
name = string_format("Region '%s'", region.second->name());
m_source_list.append(*global_alloc(debug_view_memory_source(name.c_str(), *region.second.get())));
}
// finally add all global array symbols in alphabetical order
std::vector<std::tuple<std::string, void *, u32, u32> > itemnames;
itemnames.reserve(machine().save().registration_count());
for (int itemnum = 0; itemnum < machine().save().registration_count(); itemnum++)
{
u32 valsize, valcount;
void *base;
std::string name_string(machine().save().indexed_item(itemnum, base, valsize, valcount));
// add pretty much anything that's not a timer (we may wish to cull other items later)
// also, don't trim the front of the name, it's important to know which VIA6522 we're looking at, e.g.
if (strncmp(name_string.c_str(), "timer/", 6))
itemnames.emplace_back(std::move(name_string), base, valsize, valcount);
}
std::sort(itemnames.begin(), itemnames.end(), [] (auto const &x, auto const &y) { return std::get<0>(x) < std::get<0>(y); });
for (auto const &item : itemnames)
m_source_list.append(*global_alloc(debug_view_memory_source(std::get<0>(item).c_str(), std::get<1>(item), std::get<2>(item), std::get<3>(item))));
// 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_memory::view_notify(debug_view_notification type)
{
if (type == VIEW_NOTIFY_CURSOR_CHANGED)
{
// normalize the cursor
set_cursor_pos(get_cursor_pos(m_cursor));
}
else if (type == VIEW_NOTIFY_SOURCE_CHANGED)
{
// update for the new source
const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source);
m_chunks_per_row = m_bytes_per_chunk * m_chunks_per_row / source.m_prefsize;
m_bytes_per_chunk = source.m_prefsize;
if (m_bytes_per_chunk > 8)
m_bytes_per_chunk = 8;
m_data_format = m_bytes_per_chunk;
m_steps_per_chunk = source.m_space ? source.m_space->byte_to_address(m_bytes_per_chunk) : m_bytes_per_chunk;
if (source.m_space != nullptr)
m_expression.set_context(&source.m_space->device().debug()->symtable());
else
m_expression.set_context(nullptr);
}
}
//-------------------------------------------------
// u32_to_float - return a floating point number
// whose 32 bit representation is value
//-------------------------------------------------
static inline float u32_to_float(u32 value)
{
union {
float f;
u32 i;
} v;
v.i = value;
return v.f;
}
//-------------------------------------------------
// u64_to_double - return a floating point number
// whose 64 bit representation is value
//-------------------------------------------------
static inline float u64_to_double(u64 value)
{
union {
double f;
u64 i;
} v;
v.i = value;
return v.f;
}
//-------------------------------------------------
// view_update - update the contents of the
// memory view
//-------------------------------------------------
void debug_view_memory::view_update()
{
const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source);
// if we need to recompute, do it now
if (needs_recompute())
recompute();
// get positional data
const memory_view_pos &posdata = s_memory_pos_table[m_data_format];
// loop over visible rows
for (u32 row = 0; row < m_visible.y; row++)
{
debug_view_char *destmin = &m_viewdata[row * m_visible.x];
debug_view_char *destmax = destmin + m_visible.x;
debug_view_char *destrow = destmin - m_topleft.x;
u32 effrow = m_topleft.y + row;
// reset the line of data; section 1 is normal, others are ancillary, cursor is selected
debug_view_char *dest = destmin;
for (int ch = 0; ch < m_visible.x; ch++, dest++)
{
u32 effcol = m_topleft.x + ch;
dest->byte = ' ';
dest->attrib = DCA_ANCILLARY;
if (m_section[1].contains(effcol))
{
dest->attrib = DCA_NORMAL;
if (m_cursor_visible && effrow == m_cursor.y && effcol == m_cursor.x)
dest->attrib |= DCA_SELECTED;
}
}
// if this visible row is valid, add it to the buffer
if (effrow < m_total.y)
{
offs_t addrbyte = m_byte_offset + effrow * m_bytes_per_row;
offs_t address = (source.m_space != nullptr) ? source.m_space->byte_to_address(addrbyte) : addrbyte;
char addrtext[20];
// generate the address
sprintf(addrtext, m_addrformat.c_str(), address);
dest = destrow + m_section[0].m_pos + 1;
for (int ch = 0; addrtext[ch] != 0 && ch < m_section[0].m_width - 1; ch++, dest++)
if (dest >= destmin && dest < destmax)
dest->byte = addrtext[ch];
// generate the data and the ascii string
std::string chunkascii;
for (int chunknum = 0; chunknum < m_chunks_per_row; chunknum++)
{
int chunkindex = m_reverse_view ? (m_chunks_per_row - 1 - chunknum) : chunknum;
int spacing = posdata.m_spacing;
if (m_data_format <= 8) {
u64 chunkdata;
bool ismapped = read(m_bytes_per_chunk, address + chunknum * m_steps_per_chunk, chunkdata);
dest = destrow + m_section[1].m_pos + 1 + chunkindex * spacing;
for (int ch = 0; ch < posdata.m_spacing; ch++, dest++)
if (dest >= destmin && dest < destmax)
{
u8 shift = posdata.m_shift[ch];
if (shift < 64)
dest->byte = ismapped ? "0123456789ABCDEF"[(chunkdata >> shift) & 0x0f] : '*';
}
for (int i=0; i < m_bytes_per_chunk; i++) {
u8 chval = chunkdata >> (8 * (m_bytes_per_chunk - i - 1));
chunkascii += char((ismapped && isprint(chval)) ? chval : '.');
}
}
else {
int ch;
char valuetext[64];
u64 chunkdata = 0;
floatx80 chunkdata80 = { 0, 0 };
bool ismapped;
if (m_data_format != 11)
ismapped = read(m_bytes_per_chunk, address + chunknum * m_steps_per_chunk, chunkdata);
else
ismapped = read(m_bytes_per_chunk, address + chunknum * m_steps_per_chunk, chunkdata80);
if (ismapped)
switch (m_data_format)
{
case 9:
sprintf(valuetext, "%.8g", u32_to_float(u32(chunkdata)));
break;
case 10:
sprintf(valuetext, "%.24g", u64_to_double(chunkdata));
break;
case 11:
float64 f64 = floatx80_to_float64(chunkdata80);
sprintf(valuetext, "%.24g", u64_to_double(f64));
break;
}
else {
valuetext[0] = '*';
valuetext[1] = 0;
}
dest = destrow + m_section[1].m_pos + 1 + chunkindex * spacing;
// first copy the text
for (ch = 0; (ch < spacing) && (valuetext[ch] != 0); ch++, dest++)
if (dest >= destmin && dest < destmax)
dest->byte = valuetext[ch];
// then fill with spaces
for (; ch < spacing; ch++, dest++)
if (dest >= destmin && dest < destmax)
dest->byte = ' ';
for (int i=0; i < m_bytes_per_chunk; i++) {
u8 chval = chunkdata >> (8 * (m_bytes_per_chunk - i - 1));
chunkascii += char((ismapped && isprint(chval)) ? chval : '.');
}
}
}
// generate the ASCII data, but follow the chunks
if (m_section[2].m_width > 0)
{
dest = destrow + m_section[2].m_pos + 1;
for (size_t i = 0; i != chunkascii.size(); i++) {
if (dest >= destmin && dest < destmax)
dest->byte = chunkascii[i];
dest++;
}
}
}
}
}
//-------------------------------------------------
// view_char - handle a character typed within
// the current view
//-------------------------------------------------
void debug_view_memory::view_char(int chval)
{
// get the position
cursor_pos pos = get_cursor_pos(m_cursor);
// editing is not supported when showing floating point values
if (m_edit_enabled == false)
return;
// handle the incoming key
switch (chval)
{
case DCH_UP:
if (pos.m_address >= m_byte_offset + m_bytes_per_row)
pos.m_address -= m_bytes_per_row;
break;
case DCH_DOWN:
if (pos.m_address <= m_maxaddr - m_bytes_per_row)
pos.m_address += m_bytes_per_row;
break;
case DCH_PUP:
for (u32 delta = (m_visible.y - 2) * m_bytes_per_row; delta > 0; delta -= m_bytes_per_row)
if (pos.m_address >= m_byte_offset + delta)
{
pos.m_address -= delta;
break;
}
break;
case DCH_PDOWN:
for (u32 delta = (m_visible.y - 2) * m_bytes_per_row; delta > 0; delta -= m_bytes_per_row)
if (pos.m_address <= m_maxaddr - delta)
{
pos.m_address += delta;
break;
}
break;
case DCH_HOME:
pos.m_address -= pos.m_address % m_bytes_per_row;
pos.m_shift = (m_bytes_per_chunk * 8) - 4;
break;
case DCH_CTRLHOME:
pos.m_address = m_byte_offset;
pos.m_shift = (m_bytes_per_chunk * 8) - 4;
break;
case DCH_END:
pos.m_address += (m_bytes_per_row - (pos.m_address % m_bytes_per_row) - 1);
pos.m_shift = 0;
break;
case DCH_CTRLEND:
pos.m_address = m_maxaddr;
pos.m_shift = 0;
break;
case DCH_CTRLLEFT:
if (pos.m_address >= m_byte_offset + m_bytes_per_chunk)
pos.m_address -= m_bytes_per_chunk;
break;
case DCH_CTRLRIGHT:
if (pos.m_address <= m_maxaddr - m_bytes_per_chunk)
pos.m_address += m_bytes_per_chunk;
break;
default:
{
static const char hexvals[] = "0123456789abcdef";
char *hexchar = (char *)strchr(hexvals, tolower(chval));
if (hexchar == nullptr)
break;
const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source);
offs_t address = (source.m_space != nullptr) ? source.m_space->byte_to_address(pos.m_address) : pos.m_address;
u64 data;
bool ismapped = read(m_bytes_per_chunk, address, data);
if (!ismapped)
break;
data &= ~(u64(0x0f) << pos.m_shift);
data |= u64(hexchar - hexvals) << pos.m_shift;
write(m_bytes_per_chunk, address, data);
// fall through to the right-arrow press
}
case DCH_RIGHT:
if (pos.m_shift == 0 && pos.m_address != m_maxaddr)
{
pos.m_shift = m_bytes_per_chunk * 8 - 4;
pos.m_address += m_bytes_per_chunk;
}
else
pos.m_shift -= 4;
break;
case DCH_LEFT:
if (pos.m_shift == m_bytes_per_chunk * 8 - 4 && pos.m_address != m_byte_offset)
{
pos.m_shift = 0;
pos.m_address -= m_bytes_per_chunk;
}
else
pos.m_shift += 4;
break;
}
// set a new position
begin_update();
set_cursor_pos(pos);
m_update_pending = true;
end_update();
}
//-------------------------------------------------
// view_click - handle a mouse click within the
// current view
//-------------------------------------------------
void debug_view_memory::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 && m_cursor.x == origcursor.x)
{
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();
}
//-------------------------------------------------
// recompute - recompute the internal data and
// structure of the memory view
//-------------------------------------------------
void debug_view_memory::recompute()
{
const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source);
// get the current cursor position
cursor_pos pos = get_cursor_pos(m_cursor);
// determine the maximum address and address format string from the raw information
int addrchars;
u64 maxbyte;
if (source.m_space != nullptr)
{
m_maxaddr = m_no_translation ? source.m_space->addrmask() : source.m_space->logaddrmask();
maxbyte = source.m_space->address_to_byte_end(m_maxaddr);
addrchars = m_no_translation ? source.m_space->addrchars() : source.m_space->logaddrchars();
}
else
{
maxbyte = m_maxaddr = source.m_length - 1;
addrchars = string_format("%X", m_maxaddr).size();
}
// generate an 8-byte aligned format for the address
if (!m_reverse_view)
m_addrformat = string_format("%*s%%0%dX", 8 - addrchars, "", addrchars);
else
m_addrformat = string_format("%%0%dX%*s", addrchars, 8 - addrchars, "");
// if we are viewing a space with a minimum chunk size, clamp the bytes per chunk
// BAD
#if 0
if (source.m_space != nullptr && source.m_space->byte_to_address(1) > 1)
{
u32 min_bytes_per_chunk = source.m_space->byte_to_address(1);
while (m_bytes_per_chunk < min_bytes_per_chunk)
{
m_bytes_per_chunk *= 2;
m_chunks_per_row /= 2;
}
m_chunks_per_row = std::max(1U, m_chunks_per_row);
}
#endif
// recompute the byte offset based on the most recent expression result
m_bytes_per_row = m_bytes_per_chunk * m_chunks_per_row;
offs_t val = m_expression.value();
if (source.m_space)
val = source.m_space->address_to_byte(val);
m_byte_offset = val % m_bytes_per_row;
// compute the section widths
m_section[0].m_width = 1 + 8 + 1;
if (m_data_format <= 8)
m_section[1].m_width = 1 + 3 * m_bytes_per_row + 1;
else {
const memory_view_pos &posdata = s_memory_pos_table[m_data_format];
m_section[1].m_width = 1 + posdata.m_spacing * m_chunks_per_row + 1;
}
m_section[2].m_width = m_ascii_view ? (1 + m_bytes_per_row + 1) : 0;
// compute the section positions
if (!m_reverse_view)
{
m_section[0].m_pos = 0;
m_section[1].m_pos = m_section[0].m_pos + m_section[0].m_width;
m_section[2].m_pos = m_section[1].m_pos + m_section[1].m_width;
m_total.x = m_section[2].m_pos + m_section[2].m_width;
}
else
{
m_section[2].m_pos = 0;
m_section[1].m_pos = m_section[2].m_pos + m_section[2].m_width;
m_section[0].m_pos = m_section[1].m_pos + m_section[1].m_width;
m_total.x = m_section[0].m_pos + m_section[0].m_width;
}
// derive total sizes from that
m_total.y = (maxbyte - u64(m_byte_offset) + u64(m_bytes_per_row) /*- 1*/) / m_bytes_per_row;
// reset the current cursor position
set_cursor_pos(pos);
}
//-------------------------------------------------
// needs_recompute - determine if anything has
// changed that requires a recomputation
//-------------------------------------------------
bool debug_view_memory::needs_recompute()
{
bool recompute = m_recompute;
// handle expression changes
if (m_expression.dirty())
{
const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source);
offs_t val = m_expression.value();
if (source.m_space)
val = source.m_space->address_to_byte(val & (m_no_translation ? source.m_space->addrmask() : source.m_space->logaddrmask()));
recompute = true;
m_byte_offset = val % m_bytes_per_row;
m_topleft.y = std::min(s32(val / m_bytes_per_row), m_total.y - 1);
set_cursor_pos(cursor_pos(val, m_bytes_per_chunk * 8 - 4));
}
// expression is clean at this point, and future recomputation is not necessary
m_recompute = false;
return recompute;
}
//-------------------------------------------------
// get_cursor_pos - return the cursor position as
// an address and a shift value
//-------------------------------------------------
debug_view_memory::cursor_pos debug_view_memory::get_cursor_pos(const debug_view_xy& cursor)
{
// start with the base address for this row
cursor_pos pos;
const memory_view_pos &posdata = s_memory_pos_table[m_data_format];
pos.m_address = m_byte_offset + cursor.y * m_bytes_per_chunk * m_chunks_per_row;
// determine the X position within the middle section, clamping as necessary
if (m_data_format <= 8) {
int xposition = cursor.x - m_section[1].m_pos - 1;
if (xposition < 0)
xposition = 0;
else if (xposition >= posdata.m_spacing * m_chunks_per_row)
xposition = posdata.m_spacing * m_chunks_per_row - 1;
// compute chunk number and offset within that chunk
int chunknum = xposition / posdata.m_spacing;
int chunkoffs = xposition % posdata.m_spacing;
// reverse the chunknum if we're reversed
if (m_reverse_view)
chunknum = m_chunks_per_row - 1 - chunknum;
// compute the address and shift
pos.m_address += chunknum * m_bytes_per_chunk;
pos.m_shift = posdata.m_shift[chunkoffs] & 0x7f;
}
else {
int xposition = cursor.x - m_section[1].m_pos - 1;
// check for lower limit
if (xposition < 0)
xposition = 0;
int chunknum = xposition / posdata.m_spacing;
// check for upper limit
if (chunknum >= m_chunks_per_row)
chunknum = m_chunks_per_row - 1;
// reverse the chunknum if we're reversed
if (m_reverse_view)
chunknum = m_chunks_per_row - 1 - chunknum;
// compute the address
pos.m_address += chunknum * m_bytes_per_chunk;
pos.m_shift = 0;
}
return pos;
}
//-------------------------------------------------
// set_cursor_pos - set the cursor position as a
// function of an address and a shift value
//-------------------------------------------------
void debug_view_memory::set_cursor_pos(cursor_pos pos)
{
const memory_view_pos &posdata = s_memory_pos_table[m_data_format];
// offset the address by the byte offset
if (pos.m_address < m_byte_offset)
pos.m_address = m_byte_offset;
pos.m_address -= m_byte_offset;
// compute the Y coordinate and chunk index
m_cursor.y = pos.m_address / m_bytes_per_row;
int chunknum = (pos.m_address % m_bytes_per_row) / m_bytes_per_chunk;
// reverse the chunknum if we're reversed
if (m_reverse_view)
chunknum = m_chunks_per_row - 1 - chunknum;
if (m_data_format <= 8) {
// scan within the chunk to find the shift
for (m_cursor.x = 0; m_cursor.x < posdata.m_spacing; m_cursor.x++)
if (posdata.m_shift[m_cursor.x] == pos.m_shift)
break;
// add in the chunk offset and shift to the right of divider1
m_cursor.x += m_section[1].m_pos + 1 + posdata.m_spacing * chunknum;
}
else {
m_cursor.x = m_section[1].m_pos + 1 + posdata.m_spacing * chunknum;
}
// clamp to the window bounds
m_cursor.x = std::min(m_cursor.x, m_total.x);
m_cursor.y = std::min(m_cursor.y, m_total.y);
// scroll if out of range
adjust_visible_x_for_cursor();
adjust_visible_y_for_cursor();
}
//-------------------------------------------------
// read - generic memory view data reader
//-------------------------------------------------
bool debug_view_memory::read(u8 size, offs_t offs, u64 &data)
{
const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source);
// if no raw data, just use the standard debug routines
if (source.m_space)
{
auto dis = machine().disable_side_effects();
bool ismapped = offs <= m_maxaddr;
if (ismapped && !m_no_translation)
{
offs_t dummyaddr = offs;
ismapped = source.m_memintf->translate(source.m_space->spacenum(), TRANSLATE_READ_DEBUG, dummyaddr);
}
data = ~u64(0);
if (ismapped)
{
switch (size)
{
case 1: data = machine().debugger().cpu().read_byte(*source.m_space, offs, !m_no_translation); break;
case 2: data = machine().debugger().cpu().read_word(*source.m_space, offs, !m_no_translation); break;
case 4: data = machine().debugger().cpu().read_dword(*source.m_space, offs, !m_no_translation); break;
case 8: data = machine().debugger().cpu().read_qword(*source.m_space, offs, !m_no_translation); break;
}
}
return ismapped;
}
// if larger than a byte, reduce by half and recurse
if (size > 1)
{
size /= 2;
u64 data0, data1;
bool ismapped = read(size, offs + 0 * size, data0);
ismapped |= read(size, offs + 1 * size, data1);
if (source.m_endianness == ENDIANNESS_LITTLE)
data = data0 | (data1 << (size * 8));
else
data = data1 | (data0 << (size * 8));
return ismapped;
}
// all 0xff if out of bounds
offs ^= source.m_offsetxor;
if (offs >= source.m_length)
return false;
data = *((u8 *)source.m_base + offs);
return true;
}
//-------------------------------------------------
// read - read a 80 bit value
//-------------------------------------------------
bool debug_view_memory::read(u8 size, offs_t offs, floatx80 &data)
{
u64 t;
bool mappedhi, mappedlo;
const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source);
if (source.m_endianness == ENDIANNESS_LITTLE) {
mappedlo = read(8, offs, data.low);
mappedhi = read(2, offs+8, t);
data.high = (bits16)t;
}
else {
mappedhi = read(2, offs, t);
data.high = (bits16)t;
mappedlo = read(8, offs + 2, data.low);
}
return mappedhi && mappedlo;
}
//-------------------------------------------------
// write - generic memory view data writer
//-------------------------------------------------
void debug_view_memory::write(u8 size, offs_t offs, u64 data)
{
const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source);
// if no raw data, just use the standard debug routines
if (source.m_space)
{
auto dis = machine().disable_side_effects();
switch (size)
{
case 1: machine().debugger().cpu().write_byte(*source.m_space, offs, data, !m_no_translation); break;
case 2: machine().debugger().cpu().write_word(*source.m_space, offs, data, !m_no_translation); break;
case 4: machine().debugger().cpu().write_dword(*source.m_space, offs, data, !m_no_translation); break;
case 8: machine().debugger().cpu().write_qword(*source.m_space, offs, data, !m_no_translation); break;
}
return;
}
// if larger than a byte, reduce by half and recurse
if (size > 1)
{
size /= 2;
if (source.m_endianness == ENDIANNESS_LITTLE)
{
write(size, offs + 0 * size, data);
write(size, offs + 1 * size, data >> (8 * size));
}
else
{
write(size, offs + 1 * size, data);
write(size, offs + 0 * size, data >> (8 * size));
}
return;
}
// ignore if out of bounds
offs ^= source.m_offsetxor;
if (offs >= source.m_length)
return;
*((u8 *)source.m_base + offs) = data;
// hack for FD1094 editing
#ifdef FD1094_HACK
if (source.m_base == machine().root_device().memregion("user2"))
{
extern void fd1094_regenerate_key(running_machine &machine);
fd1094_regenerate_key(machine());
}
#endif
}
//-------------------------------------------------
// set_expression - set the expression string
// describing the home address
//-------------------------------------------------
void debug_view_memory::set_expression(const std::string &expression)
{
begin_update();
m_expression.set_string(expression);
m_recompute = m_update_pending = true;
end_update();
}
//-------------------------------------------------
// set_chunks_per_row - specify the number of
// chunks displayed across a row
//-------------------------------------------------
void debug_view_memory::set_chunks_per_row(u32 rowchunks)
{
if (rowchunks < 1)
return;
cursor_pos pos = begin_update_and_get_cursor_pos();
m_chunks_per_row = rowchunks;
m_recompute = m_update_pending = true;
end_update_and_set_cursor_pos(pos);
}
//-------------------------------------------------
// set_data_format - specify what kind of values
// are shown, 1-8 8-64 bits, 9 32bit floating point
//-------------------------------------------------
void debug_view_memory::set_data_format(int format)
{
cursor_pos pos;
// should never be
if ((format <= 0) || (format > 11))
return;
// no need to change
if (format == m_data_format)
return;
pos = begin_update_and_get_cursor_pos();
const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source);
if ((format <= 8) && (m_data_format <= 8)) {
pos.m_address += (pos.m_shift / 8) ^ ((source.m_endianness == ENDIANNESS_LITTLE) ? 0 : (m_bytes_per_chunk - 1));
pos.m_shift %= 8;
m_bytes_per_chunk = format;
m_steps_per_chunk = source.m_space ? source.m_space->byte_to_address(m_bytes_per_chunk) : m_bytes_per_chunk;
m_chunks_per_row = m_bytes_per_row / format;
if (m_chunks_per_row < 1)
m_chunks_per_row = 1;
pos.m_shift += 8 * ((pos.m_address % m_bytes_per_chunk) ^ ((source.m_endianness == ENDIANNESS_LITTLE) ? 0 : (m_bytes_per_chunk - 1)));
pos.m_address -= pos.m_address % m_bytes_per_chunk;
} else {
if (format <= 8) {
m_supports_cursor = true;
m_edit_enabled = true;
m_bytes_per_chunk = format;
}
else {
m_supports_cursor = false;
m_edit_enabled = false;
m_cursor_visible = false;
switch (format)
{
case 9:
m_bytes_per_chunk = 4;
break;
case 10:
m_bytes_per_chunk = 8;
break;
case 11:
m_bytes_per_chunk = 10;
break;
}
}
m_chunks_per_row = m_bytes_per_row / m_bytes_per_chunk;
m_steps_per_chunk = source.m_space ? source.m_space->byte_to_address(m_bytes_per_chunk) : m_bytes_per_chunk;
pos.m_shift = 0;
pos.m_address -= pos.m_address % m_bytes_per_chunk;
}
m_recompute = m_update_pending = true;
m_data_format = format;
end_update_and_set_cursor_pos(pos);
}
//-------------------------------------------------
// set_reverse - specify true if the memory view
// is displayed reverse
//-------------------------------------------------
void debug_view_memory::set_reverse(bool reverse)
{
cursor_pos pos = begin_update_and_get_cursor_pos();
m_reverse_view = reverse;
m_recompute = m_update_pending = true;
end_update_and_set_cursor_pos(pos);
}
//-------------------------------------------------
// set_ascii - specify true if the memory view
// should display an ASCII representation
//-------------------------------------------------
void debug_view_memory::set_ascii(bool ascii)
{
cursor_pos pos = begin_update_and_get_cursor_pos();
m_ascii_view = ascii;
m_recompute = m_update_pending = true;
end_update_and_set_cursor_pos(pos);
}
//-------------------------------------------------
// set_physical - specify true if the memory view
// should display physical addresses versus
// logical addresses
//-------------------------------------------------
void debug_view_memory::set_physical(bool physical)
{
cursor_pos pos = begin_update_and_get_cursor_pos();
m_no_translation = physical;
m_recompute = m_update_pending = true;
end_update_and_set_cursor_pos(pos);
}