// 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 #include #include //************************************************************************** // 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(&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 ®ion) : 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(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 ®ion : 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 > 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(*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(*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_chunk(address, chunknum, 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(*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(*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(*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(*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(*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; } //------------------------------------------------- // read_chunk - memory view data reader helper //------------------------------------------------- bool debug_view_memory::read_chunk(offs_t address, int chunknum, u64 &chunkdata) { const debug_view_memory_source &source = downcast(*m_source); if (source.m_space) { address += source.m_space->byte_to_address(chunknum * m_bytes_per_chunk); if (!source.m_space->byte_to_address(m_bytes_per_chunk)) { // if chunks are too small to be addressable, read a minimal chunk and split it up u8 minbytes = 1 << -source.m_space->addr_shift(); bool ismapped = read(minbytes, address, chunkdata); u8 suboffset = (chunknum * m_bytes_per_chunk) & (minbytes - 1); chunkdata >>= 8 * (source.m_space->endianness() == ENDIANNESS_LITTLE ? suboffset : minbytes - m_bytes_per_chunk - suboffset); chunkdata &= ~u64(0) >> (64 - 8 * m_bytes_per_chunk); return ismapped; } } else address += chunknum * m_bytes_per_chunk; return read(m_bytes_per_chunk, address, chunkdata); } //------------------------------------------------- // 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(*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(*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); }