summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/debug/points.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/debug/points.cpp')
-rw-r--r--src/emu/debug/points.cpp441
1 files changed, 441 insertions, 0 deletions
diff --git a/src/emu/debug/points.cpp b/src/emu/debug/points.cpp
new file mode 100644
index 00000000000..1cde2bbf486
--- /dev/null
+++ b/src/emu/debug/points.cpp
@@ -0,0 +1,441 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ points.cpp
+
+ Debugger breakpoints, watchpoints, etc.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "points.h"
+#include "debugger.h"
+#include "debugcon.h"
+
+
+//**************************************************************************
+// DEBUG BREAKPOINT
+//**************************************************************************
+
+//-------------------------------------------------
+// debug_breakpoint - constructor
+//-------------------------------------------------
+
+debug_breakpoint::debug_breakpoint(device_debug* debugInterface,
+ symbol_table &symbols,
+ int index,
+ offs_t address,
+ const char *condition,
+ const char *action)
+ : m_debugInterface(debugInterface),
+ m_index(index),
+ m_enabled(true),
+ m_address(address),
+ m_condition(symbols, (condition != nullptr) ? condition : "1"),
+ m_action((action != nullptr) ? action : "")
+{
+}
+
+
+//-------------------------------------------------
+// hit - detect a hit
+//-------------------------------------------------
+
+bool debug_breakpoint::hit(offs_t pc)
+{
+ // don't hit if disabled
+ if (!m_enabled)
+ return false;
+
+ // must match our address
+ if (m_address != pc)
+ return false;
+
+ // must satisfy the condition
+ if (!m_condition.is_empty())
+ {
+ try
+ {
+ return (m_condition.execute() != 0);
+ }
+ catch (expression_error &)
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+
+//**************************************************************************
+// DEBUG WATCHPOINT
+//**************************************************************************
+
+//-------------------------------------------------
+// debug_watchpoint - constructor
+//-------------------------------------------------
+
+debug_watchpoint::debug_watchpoint(device_debug* debugInterface,
+ symbol_table &symbols,
+ int index,
+ address_space &space,
+ read_or_write type,
+ offs_t address,
+ offs_t length,
+ const char *condition,
+ const char *action)
+ : m_debugInterface(debugInterface),
+ 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 : ""),
+ m_installing(false)
+{
+ 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);
+
+ int 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>((subamask + 1 + end - rend) * unit_size);
+ else
+ emask &= ~make_bitmask<u64>((rend - end) * 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
+ {
+ if (rstart < rend - subamask)
+ {
+ 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)
+ {
+ install(mode);
+ }
+ });
+}
+
+debug_watchpoint::~debug_watchpoint()
+{
+ m_space.remove_change_notifier(m_notifier);
+ if (m_phr)
+ m_phr->remove();
+ if (m_phw)
+ m_phw->remove();
+}
+
+void debug_watchpoint::setEnabled(bool value)
+{
+ if (m_enabled != value)
+ {
+ m_enabled = value;
+ if (m_enabled)
+ install(read_or_write::READWRITE);
+ else
+ {
+ m_installing = true;
+ if(m_phr)
+ m_phr->remove();
+ if(m_phw)
+ m_phw->remove();
+ m_installing = false;
+ }
+ }
+}
+
+void debug_watchpoint::install(read_or_write mode)
+{
+ if (m_installing)
+ return;
+ m_installing = true;
+ if ((u32(mode) & u32(read_or_write::READ)) && m_phr)
+ m_phr->remove();
+ if ((u32(mode) & u32(read_or_write::WRITE)) && m_phw)
+ m_phw->remove();
+ 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;
+
+ 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;
+ }
+ m_installing = false;
+}
+
+void debug_watchpoint::triggered(read_or_write type, offs_t address, u64 data, u64 mem_mask)
+{
+ running_machine &machine = m_debugInterface->device().machine();
+ debugger_manager &debug = machine.debugger();
+
+ // if we're within debugger code, don't trigger
+ if (debug.cpu().within_instruction_hook() || machine.side_effects_disabled())
+ return;
+
+ // adjust address, size & value_to_write based on mem_mask.
+ offs_t size = 0;
+ int 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
+ {
+ if (!m_condition.execute())
+ {
+ debug.cpu().set_within_instruction(false);
+ return;
+ }
+ }
+ catch (expression_error &)
+ {
+ debug.cpu().set_within_instruction(false);
+ return;
+ }
+ }
+
+ // halt in the debugger by default
+ bool was_stopped = debug.cpu().is_stopped();
+ 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())
+ {
+ std::string buffer;
+
+ buffer = string_format(type == read_or_write::READ ?
+ "Stopped at watchpoint %X reading %0*X from %08X" :
+ "Stopped at watchpoint %X writing %0*X to %08X",
+ m_index,
+ size * unit_size / 4,
+ data,
+ address);
+
+ if (debug.cpu().live_cpu() == &m_space.device())
+ {
+ offs_t pc = m_space.device().state().pcbase();
+ debug.console().printf("%s (PC=%X)\n", buffer, pc);
+ m_debugInterface->compute_debug_flags();
+ }
+ else if (!was_stopped)
+ {
+ debug.console().printf("%s\n", buffer);
+ debug.cpu().set_execution_running();
+ debug.cpu().set_break_cpu(&m_space.device());
+ }
+ m_debugInterface->set_triggered_watchpoint(this);
+ }
+
+ debug.cpu().set_within_instruction(false);
+}
+
+//**************************************************************************
+// DEBUG REGISTERPOINT
+//**************************************************************************
+
+//-------------------------------------------------
+// debug_registerpoint - constructor
+//-------------------------------------------------
+
+debug_registerpoint::debug_registerpoint(symbol_table &symbols, int index, const char *condition, const char *action)
+ : m_index(index),
+ m_enabled(true),
+ m_condition(symbols, (condition != nullptr) ? condition : "1"),
+ m_action((action != nullptr) ? action : "")
+{
+}
+
+
+//-------------------------------------------------
+// hit - detect a hit
+//-------------------------------------------------
+
+bool debug_registerpoint::hit()
+{
+ // don't hit if disabled
+ if (!m_enabled)
+ return false;
+
+ // must satisfy the condition
+ if (!m_condition.is_empty())
+ {
+ try
+ {
+ return (m_condition.execute() != 0);
+ }
+ catch (expression_error &)
+ {
+ return false;
+ }
+ }
+
+ return true;
+}