diff options
Diffstat (limited to 'src/emu/debug/debugcpu.cpp')
-rw-r--r-- | src/emu/debug/debugcpu.cpp | 581 |
1 files changed, 378 insertions, 203 deletions
diff --git a/src/emu/debug/debugcpu.cpp b/src/emu/debug/debugcpu.cpp index c3dd9a0c3a6..fd0093bd283 100644 --- a/src/emu/debug/debugcpu.cpp +++ b/src/emu/debug/debugcpu.cpp @@ -19,10 +19,12 @@ #include "debugger.h" #include "emuopts.h" +#include "fileio.h" +#include "main.h" #include "screen.h" #include "uiinput.h" -#include "coreutil.h" +#include "corestr.h" #include "osdepend.h" #include "xmlfile.h" @@ -45,8 +47,10 @@ debugger_cpu::debugger_cpu(running_machine &machine) , m_bpindex(1) , m_wpindex(1) , m_rpindex(1) + , m_epindex(1) , m_wpdata(0) , m_wpaddr(0) + , m_wpsize(0) , m_last_periodic_update_time(0) , m_comments_loaded(false) { @@ -56,13 +60,14 @@ debugger_cpu::debugger_cpu(running_machine &machine) m_symtable = std::make_unique<symbol_table>(machine); m_symtable->set_memory_modified_func([this]() { set_memory_modified(true); }); - /* add "wpaddr", "wpdata" to the global symbol table */ + /* add "wpaddr", "wpdata", "wpsize" to the global symbol table */ m_symtable->add("wpaddr", symbol_table::READ_ONLY, &m_wpaddr); m_symtable->add("wpdata", symbol_table::READ_ONLY, &m_wpdata); + m_symtable->add("wpsize", symbol_table::READ_ONLY, &m_wpsize); - screen_device_iterator screen_iterator = screen_device_iterator(m_machine.root_device()); - screen_device_iterator::auto_iterator iter = screen_iterator.begin(); - const uint32_t count = (uint32_t)screen_iterator.count(); + screen_device_enumerator screen_enumerator = screen_device_enumerator(m_machine.root_device()); + screen_device_enumerator::iterator iter = screen_enumerator.begin(); + const uint32_t count = (uint32_t)screen_enumerator.count(); if (count == 1) { @@ -88,7 +93,7 @@ debugger_cpu::debugger_cpu(running_machine &machine) for (int regnum = 0; regnum < NUM_TEMP_VARIABLES; regnum++) { char symname[10]; - sprintf(symname, "temp%d", regnum); + snprintf(symname, 10, "temp%d", regnum); m_symtable->add(symname, symbol_table::READ_WRITE, &m_tempvar[regnum]); } } @@ -104,7 +109,7 @@ void debugger_cpu::flush_traces() { /* this can be called on exit even when no debugging is enabled, so make sure the devdebug is valid before proceeding */ - for (device_t &device : device_iterator(m_machine.root_device())) + for (device_t &device : device_enumerator(m_machine.root_device())) if (device.debug() != nullptr) device.debug()->trace_flush(); } @@ -146,7 +151,7 @@ bool debugger_cpu::comment_save() // for each device bool found_comments = false; - for (device_t &device : device_iterator(m_machine.root_device())) + for (device_t &device : device_enumerator(m_machine.root_device())) if (device.debug() && device.debug()->comment_count() > 0) { // create a node for this device @@ -165,8 +170,8 @@ bool debugger_cpu::comment_save() if (found_comments) { emu_file file(m_machine.options().comment_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); - osd_file::error filerr = file.open(m_machine.basename() + ".cmt"); - if (filerr == osd_file::error::NONE) + std::error_condition const filerr = file.open(m_machine.basename() + ".cmt"); + if (!filerr) { root->write(file); comments_saved = true; @@ -191,10 +196,10 @@ bool debugger_cpu::comment_load(bool is_inline) { // open the file emu_file file(m_machine.options().comment_directory(), OPEN_FLAG_READ); - osd_file::error filerr = file.open(m_machine.basename() + ".cmt"); + std::error_condition const filerr = file.open(m_machine.basename() + ".cmt"); // if an error, just return false - if (filerr != osd_file::error::NONE) + if (filerr) return false; // wrap in a try/catch to handle errors @@ -272,7 +277,7 @@ void debugger_cpu::on_vblank(screen_device &device, bool vblank_state) void debugger_cpu::reset_transient_flags() { /* loop over CPUs and reset the transient flags */ - for (device_t &device : device_iterator(m_machine.root_device())) + for (device_t &device : device_enumerator(m_machine.root_device())) device.debug()->reset_transient_flag(); m_stop_when_not_device = nullptr; } @@ -382,7 +387,7 @@ void debugger_cpu::go_vblank() m_execution_state = exec_state::RUNNING; } -void debugger_cpu::halt_on_next_instruction(device_t *device, util::format_argument_pack<std::ostream> &&args) +void debugger_cpu::halt_on_next_instruction(device_t *device, util::format_argument_pack<char> &&args) { // if something is pending on this CPU already, ignore this request if (device == m_breakcpu) @@ -420,9 +425,9 @@ device_debug::device_debug(device_t &device) , m_disasm(nullptr) , m_flags(0) , m_symtable(std::make_unique<symbol_table>(device.machine(), &device.machine().debugger().cpu().global_symtable(), &device)) - , m_instrhook(nullptr) , m_stepaddr(0) , m_stepsleft(0) + , m_delay_steps(0) , m_stopaddr(0) , m_stoptime(attotime::zero) , m_stopirq(0) @@ -431,12 +436,13 @@ device_debug::device_debug(device_t &device) , m_total_cycles(0) , m_last_total_cycles(0) , m_pc_history_index(0) + , m_pc_history_valid(0) , m_bplist() - , m_rplist(std::make_unique<std::forward_list<debug_registerpoint>>()) + , m_rplist() + , m_eplist() , m_triggered_breakpoint(nullptr) , m_triggered_watchpoint(nullptr) , m_trace(nullptr) - , m_hotspot_threshhold(0) , m_track_pc_set() , m_track_pc(false) , m_comment_set() @@ -455,15 +461,14 @@ device_debug::device_debug(device_t &device) // 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); + m_phw.resize(count); 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); })); + m_notifiers.emplace_back(space.add_change_notifier([this, &space] (read_or_write mode) { reinstall(space, mode); })); } else - m_notifiers.push_back(-1); + m_notifiers.emplace_back(); } // set up state-related stuff @@ -503,14 +508,13 @@ device_debug::device_debug(device_t &device) } // add all registers into it - std::string tempstr; for (const auto &entry : m_state->state_entries()) { // TODO: floating point registers if (!entry->is_float()) { using namespace std::placeholders; - strmakelower(tempstr.assign(entry->symbol())); + std::string tempstr(strmakelower(entry->symbol())); m_symtable->add( tempstr.c_str(), std::bind(&device_state_entry::value, entry.get()), @@ -546,11 +550,12 @@ device_debug::~device_debug() breakpoint_clear_all(); watchpoint_clear_all(); registerpoint_clear_all(); + exceptionpoint_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)); + dasm_memory_access const newAccess(space.spacenum(), address, data, m_state->pcbase()); 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; @@ -559,30 +564,16 @@ void device_debug::write_tracking(address_space &space, offs_t address, u64 data 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(); + 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; + case 8: m_phw[id] = space.install_write_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_write_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_write_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_write_tap(0, space.addrmask(), "track_mem", [this, &space] (offs_t address, u64 &data, u64) { write_tracking(space, address, data); }, &m_phw[id]); break; } } } @@ -596,6 +587,21 @@ void device_debug::reinstall_all(read_or_write mode) } //------------------------------------------------- +// set_track_mem - start or stop tracking memory +// writes +//------------------------------------------------- + +void device_debug::set_track_mem(bool value) +{ + if (m_track_mem != value) + { + m_track_mem = value; + reinstall_all(read_or_write::WRITE); + } +} + + +//------------------------------------------------- // start_hook - the scheduler calls this hook // before beginning execution for the given device //------------------------------------------------- @@ -630,15 +636,46 @@ void device_debug::stop_hook() // acknowledged //------------------------------------------------- -void device_debug::interrupt_hook(int irqline) +void device_debug::interrupt_hook(int irqline, offs_t pc) { // see if this matches a pending interrupt request if ((m_flags & DEBUG_FLAG_STOP_INTERRUPT) != 0 && (m_stopirq == -1 || m_stopirq == irqline)) { m_device.machine().debugger().cpu().set_execution_stopped(); - m_device.machine().debugger().console().printf("Stopped on interrupt (CPU '%s', IRQ %d)\n", m_device.tag(), irqline); + const address_space &space = m_memory->space(AS_PROGRAM); + if (space.is_octal()) + m_device.machine().debugger().console().printf("Stopped on interrupt (CPU '%s', IRQ %d, PC=%0*o)\n", m_device.tag(), irqline, (space.logaddr_width() + 2) / 3, pc); + else + m_device.machine().debugger().console().printf("Stopped on interrupt (CPU '%s', IRQ %d, PC=%0*X)\n", m_device.tag(), irqline, space.logaddrchars(), pc); compute_debug_flags(); } + + if (m_trace != nullptr) + m_trace->interrupt_update(irqline, pc); + + if ((m_flags & (DEBUG_FLAG_STEPPING_OVER | DEBUG_FLAG_STEPPING_OUT | DEBUG_FLAG_STEPPING_BRANCH)) != 0) + { + if ((m_flags & DEBUG_FLAG_CALL_IN_PROGRESS) == 0) + { + if ((m_flags & DEBUG_FLAG_TEST_IN_PROGRESS) != 0) + { + if ((m_stepaddr == pc && (m_flags & DEBUG_FLAG_STEPPING_BRANCH_FALSE) != 0) || + (m_stepaddr != pc && m_delay_steps == 1 && (m_flags & (DEBUG_FLAG_STEPPING_OUT | DEBUG_FLAG_STEPPING_BRANCH_TRUE)) != 0)) + { + // step over the interrupt and then call it finished + m_flags = (m_flags & ~(DEBUG_FLAG_TEST_IN_PROGRESS | DEBUG_FLAG_STEPPING_ANY)) | DEBUG_FLAG_STEPPING_OVER; + m_stepsleft = 1; + } + } + + // remember the interrupt return address + m_flags |= DEBUG_FLAG_CALL_IN_PROGRESS; + m_stepaddr = pc; + } + + m_flags &= ~DEBUG_FLAG_TEST_IN_PROGRESS; + m_delay_steps = 0; + } } @@ -668,10 +705,39 @@ void device_debug::exception_hook(int exception) if (matched) { m_device.machine().debugger().cpu().set_execution_stopped(); - m_device.machine().debugger().console().printf("Stopped on exception (CPU '%s', exception %d, PC=%X)\n", m_device.tag(), exception, m_state->pcbase()); + m_device.machine().debugger().console().printf("Stopped on exception (CPU '%s', exception %X, PC=%s)\n", m_device.tag(), exception, m_state->state_string(STATE_GENPC)); compute_debug_flags(); } } + + // see if any exception points match + if (!m_eplist.empty()) + { + auto epitp = m_eplist.equal_range(exception); + for (auto epit = epitp.first; epit != epitp.second; ++epit) + { + debug_exceptionpoint &ep = *epit->second; + if (ep.hit(exception)) + { + // halt in the debugger by default + debugger_cpu &debugcpu = m_device.machine().debugger().cpu(); + debugcpu.set_execution_stopped(); + + // if we hit, evaluate the action + if (!ep.m_action.empty()) + m_device.machine().debugger().console().execute_command(ep.m_action, false); + + // print a notification, unless the action made us go again + if (debugcpu.is_stopped()) + { + debugcpu.set_execution_stopped(); + m_device.machine().debugger().console().printf("Stopped at exception point %X (CPU '%s', PC=%s)\n", ep.m_index, m_device.tag(), m_state->state_string(STATE_GENPC)); + compute_debug_flags(); + } + break; + } + } + } } @@ -685,11 +751,11 @@ void device_debug::privilege_hook() if ((m_flags & DEBUG_FLAG_STOP_PRIVILEGE) != 0) { bool matched = true; - if (m_privilege_condition && !m_privilege_condition->is_empty()) + if (m_stop_condition && !m_stop_condition->is_empty()) { try { - matched = m_privilege_condition->execute(); + matched = m_stop_condition->execute(); } catch (expression_error &) { @@ -720,7 +786,10 @@ void device_debug::instruction_hook(offs_t curpc) debugcpu.set_within_instruction(true); // update the history - m_pc_history[m_pc_history_index++ % HISTORY_SIZE] = curpc; + m_pc_history[m_pc_history_index] = curpc; + m_pc_history_index = (m_pc_history_index + 1) % std::size(m_pc_history); + if (std::size(m_pc_history) > m_pc_history_valid) + ++m_pc_history_valid; // update total cycles m_last_total_cycles = m_total_cycles; @@ -737,26 +806,56 @@ void device_debug::instruction_hook(offs_t curpc) if (m_trace != nullptr) m_trace->update(curpc); - // per-instruction hook? - if (!debugcpu.is_stopped() && (m_flags & DEBUG_FLAG_HOOKED) != 0 && (*m_instrhook)(m_device, curpc)) - debugcpu.set_execution_stopped(); - // handle single stepping if (!debugcpu.is_stopped() && (m_flags & DEBUG_FLAG_STEPPING_ANY) != 0) { + bool do_step = true; + if ((m_flags & (DEBUG_FLAG_CALL_IN_PROGRESS | DEBUG_FLAG_TEST_IN_PROGRESS)) != 0) + { + if (curpc == m_stepaddr) + { + if ((~m_flags & (DEBUG_FLAG_TEST_IN_PROGRESS | DEBUG_FLAG_STEPPING_BRANCH_FALSE)) == 0) + { + debugcpu.set_execution_stopped(); + do_step = false; + } + + // reset the breakpoint + m_flags &= ~(DEBUG_FLAG_CALL_IN_PROGRESS | DEBUG_FLAG_TEST_IN_PROGRESS); + m_delay_steps = 0; + } + else if (m_delay_steps != 0) + { + m_delay_steps--; + if (m_delay_steps == 0) + { + // branch taken or subroutine entered (TODO: interleaved multithreading can falsely trigger this) + if ((m_flags & DEBUG_FLAG_TEST_IN_PROGRESS) != 0 && (m_flags & (DEBUG_FLAG_STEPPING_OUT | DEBUG_FLAG_STEPPING_BRANCH_TRUE)) != 0) + { + debugcpu.set_execution_stopped(); + do_step = false; + } + if ((m_flags & DEBUG_FLAG_CALL_IN_PROGRESS) != 0) + do_step = false; + m_flags &= ~DEBUG_FLAG_TEST_IN_PROGRESS; + } + } + else + do_step = false; + } + // is this an actual step? - if (m_stepaddr == ~0 || curpc == m_stepaddr) + if (do_step) { - // decrement the count and reset the breakpoint + // decrement the count m_stepsleft--; - m_stepaddr = ~0; // if we hit 0, stop if (m_stepsleft == 0) debugcpu.set_execution_stopped(); // update every 100 steps until we are within 200 of the end - else if ((m_flags & DEBUG_FLAG_STEPPING_OUT) == 0 && (m_stepsleft < 200 || m_stepsleft % 100 == 0)) + else if ((m_flags & (DEBUG_FLAG_STEPPING_OUT | DEBUG_FLAG_STEPPING_BRANCH_TRUE | DEBUG_FLAG_STEPPING_BRANCH_FALSE)) == 0 && (m_stepsleft < 200 || m_stepsleft % 100 == 0)) { machine.debug_view().update_all(); machine.debug_view().flush_osd_updates(); @@ -778,7 +877,10 @@ void device_debug::instruction_hook(offs_t curpc) // check the temp running breakpoint and break if we hit it else if ((m_flags & DEBUG_FLAG_STOP_PC) != 0 && m_stopaddr == curpc) { - machine.debugger().console().printf("Stopped at temporary breakpoint %X on CPU '%s'\n", m_stopaddr, m_device.tag()); + if (is_octal()) + machine.debugger().console().printf("Stopped at temporary breakpoint %o on CPU '%s'\n", m_stopaddr, m_device.tag()); + else + machine.debugger().console().printf("Stopped at temporary breakpoint %X on CPU '%s'\n", m_stopaddr, m_device.tag()); debugcpu.set_execution_stopped(); } @@ -825,6 +927,7 @@ void device_debug::instruction_hook(offs_t curpc) if (debugcpu.memory_modified()) { machine.debug_view().update_all(DVT_DISASSEMBLY); + machine.debug_view().update_all(DVT_STATE); machine.debugger().refresh_display(); } @@ -842,7 +945,7 @@ void device_debug::instruction_hook(offs_t curpc) } // handle step out/over on the instruction we are about to execute - if ((m_flags & (DEBUG_FLAG_STEPPING_OVER | DEBUG_FLAG_STEPPING_OUT)) != 0 && m_stepaddr == ~0) + if ((m_flags & (DEBUG_FLAG_STEPPING_OVER | DEBUG_FLAG_STEPPING_OUT | DEBUG_FLAG_STEPPING_BRANCH)) != 0 && (m_flags & (DEBUG_FLAG_CALL_IN_PROGRESS | DEBUG_FLAG_TEST_IN_PROGRESS)) == 0) prepare_for_step_overout(m_state->pcbase()); // no longer in debugger code @@ -851,22 +954,6 @@ void device_debug::instruction_hook(offs_t curpc) //------------------------------------------------- -// set_instruction_hook - set a hook to be -// called on each instruction for a given device -//------------------------------------------------- - -void device_debug::set_instruction_hook(debug_instruction_hook_func hook) -{ - // set the hook and also the CPU's flag for fast knowledge of the hook - m_instrhook = hook; - if (hook != nullptr) - m_flags |= DEBUG_FLAG_HOOKED; - else - m_flags &= ~DEBUG_FLAG_HOOKED; -} - - -//------------------------------------------------- // ignore - ignore/observe a given device //------------------------------------------------- @@ -921,8 +1008,9 @@ void device_debug::single_step(int numsteps) m_device.machine().rewind_capture(); m_stepsleft = numsteps; - m_stepaddr = ~0; + m_delay_steps = 0; m_flags |= DEBUG_FLAG_STEPPING; + m_flags &= ~(DEBUG_FLAG_CALL_IN_PROGRESS | DEBUG_FLAG_TEST_IN_PROGRESS); m_device.machine().debugger().cpu().set_execution_running(); } @@ -938,8 +1026,9 @@ void device_debug::single_step_over(int numsteps) m_device.machine().rewind_capture(); m_stepsleft = numsteps; - m_stepaddr = ~0; + m_delay_steps = 0; m_flags |= DEBUG_FLAG_STEPPING_OVER; + m_flags &= ~(DEBUG_FLAG_CALL_IN_PROGRESS | DEBUG_FLAG_TEST_IN_PROGRESS); m_device.machine().debugger().cpu().set_execution_running(); } @@ -954,9 +1043,11 @@ void device_debug::single_step_out() assert(m_exec != nullptr); m_device.machine().rewind_capture(); + m_stop_condition.reset(); m_stepsleft = 100; - m_stepaddr = ~0; + m_delay_steps = 0; m_flags |= DEBUG_FLAG_STEPPING_OUT; + m_flags &= ~(DEBUG_FLAG_CALL_IN_PROGRESS | DEBUG_FLAG_TEST_IN_PROGRESS); m_device.machine().debugger().cpu().set_execution_running(); } @@ -1053,20 +1144,38 @@ void device_debug::go_privilege(const char *condition) { assert(m_exec != nullptr); m_device.machine().rewind_invalidate(); - m_privilege_condition = std::make_unique<parsed_expression>(*m_symtable, condition); + m_stop_condition = std::make_unique<parsed_expression>(*m_symtable, condition); m_flags |= DEBUG_FLAG_STOP_PRIVILEGE; m_device.machine().debugger().cpu().set_execution_running(); } //------------------------------------------------- +// go_branch - execute until branch taken or +// not taken +//------------------------------------------------- + +void device_debug::go_branch(bool sense, const char *condition) +{ + assert(m_exec != nullptr); + m_device.machine().rewind_invalidate(); + m_stop_condition = std::make_unique<parsed_expression>(*m_symtable, condition); + m_stepsleft = 100; + m_delay_steps = 0; + m_flags |= sense ? DEBUG_FLAG_STEPPING_BRANCH_TRUE : DEBUG_FLAG_STEPPING_BRANCH_FALSE; + m_flags &= ~(DEBUG_FLAG_CALL_IN_PROGRESS | DEBUG_FLAG_TEST_IN_PROGRESS); + m_device.machine().debugger().cpu().set_execution_running(); +} + + +//------------------------------------------------- // halt_on_next_instruction_impl - halt in the // debugger on the next instruction, internal // implementation which is necessary solely due // to templates in C++ being janky as all get out //------------------------------------------------- -void device_debug::halt_on_next_instruction_impl(util::format_argument_pack<std::ostream> &&args) +void device_debug::halt_on_next_instruction_impl(util::format_argument_pack<char> &&args) { assert(m_exec != nullptr); m_device.machine().debugger().cpu().halt_on_next_instruction(&m_device, std::move(args)); @@ -1091,7 +1200,7 @@ const debug_breakpoint *device_debug::breakpoint_find(offs_t address) const // returning its index //------------------------------------------------- -int device_debug::breakpoint_set(offs_t address, const char *condition, const char *action) +int device_debug::breakpoint_set(offs_t address, const char *condition, std::string_view action) { // allocate a new one and hook it into our list u32 id = m_device.machine().debugger().cpu().get_breakpoint_index(); @@ -1178,7 +1287,7 @@ void device_debug::breakpoint_enable_all(bool enable) // returning its index //------------------------------------------------- -int device_debug::watchpoint_set(address_space &space, read_or_write 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, std::string_view action) { if (space.spacenum() >= int(m_wplist.size())) m_wplist.resize(space.spacenum()+1); @@ -1265,15 +1374,15 @@ void device_debug::watchpoint_enable_all(bool enable) // returning its index //------------------------------------------------- -int device_debug::registerpoint_set(const char *condition, const char *action) +int device_debug::registerpoint_set(const char *condition, std::string_view action) { // allocate a new one u32 id = m_device.machine().debugger().cpu().get_registerpoint_index(); - m_rplist->emplace_front(*m_symtable, id, condition, action); + m_rplist.emplace_front(*m_symtable, id, condition, action); // update the flags and return the index breakpoint_update_flags(); - return m_rplist->front().m_index; + return m_rplist.front().m_index; } @@ -1285,10 +1394,10 @@ int device_debug::registerpoint_set(const char *condition, const char *action) bool device_debug::registerpoint_clear(int index) { // scan the list to see if we own this registerpoint - for (auto brp = m_rplist->before_begin(); std::next(brp) != m_rplist->end(); ++brp) + for (auto brp = m_rplist.before_begin(); std::next(brp) != m_rplist.end(); ++brp) if (std::next(brp)->m_index == index) { - m_rplist->erase_after(brp); + m_rplist.erase_after(brp); breakpoint_update_flags(); return true; } @@ -1305,7 +1414,7 @@ bool device_debug::registerpoint_clear(int index) void device_debug::registerpoint_clear_all() { // clear the list - m_rplist->clear(); + m_rplist.clear(); breakpoint_update_flags(); } @@ -1318,7 +1427,7 @@ void device_debug::registerpoint_clear_all() bool device_debug::registerpoint_enable(int index, bool enable) { // scan the list to see if we own this conditionpoint - for (debug_registerpoint &rp : *m_rplist) + for (debug_registerpoint &rp : m_rplist) if (rp.m_index == index) { rp.m_enabled = enable; @@ -1339,32 +1448,93 @@ bool device_debug::registerpoint_enable(int index, bool enable) void device_debug::registerpoint_enable_all(bool enable) { // apply the enable to all registerpoints we own - for (debug_registerpoint &rp : *m_rplist) + for (debug_registerpoint &rp : m_rplist) registerpoint_enable(rp.index(), enable); } //------------------------------------------------- -// hotspot_track - enable/disable tracking of -// hotspots +// exceptionpoint_set - set a new exception +// point, returning its index //------------------------------------------------- -void device_debug::hotspot_track(int numspots, int threshhold) +int device_debug::exceptionpoint_set(int exception, const char *condition, std::string_view action) { - // if we already have tracking enabled, kill it - m_hotspots.clear(); + // allocate a new one and hook it into our list + u32 id = m_device.machine().debugger().cpu().get_exceptionpoint_index(); + m_eplist.emplace(exception, std::make_unique<debug_exceptionpoint>(this, *m_symtable, id, exception, condition, action)); - // only start tracking if we have a non-zero count - if (numspots > 0) - { - // allocate memory for hotspots - m_hotspots.resize(numspots); - memset(&m_hotspots[0], 0xff, numspots*sizeof(m_hotspots[0])); + // return the index + return id; +} + + +//------------------------------------------------- +// exceptionpoint_clear - clear an exception +// point by index, returning true if we found it +//------------------------------------------------- + +bool device_debug::exceptionpoint_clear(int index) +{ + // scan the list to see if we own this breakpoint + for (auto epit = m_eplist.begin(); epit != m_eplist.end(); ++epit) + if (epit->second->m_index == index) + { + m_eplist.erase(epit); + return true; + } + + // we don't own it, return false + return false; +} + + +//------------------------------------------------- +// exceptionpoint_clear_all - clear all exception +// points +//------------------------------------------------- + +void device_debug::exceptionpoint_clear_all() +{ + // clear the list + m_eplist.clear(); +} + + +//------------------------------------------------- +// exceptionpoint_enable - enable/disable an +// exception point by index, returning true if we +// found it +//------------------------------------------------- - // fill in the info - m_hotspot_threshhold = threshhold; +bool device_debug::exceptionpoint_enable(int index, bool enable) +{ + // scan the list to see if we own this exception point + for (auto &epp : m_eplist) + { + debug_exceptionpoint &ep = *epp.second; + if (ep.m_index == index) + { + ep.m_enabled = enable; + return true; + } } - reinstall_all(read_or_write::READ); + + // we don't own it, return false + return false; +} + + +//------------------------------------------------- +// exceptionpoint_enable_all - enable/disable all +// exception points +//------------------------------------------------- + +void device_debug::exceptionpoint_enable_all(bool enable) +{ + // apply the enable to all exception points we own + for (auto &epp : m_eplist) + exceptionpoint_enable(epp.second->index(), enable); } @@ -1373,13 +1543,28 @@ void device_debug::hotspot_track(int numspots, int threshhold) // history //------------------------------------------------- -offs_t device_debug::history_pc(int index) const +std::pair<offs_t, bool> device_debug::history_pc(int index) const { - if (index > 0) - index = 0; - if (index <= -HISTORY_SIZE) - index = -HISTORY_SIZE + 1; - return m_pc_history[(m_pc_history_index + ARRAY_LENGTH(m_pc_history) - 1 + index) % ARRAY_LENGTH(m_pc_history)]; + if ((index <= 0) && (-index < m_pc_history_valid)) + { + int const i = (m_pc_history_index + std::size(m_pc_history) - 1 + index) % std::size(m_pc_history); + return std::make_pair(m_pc_history[i], true); + } + else + { + return std::make_pair(offs_t(0), false); + } +} + + +//------------------------------------------------- +// set_track_pc - turn visited PC tracking on or +// off +//------------------------------------------------- + +void device_debug::set_track_pc(bool value) +{ + m_track_pc = value; } @@ -1387,10 +1572,9 @@ offs_t device_debug::history_pc(int index) const // track_pc_visited - returns a boolean stating // if this PC has been visited or not. CRC32 is // done in this function on currently active CPU. -// TODO: Take a CPU context as input //------------------------------------------------- -bool device_debug::track_pc_visited(const offs_t& pc) const +bool device_debug::track_pc_visited(offs_t pc) const { if (m_track_pc_set.empty()) return false; @@ -1401,10 +1585,9 @@ bool device_debug::track_pc_visited(const offs_t& pc) const //------------------------------------------------- // set_track_pc_visited - set this pc as visited. -// TODO: Take a CPU context as input //------------------------------------------------- -void device_debug::set_track_pc_visited(const offs_t& pc) +void device_debug::set_track_pc_visited(offs_t pc) { const u32 crc = compute_opcode_crc32(pc); m_track_pc_set.insert(dasm_pc_tag(pc, crc)); @@ -1543,7 +1726,7 @@ u32 device_debug::compute_opcode_crc32(offs_t pc) const buffer.data_get(pc, dasmresult & util::disasm_interface::LENGTHMASK, true, opbuf); // return a CRC of the exact count of opcode bytes - return core_crc32(0, &opbuf[0], opbuf.size()); + return util::crc32_creator::simple(&opbuf[0], opbuf.size()); } @@ -1551,31 +1734,14 @@ u32 device_debug::compute_opcode_crc32(offs_t pc) const // trace - trace execution of a given device //------------------------------------------------- -void device_debug::trace(FILE *file, bool trace_over, bool detect_loops, bool logerror, const char *action) +void device_debug::trace(std::unique_ptr<std::ostream> &&file, bool trace_over, bool detect_loops, bool logerror, std::string_view action) { // delete any existing tracers m_trace = nullptr; // if we have a new file, make a new tracer if (file != nullptr) - m_trace = std::make_unique<tracer>(*this, *file, trace_over, detect_loops, logerror, action); -} - - -//------------------------------------------------- -// trace_printf - output data into the given -// device's tracefile, if tracing -//------------------------------------------------- - -void device_debug::trace_printf(const char *fmt, ...) -{ - if (m_trace != nullptr) - { - va_list va; - va_start(va, fmt); - m_trace->vprintf(fmt, va); - va_end(va); - } + m_trace = std::make_unique<tracer>(*this, std::move(file), trace_over, detect_loops, logerror, action); } @@ -1603,7 +1769,7 @@ void device_debug::compute_debug_flags() // if we're tracking history, or we're hooked, or stepping, or stopping at a breakpoint // make sure we call the hook - if ((m_flags & (DEBUG_FLAG_HISTORY | DEBUG_FLAG_HOOKED | DEBUG_FLAG_STEPPING_ANY | DEBUG_FLAG_STOP_PC | DEBUG_FLAG_LIVE_BP)) != 0) + if ((m_flags & (DEBUG_FLAG_HISTORY | DEBUG_FLAG_STEPPING_ANY | DEBUG_FLAG_STOP_PC | DEBUG_FLAG_LIVE_BP)) != 0) machine.debug_flags |= DEBUG_FLAG_CALL_HOOK; // also call if we are tracing @@ -1627,12 +1793,32 @@ void device_debug::prepare_for_step_overout(offs_t pc) // disassemble the current instruction and get the flags u32 dasmresult = buffer.disassemble_info(pc); + if ((dasmresult & util::disasm_interface::SUPPORTED) == 0) + return; + + bool step_out = (m_flags & DEBUG_FLAG_STEPPING_OUT) != 0 && (dasmresult & util::disasm_interface::STEP_OUT) != 0; + bool test_cond = (dasmresult & util::disasm_interface::STEP_COND) != 0 && ((m_flags & (DEBUG_FLAG_STEPPING_BRANCH_TRUE | DEBUG_FLAG_STEPPING_BRANCH_FALSE)) != 0 || step_out); + if (test_cond && m_stop_condition && !m_stop_condition->is_empty()) + { + try + { + test_cond = m_stop_condition->execute(); + } + catch (expression_error &) + { + test_cond = false; + } + } // if flags are supported and it's a call-style opcode, set a temp breakpoint after that instruction - if ((dasmresult & util::disasm_interface::SUPPORTED) != 0 && (dasmresult & util::disasm_interface::STEP_OVER) != 0) + // (TODO: this completely fails for subroutines that consume inline operands or use alternate returns) + if ((dasmresult & util::disasm_interface::STEP_OVER) != 0 || test_cond) { int extraskip = (dasmresult & util::disasm_interface::OVERINSTMASK) >> util::disasm_interface::OVERINSTSHIFT; pc = buffer.next_pc_wrap(pc, dasmresult & util::disasm_interface::LENGTHMASK); + m_delay_steps = extraskip + 1; + if (m_stepsleft < m_delay_steps) + m_stepsleft = m_delay_steps; // if we need to skip additional instructions, advance as requested while (extraskip-- > 0) { @@ -1640,15 +1826,27 @@ void device_debug::prepare_for_step_overout(offs_t pc) pc = buffer.next_pc_wrap(pc, result & util::disasm_interface::LENGTHMASK); } m_stepaddr = pc; + if ((dasmresult & util::disasm_interface::STEP_OVER) != 0) + m_flags |= DEBUG_FLAG_CALL_IN_PROGRESS; + if (test_cond) + m_flags |= DEBUG_FLAG_TEST_IN_PROGRESS; } // if we're stepping out and this isn't a step out instruction, reset the steps until stop to a high number - if ((m_flags & DEBUG_FLAG_STEPPING_OUT) != 0) + if ((m_flags & (DEBUG_FLAG_STEPPING_OUT | DEBUG_FLAG_STEPPING_BRANCH_TRUE | DEBUG_FLAG_STEPPING_BRANCH_FALSE)) != 0) { - if ((dasmresult & util::disasm_interface::SUPPORTED) != 0 && (dasmresult & util::disasm_interface::STEP_OUT) == 0) + // make sure to also reset the number of steps for conditionals that may be single-instruction loops + if (test_cond || !step_out) m_stepsleft = 100; else - m_stepsleft = 1; + { + // add extra instructions for delay slots + int extraskip = (dasmresult & util::disasm_interface::OVERINSTMASK) >> util::disasm_interface::OVERINSTSHIFT; + m_stepsleft = extraskip + 1; + + // take the last few steps normally + m_flags = (m_flags | DEBUG_FLAG_STEPPING) & ~DEBUG_FLAG_STEPPING_OUT; + } } } @@ -1669,14 +1867,15 @@ void device_debug::breakpoint_update_flags() break; } - if ( ! ( m_flags & DEBUG_FLAG_LIVE_BP ) ) + if (!(m_flags & DEBUG_FLAG_LIVE_BP)) { // see if there are any enabled registerpoints - for (debug_registerpoint &rp : *m_rplist) + for (debug_registerpoint &rp : m_rplist) { if (rp.m_enabled) { m_flags |= DEBUG_FLAG_LIVE_BP; + break; } } } @@ -1721,7 +1920,7 @@ void device_debug::breakpoint_check(offs_t pc) } // see if we have any matching registerpoints - for (debug_registerpoint &rp : *m_rplist) + for (debug_registerpoint &rp : m_rplist) { if (rp.hit()) { @@ -1745,56 +1944,6 @@ void device_debug::breakpoint_check(offs_t pc) } -//------------------------------------------------- -// watchpoint_check - check the watchpoints -// for a given CPU and address space -//------------------------------------------------- - -//------------------------------------------------- -// hotspot_check - check for hotspots on a -// memory read access -//------------------------------------------------- - -void device_debug::hotspot_check(address_space &space, offs_t address) -{ - offs_t curpc = m_device.state().pcbase(); - - // see if we have a match in our list - unsigned int hotindex; - for (hotindex = 0; hotindex < m_hotspots.size(); hotindex++) - if (m_hotspots[hotindex].m_access == address && m_hotspots[hotindex].m_pc == curpc && m_hotspots[hotindex].m_space == &space) - break; - - // if we didn't find any, make a new entry - if (hotindex == m_hotspots.size()) - { - // if the bottom of the list is over the threshold, print it - hotspot_entry &spot = m_hotspots[m_hotspots.size() - 1]; - if (spot.m_count > m_hotspot_threshhold) - m_device.machine().debugger().console().printf("Hotspot @ %s %08X (PC=%08X) hit %d times (fell off bottom)\n", space.name(), spot.m_access, spot.m_pc, spot.m_count); - - // move everything else down and insert this one at the top - memmove(&m_hotspots[1], &m_hotspots[0], sizeof(m_hotspots[0]) * (m_hotspots.size() - 1)); - m_hotspots[0].m_access = address; - m_hotspots[0].m_pc = curpc; - m_hotspots[0].m_space = &space; - m_hotspots[0].m_count = 1; - } - - // if we did find one, increase the count and move it to the top - else - { - m_hotspots[hotindex].m_count++; - if (hotindex != 0) - { - hotspot_entry temp = m_hotspots[hotindex]; - memmove(&m_hotspots[1], &m_hotspots[0], sizeof(m_hotspots[0]) * hotindex); - m_hotspots[0] = temp; - } - } -} - - //************************************************************************** // TRACER @@ -1804,10 +1953,10 @@ void device_debug::hotspot_check(address_space &space, offs_t address) // tracer - constructor //------------------------------------------------- -device_debug::tracer::tracer(device_debug &debug, FILE &file, bool trace_over, bool detect_loops, bool logerror, const char *action) +device_debug::tracer::tracer(device_debug &debug, std::unique_ptr<std::ostream> &&file, bool trace_over, bool detect_loops, bool logerror, std::string_view action) : m_debug(debug) - , m_file(file) - , m_action((action != nullptr) ? action : "") + , m_file(std::move(file)) + , m_action(action) , m_detect_loops(detect_loops) , m_logerror(logerror) , m_loops(0) @@ -1826,7 +1975,7 @@ device_debug::tracer::tracer(device_debug &debug, FILE &file, bool trace_over, b device_debug::tracer::~tracer() { // make sure we close the file if we can - fclose(&m_file); + m_file.reset(); } @@ -1862,7 +2011,7 @@ void device_debug::tracer::update(offs_t pc) // if we just finished looping, indicate as much if (m_loops != 0) - fprintf(&m_file, "\n (loops for %d instructions)\n\n", m_loops); + util::stream_format(*m_file, "\n (loops for %d instructions)\n\n", m_loops); m_loops = 0; } @@ -1877,7 +2026,7 @@ void device_debug::tracer::update(offs_t pc) buffer.disassemble(pc, instruction, next_pc, size, dasmresult); // output the result - fprintf(&m_file, "%s: %s\n", buffer.pc_to_string(pc).c_str(), instruction.c_str()); + util::stream_format(*m_file, "%s: %s\n", buffer.pc_to_string(pc), instruction); // do we need to step the trace over this instruction? if (m_trace_over && (dasmresult & util::disasm_interface::SUPPORTED) != 0 && (dasmresult & util::disasm_interface::STEP_OVER) != 0) @@ -1895,7 +2044,33 @@ void device_debug::tracer::update(offs_t pc) // log this PC m_nextdex = (m_nextdex + 1) % TRACE_LOOPS; m_history[m_nextdex] = pc; - fflush(&m_file); + m_file->flush(); +} + + +//------------------------------------------------- +// interrupt_update - log interrupt to tracefile +//------------------------------------------------- + +void device_debug::tracer::interrupt_update(int irqline, offs_t pc) +{ + if (m_trace_over) + { + if (m_trace_over_target != ~0) + return; + m_trace_over_target = pc; + } + + // if we just finished looping, indicate as much + *m_file << "\n"; + if (m_detect_loops && m_loops != 0) + { + util::stream_format(*m_file, " (loops for %d instructions)\n", m_loops); + m_loops = 0; + } + + util::stream_format(*m_file, " (interrupted at %s, IRQ %d)\n\n", debug_disasm_buffer(m_debug.device()).pc_to_string(pc), irqline); + m_file->flush(); } @@ -1903,11 +2078,11 @@ void device_debug::tracer::update(offs_t pc) // vprintf - generic print to the trace file //------------------------------------------------- -void device_debug::tracer::vprintf(const char *format, va_list va) +void device_debug::tracer::vprintf(util::format_argument_pack<char> const &args) { // pass through to the file - vfprintf(&m_file, format, va); - fflush(&m_file); + util::stream_format(*m_file, args); + m_file->flush(); } @@ -1918,7 +2093,7 @@ void device_debug::tracer::vprintf(const char *format, va_list va) void device_debug::tracer::flush() { - fflush(&m_file); + m_file->flush(); } |