diff options
Diffstat (limited to 'src/emu/debug/debugcpu.cpp')
-rw-r--r-- | src/emu/debug/debugcpu.cpp | 569 |
1 files changed, 342 insertions, 227 deletions
diff --git a/src/emu/debug/debugcpu.cpp b/src/emu/debug/debugcpu.cpp index bbf1170b762..cd130c5e3f0 100644 --- a/src/emu/debug/debugcpu.cpp +++ b/src/emu/debug/debugcpu.cpp @@ -1388,6 +1388,20 @@ device_debug::device_debug(device_t &device) device.interface(m_state); device.interface(m_disasm); + // set up notifiers and clear the passthrough handlers + if (m_memory) { + int count = m_memory->max_space_count(); + m_phr.resize(count, nullptr); + m_phw.resize(count, nullptr); + for (int i=0; i != count; i++) + if (m_memory->has_space(i)) { + address_space &space = m_memory->space(i); + m_notifiers.push_back(space.add_change_notifier([this, &space](read_or_write mode) { reinstall(space, mode); })); + } + else + m_notifiers.push_back(-1); + } + // set up state-related stuff if (m_state != nullptr) { @@ -1470,6 +1484,53 @@ device_debug::~device_debug() registerpoint_clear_all(); } +void device_debug::write_tracking(address_space &space, offs_t address, u64 data) +{ + dasm_memory_access const newAccess(space.spacenum(), address, data, history_pc(0)); + std::pair<std::set<dasm_memory_access>::iterator, bool> trackedAccess = m_track_mem_set.insert(newAccess); + if (!trackedAccess.second) + trackedAccess.first->m_pc = newAccess.m_pc; +} + +void device_debug::reinstall(address_space &space, read_or_write mode) +{ + int id = space.spacenum(); + if (u32(mode) & u32(read_or_write::READ)) + { + if (m_phr[id]) + m_phr[id]->remove(); + if (!m_hotspots.empty()) + switch (space.data_width()) + { + case 8: m_phr[id] = space.install_read_tap(0, space.addrmask(), "hotspot", [this, &space](offs_t address, u8 &, u8 ) { hotspot_check(space, address); }, m_phr[id]); break; + case 16: m_phr[id] = space.install_read_tap(0, space.addrmask(), "hotspot", [this, &space](offs_t address, u16 &, u16) { hotspot_check(space, address); }, m_phr[id]); break; + case 32: m_phr[id] = space.install_read_tap(0, space.addrmask(), "hotspot", [this, &space](offs_t address, u32 &, u32) { hotspot_check(space, address); }, m_phr[id]); break; + case 64: m_phr[id] = space.install_read_tap(0, space.addrmask(), "hotspot", [this, &space](offs_t address, u64 &, u64) { hotspot_check(space, address); }, m_phr[id]); break; + } + } + if (u32(mode) & u32(read_or_write::WRITE)) + { + if (m_phw[id]) + m_phw[id]->remove(); + if (m_track_mem) + switch (space.data_width()) + { + case 8: m_phw[id] = space.install_read_tap(0, space.addrmask(), "track_mem", [this, &space](offs_t address, u8 &data, u8 ) { write_tracking(space, address, data); }, m_phw[id]); break; + case 16: m_phw[id] = space.install_read_tap(0, space.addrmask(), "track_mem", [this, &space](offs_t address, u16 &data, u16) { write_tracking(space, address, data); }, m_phw[id]); break; + case 32: m_phw[id] = space.install_read_tap(0, space.addrmask(), "track_mem", [this, &space](offs_t address, u32 &data, u32) { write_tracking(space, address, data); }, m_phw[id]); break; + case 64: m_phw[id] = space.install_read_tap(0, space.addrmask(), "track_mem", [this, &space](offs_t address, u64 &data, u64) { write_tracking(space, address, data); }, m_phw[id]); break; + } + } +} + +void device_debug::reinstall_all(read_or_write mode) +{ + int count = m_memory->max_space_count(); + for (int i=0; i < count; i++) + if (m_memory->has_space(i)) + reinstall(m_memory->space(i), mode); +} + //------------------------------------------------- // start_hook - the scheduler calls this hook // before beginning execution for the given device @@ -1679,42 +1740,6 @@ void device_debug::instruction_hook(offs_t curpc) //------------------------------------------------- -// memory_read_hook - the memory system calls -// this hook when watchpoints are enabled and a -// memory read happens -//------------------------------------------------- - -void device_debug::memory_read_hook(address_space &space, offs_t address, u64 mem_mask) -{ - // check watchpoints - watchpoint_check(space, WATCHPOINT_READ, address, 0, mem_mask); - - // check hotspots - if (!m_hotspots.empty()) - hotspot_check(space, address); -} - - -//------------------------------------------------- -// memory_write_hook - the memory system calls -// this hook when watchpoints are enabled and a -// memory write happens -//------------------------------------------------- - -void device_debug::memory_write_hook(address_space &space, offs_t address, u64 data, u64 mem_mask) -{ - if (m_track_mem) - { - dasm_memory_access const newAccess(space.spacenum(), address, data, history_pc(0)); - std::pair<std::set<dasm_memory_access>::iterator, bool> trackedAccess = m_track_mem_set.insert(newAccess); - if (!trackedAccess.second) - trackedAccess.first->m_pc = newAccess.m_pc; - } - watchpoint_check(space, WATCHPOINT_WRITE, address, data, mem_mask); -} - - -//------------------------------------------------- // set_instruction_hook - set a hook to be // called on each instruction for a given device //------------------------------------------------- @@ -2015,22 +2040,16 @@ void device_debug::breakpoint_enable_all(bool enable) // returning its index //------------------------------------------------- -int device_debug::watchpoint_set(address_space &space, int type, offs_t address, offs_t length, const char *condition, const char *action) +int device_debug::watchpoint_set(address_space &space, read_or_write type, offs_t address, offs_t length, const char *condition, const char *action) { if (space.spacenum() >= int(m_wplist.size())) - m_wplist.resize(space.spacenum()+1, nullptr); + m_wplist.resize(space.spacenum()+1); // allocate a new one u32 id = m_device.machine().debugger().cpu().get_watchpoint_index(); - watchpoint *wp = auto_alloc(m_device.machine(), watchpoint(this, m_symtable, id, space, type, address, length, condition, action)); - - // hook it into our list - wp->m_next = m_wplist[space.spacenum()]; - m_wplist[space.spacenum()] = wp; + m_wplist[space.spacenum()].emplace_back(std::make_unique<watchpoint>(this, m_symtable, id, space, type, address, length, condition, action)); - // update the flags and return the index - watchpoint_update_flags(wp->m_space); - return wp->m_index; + return id; } @@ -2042,17 +2061,15 @@ int device_debug::watchpoint_set(address_space &space, int type, offs_t address, bool device_debug::watchpoint_clear(int index) { // scan the list to see if we own this breakpoint - for (int spacenum = 0; spacenum < int(m_wplist.size()); ++spacenum) - for (watchpoint **wp = &m_wplist[spacenum]; *wp != nullptr; wp = &(*wp)->m_next) - if ((*wp)->m_index == index) + for (auto &wpl : m_wplist) + { + for (auto wpi = wpl.begin(); wpi != wpl.end(); wpi++) + if ((*wpi)->index() == index) { - watchpoint *deleteme = *wp; - address_space &space = deleteme->m_space; - *wp = deleteme->m_next; - auto_free(m_device.machine(), deleteme); - watchpoint_update_flags(space); + wpl.erase(wpi); return true; } + } // we don't own it, return false return false; @@ -2065,10 +2082,8 @@ bool device_debug::watchpoint_clear(int index) void device_debug::watchpoint_clear_all() { - // clear the head until we run out - for (int spacenum = 0; spacenum < int(m_wplist.size()); ++spacenum) - while (m_wplist[spacenum] != nullptr) - watchpoint_clear(m_wplist[spacenum]->index()); + for (auto &wpl : m_wplist) + wpl.clear(); } @@ -2080,12 +2095,11 @@ void device_debug::watchpoint_clear_all() bool device_debug::watchpoint_enable(int index, bool enable) { // scan the list to see if we own this watchpoint - for (int spacenum = 0; spacenum < int(m_wplist.size()); ++spacenum) - for (watchpoint *wp = m_wplist[spacenum]; wp != nullptr; wp = wp->next()) - if (wp->m_index == index) + for (auto &wpl : m_wplist) + for (auto &wp : wpl) + if (wp->index() == index) { - wp->m_enabled = enable; - watchpoint_update_flags(wp->m_space); + wp->setEnabled(enable); return true; } @@ -2102,9 +2116,9 @@ bool device_debug::watchpoint_enable(int index, bool enable) void device_debug::watchpoint_enable_all(bool enable) { // apply the enable to all watchpoints we own - for (int spacenum = 0; spacenum < int(m_wplist.size()); ++spacenum) - for (watchpoint *wp = m_wplist[spacenum]; wp != nullptr; wp = wp->next()) - watchpoint_enable(wp->index(), enable); + for (auto &wpl : m_wplist) + for (auto &wp : wpl) + wp->setEnabled(enable); } @@ -2218,10 +2232,7 @@ void device_debug::hotspot_track(int numspots, int threshhold) // fill in the info m_hotspot_threshhold = threshhold; } - - // update the watchpoint flags to include us - if (m_memory != nullptr && m_memory->has_space(AS_PROGRAM)) - watchpoint_update_flags(m_memory->space(AS_PROGRAM)); + reinstall_all(read_or_write::READ); } @@ -2596,139 +2607,10 @@ void device_debug::breakpoint_check(offs_t pc) //------------------------------------------------- -// watchpoint_update_flags - update the device's -// watchpoint flags -//------------------------------------------------- - -void device_debug::watchpoint_update_flags(address_space &space) -{ - // if hotspots are enabled, turn on all reads - bool enableread = false; - if (!m_hotspots.empty()) - enableread = true; - - // see if there are any enabled breakpoints - bool enablewrite = false; - if (space.spacenum() < int(m_wplist.size())) - for (watchpoint *wp = m_wplist[space.spacenum()]; wp != nullptr; wp = wp->m_next) - if (wp->m_enabled) - { - if (wp->m_type & WATCHPOINT_READ) - enableread = true; - if (wp->m_type & WATCHPOINT_WRITE) - enablewrite = true; - } - - // push the flags out globally - space.enable_read_watchpoints(enableread); - space.enable_write_watchpoints(enablewrite); -} - - -//------------------------------------------------- // watchpoint_check - check the watchpoints // for a given CPU and address space //------------------------------------------------- -void device_debug::watchpoint_check(address_space& space, int type, offs_t address, u64 value_to_write, u64 mem_mask) -{ - m_device.machine().debugger().cpu().watchpoint_check(space, type, address, value_to_write, mem_mask, m_wplist); -} - -void debugger_cpu::watchpoint_check(address_space& space, int type, offs_t address, u64 value_to_write, u64 mem_mask, std::vector<device_debug::watchpoint *> &wplist) -{ - // if we're within debugger code, don't stop - if (m_within_instruction_hook || m_machine.side_effects_disabled()) - return; - - m_within_instruction_hook = true; - - // adjust address, size & value_to_write based on mem_mask. - offs_t size = 0; - if (mem_mask != 0) - { - int bus_size = space.data_width() / 8; - int address_offset = 0; - - while (address_offset < bus_size && (mem_mask & 0xff) == 0) - { - address_offset++; - value_to_write >>= 8; - mem_mask >>= 8; - } - - while (mem_mask != 0) - { - size++; - mem_mask >>= 8; - } - - // (1<<(size*8))-1 won't work when size is 8; let's just use a lut - static const u64 masks[] = { - 0x0U, - 0xffU, - 0xffffU, - 0xffffffU, - 0xffffffffU, - 0xffffffffffU, - 0xffffffffffffU, - 0xffffffffffffffU, - 0xffffffffffffffffU}; - value_to_write &= masks[size]; - - if (space.endianness() == ENDIANNESS_LITTLE) - address += address_offset; - else - address += bus_size - size - address_offset; - } - - // if we are a write watchpoint, stash the value that will be written - m_wpaddr = address; - if (type & WATCHPOINT_WRITE) - m_wpdata = value_to_write; - - // see if we match - if (space.spacenum() < int(wplist.size())) - for (device_debug::watchpoint *wp = wplist[space.spacenum()]; wp != nullptr; wp = wp->next()) - if (wp->hit(type, address, size)) - { - // halt in the debugger by default - m_execution_state = exec_state::STOPPED; - - // if we hit, evaluate the action - if (!wp->action().empty()) - m_machine.debugger().console().execute_command(wp->action(), false); - - // print a notification, unless the action made us go again - if (m_execution_state == exec_state::STOPPED) - { - static const char *const sizes[] = - { - "0bytes", "byte", "word", "3bytes", "dword", "5bytes", "6bytes", "7bytes", "qword" - }; - offs_t pc = space.device().state().pcbase(); - std::string buffer; - - if (type & WATCHPOINT_WRITE) - { - buffer = string_format("Stopped at watchpoint %X writing %s to %08X (PC=%X)", wp->index(), sizes[size], address, pc); - if (value_to_write >> 32) - buffer.append(string_format(" (data=%X%08X)", u32(value_to_write >> 32), u32(value_to_write))); - else - buffer.append(string_format(" (data=%X)", u32(value_to_write))); - } - else - buffer = string_format("Stopped at watchpoint %X reading %s from %08X (PC=%X)", wp->index(), sizes[size], address, pc); - m_machine.debugger().console().printf("%s\n", buffer.c_str()); - space.device().debug()->compute_debug_flags(); - } - break; - } - - m_within_instruction_hook = false; -} - - //------------------------------------------------- // hotspot_check - check for hotspots on a // memory read access @@ -2773,7 +2655,6 @@ void device_debug::hotspot_check(address_space &space, offs_t address) } } - //------------------------------------------------- // get_current_pc - getter callback for a device's // current instruction pointer @@ -2917,59 +2798,293 @@ device_debug::watchpoint::watchpoint(device_debug* debugInterface, symbol_table &symbols, int index, address_space &space, - int type, + read_or_write type, offs_t address, offs_t length, const char *condition, const char *action) : m_debugInterface(debugInterface), - m_next(nullptr), - m_space(space), - m_index(index), - m_enabled(true), - m_type(type), - m_address(address & space.addrmask()), - m_length(length), - m_condition(&symbols, (condition != nullptr) ? condition : "1"), - m_action((action != nullptr) ? action : "") + m_phr(nullptr), + m_phw(nullptr), + m_space(space), + m_index(index), + m_enabled(true), + m_type(type), + m_address(address & space.addrmask()), + m_length(length), + m_condition(&symbols, (condition != nullptr) ? condition : "1"), + m_action((action != nullptr) ? action : "") +{ + std::fill(std::begin(m_start_address), std::end(m_start_address), 0); + std::fill(std::begin(m_end_address), std::end(m_end_address), 0); + std::fill(std::begin(m_masks), std::end(m_masks), 0); + + offs_t ashift = m_space.addr_shift(); + endianness_t endian = m_space.endianness(); + offs_t subamask = m_space.alignment() - 1; + offs_t unit_size = ashift <= 0 ? 8 << -ashift : 8 >> ashift; + offs_t start = m_address; + offs_t end = (m_address + m_length - 1) & space.addrmask(); + if (end < start) + end = space.addrmask(); + offs_t rstart = start & ~subamask; + offs_t rend = end | subamask; + u64 smask, mmask, emask; + smask = mmask = emask = make_bitmask<u64>(m_space.data_width()); + if (start != rstart) + { + if (endian == ENDIANNESS_LITTLE) + smask &= ~make_bitmask<u64>((start - rstart) * unit_size); + else + smask &= make_bitmask<u64>((rstart + subamask + 1 - start) * unit_size); + } + if (end != rend) + { + if (endian == ENDIANNESS_LITTLE) + emask &= make_bitmask<u64>((rend + subamask + 1 - end) * unit_size); + else + emask &= ~make_bitmask<u64>((end - rend) * unit_size); + } + + if (rend == (rstart | subamask) || smask == emask) + { + m_start_address[0] = rstart; + m_end_address[0] = rend; + m_masks[0] = smask & emask; + } + else + { + int idx = 0; + if (smask != mmask) + { + m_start_address[idx] = rstart; + m_end_address[idx] = rstart | subamask; + m_masks[idx] = smask; + idx++; + rstart += subamask + 1; + } + if (mmask == emask) + { + m_start_address[idx] = rstart; + m_end_address[idx] = rend; + m_masks[idx] = emask; + } + else + { + m_start_address[idx] = rstart; + m_end_address[idx] = rend - subamask - 1; + m_masks[idx] = mmask; + idx++; + m_start_address[idx] = rend - subamask; + m_end_address[idx] = rend; + m_masks[idx] = emask; + } + } + + install(read_or_write::READWRITE); + m_notifier = m_space.add_change_notifier([this](read_or_write mode) { + if (m_enabled) + { + if (u32(mode) & u32(read_or_write::READ)) + m_phr->remove(); + if (u32(mode) & u32(read_or_write::WRITE)) + m_phw->remove(); + install(mode); + } + }); +} + +device_debug::watchpoint::~watchpoint() { + m_space.remove_change_notifier(m_notifier); +} + +void device_debug::watchpoint::setEnabled(bool value) +{ + if (m_enabled != value) + { + m_enabled = value; + if (m_enabled) + install(read_or_write::READWRITE); + else + { + m_phr->remove(); + m_phw->remove(); + } + } } +void device_debug::watchpoint::install(read_or_write mode) +{ + std::string name = util::string_format("wp@%x", m_address); + switch (m_space.data_width()) + { + case 8: + if (u32(m_type) & u32(mode) & u32(read_or_write::READ)) + m_phr = m_space.install_read_tap(m_start_address[0], m_end_address[0], name, + [this](offs_t offset, u8 &data, u8 mem_mask) { + triggered(read_or_write::READ, offset, data, mem_mask); + }, m_phr); + if (u32(m_type) & u32(mode) & u32(read_or_write::WRITE)) + m_phw = m_space.install_write_tap(m_start_address[0], m_end_address[0], name, + [this](offs_t offset, u8 &data, u8 mem_mask) { + triggered(read_or_write::WRITE, offset, data, mem_mask); + }, m_phw); + break; -//------------------------------------------------- -// hit - detect a hit -//------------------------------------------------- + case 16: + for (int i=0; i != 3; i++) + if (m_masks[i]) + { + u16 mask = m_masks[i]; + if (u32(m_type) & u32(mode) & u32(read_or_write::READ)) + m_phr = m_space.install_read_tap(m_start_address[i], m_end_address[i], name, + [this, mask](offs_t offset, u16 &data, u16 mem_mask) { + if (mem_mask & mask) + triggered(read_or_write::READ, offset, data, mem_mask); + }, m_phr); + if (u32(m_type) & u32(mode) & u32(read_or_write::WRITE)) + m_phw = m_space.install_write_tap(m_start_address[i], m_end_address[i], name, + [this, mask](offs_t offset, u16 &data, u16 mem_mask) { + if (mem_mask & mask) + triggered(read_or_write::WRITE, offset, data, mem_mask); + }, m_phw); + } + break; + + case 32: + for (int i=0; i != 3; i++) + if (m_masks[i]) + { + u32 mask = m_masks[i]; + if (u32(m_type) & u32(mode) & u32(read_or_write::READ)) + m_phr = m_space.install_read_tap(m_start_address[i], m_end_address[i], name, + [this, mask](offs_t offset, u32 &data, u32 mem_mask) { + if (mem_mask & mask) + triggered(read_or_write::READ, offset, data, mem_mask); + }, m_phr); + if (u32(m_type) & u32(mode) & u32(read_or_write::WRITE)) + m_phw = m_space.install_write_tap(m_start_address[i], m_end_address[i], name, + [this, mask](offs_t offset, u32 &data, u32 mem_mask) { + if (mem_mask & mask) + triggered(read_or_write::WRITE, offset, data, mem_mask); + }, m_phw); + } + break; + + case 64: + for (int i=0; i != 3; i++) + if (m_masks[i]) + { + u64 mask = m_masks[i]; + if (u32(m_type) & u32(mode) & u32(read_or_write::READ)) + m_phr = m_space.install_read_tap(m_start_address[i], m_end_address[i], name, + [this, mask](offs_t offset, u64 &data, u64 mem_mask) { + if (mem_mask & mask) + triggered(read_or_write::READ, offset, data, mem_mask); + }, m_phr); + if (u32(m_type) & u32(mode) & u32(read_or_write::WRITE)) + m_phw = m_space.install_write_tap(m_start_address[i], m_end_address[i], name, + [this, mask](offs_t offset, u64 &data, u64 mem_mask) { + if (mem_mask & mask) + triggered(read_or_write::WRITE, offset, data, mem_mask); + }, m_phw); + } + break; + } +} -bool device_debug::watchpoint::hit(int type, offs_t address, int size) +void device_debug::watchpoint::triggered(read_or_write type, offs_t address, u64 data, u64 mem_mask) { - // don't hit if disabled - if (!m_enabled) - return false; + auto &machine = m_debugInterface->m_device.machine(); + auto &debug = machine.debugger(); - // must match the type - if ((m_type & type) == 0) - return false; + // if we're within debugger code, don't trigger + if (debug.cpu().within_instruction_hook() || machine.side_effects_disabled()) + return; - // must match our address - if (address + size <= m_address || address >= m_address + m_length) - return false; + // adjust address, size & value_to_write based on mem_mask. + offs_t size = 0; + offs_t ashift = m_space.addr_shift(); + offs_t unit_size = ashift <= 0 ? 8 << -ashift : 8 >> ashift; + u64 unit_mask = make_bitmask<u64>(unit_size); + + offs_t address_offset = 0; + + if(!mem_mask) + mem_mask = 0xff; + + while (!(mem_mask & unit_mask)) + { + address_offset++; + data >>= unit_size; + mem_mask >>= unit_size; + } + + while (mem_mask) + { + size++; + mem_mask >>= unit_size; + } + + data &= make_bitmask<u64>(size * unit_size); + + if (m_space.endianness() == ENDIANNESS_LITTLE) + address += address_offset; + else + address += m_space.alignment() - size - address_offset; + + // stash the value that will be written or has just been read + debug.cpu().set_wpinfo(address, data); + + // protect against recursion + debug.cpu().set_within_instruction(true); // must satisfy the condition if (!m_condition.is_empty()) { try { - return (m_condition.execute() != 0); + if (!m_condition.execute() != 0) + { + debug.cpu().set_within_instruction(false); + return; + } } catch (expression_error &) { - return false; + debug.cpu().set_within_instruction(false); + return; } } - return true; -} + // halt in the debugger by default + debug.cpu().set_execution_stopped(); + + // evaluate the action + if (!m_action.empty()) + debug.console().execute_command(m_action, false); + // print a notification, unless the action made us go again + if (debug.cpu().is_stopped()) + { + offs_t pc = m_space.device().state().pcbase(); + std::string buffer; + + buffer = string_format(type == read_or_write::READ ? + "Stopped at watchpoint %X reading %0*X from %08X (PC=%X)" : + "Stopped at watchpoint %X writing %0*X to %08X (PC=%X)", + m_index, + size * unit_size / 4, + data, + address, + pc); + debug.console().printf("%s\n", buffer); + m_debugInterface->compute_debug_flags(); + } + + debug.cpu().set_within_instruction(false); +} //************************************************************************** // DEBUG REGISTERPOINT |