summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/debug/debugcpu.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/debug/debugcpu.cpp')
-rw-r--r--src/emu/debug/debugcpu.cpp2317
1 files changed, 625 insertions, 1692 deletions
diff --git a/src/emu/debug/debugcpu.cpp b/src/emu/debug/debugcpu.cpp
index 01e0a91647b..968966b1b74 100644
--- a/src/emu/debug/debugcpu.cpp
+++ b/src/emu/debug/debugcpu.cpp
@@ -13,15 +13,18 @@
#include "debugbuf.h"
#include "express.h"
+#include "points.h"
#include "debugcon.h"
#include "debugvw.h"
#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"
@@ -36,7 +39,6 @@ const size_t debugger_cpu::NUM_TEMP_VARIABLES = 10;
debugger_cpu::debugger_cpu(running_machine &machine)
: m_machine(machine)
, m_livecpu(nullptr)
- , m_visiblecpu(nullptr)
, m_breakcpu(nullptr)
, m_symtable(nullptr)
, m_vblank_occurred(false)
@@ -45,45 +47,45 @@ 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)
{
m_tempvar = make_unique_clear<u64[]>(NUM_TEMP_VARIABLES);
/* create a global symbol table */
- m_symtable = std::make_unique<symbol_table>(&m_machine);
+ m_symtable = std::make_unique<symbol_table>(machine);
+ m_symtable->set_memory_modified_func([this]() { set_memory_modified(true); });
- // configure our base memory accessors
- configure_memory(*m_symtable);
-
- /* add "wpaddr", "wpdata", "cycles", "cpunum", "logunmap" 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);
- using namespace std::placeholders;
- m_symtable->add("cpunum", std::bind(&debugger_cpu::get_cpunum, this, _1));
-
- 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)
{
- m_symtable->add("beamx", std::bind(&debugger_cpu::get_beamx, this, _1, iter.current()));
- m_symtable->add("beamy", std::bind(&debugger_cpu::get_beamy, this, _1, iter.current()));
- m_symtable->add("frame", std::bind(&debugger_cpu::get_frame, this, _1, iter.current()));
- iter.current()->register_vblank_callback(vblank_state_delegate(&debugger_cpu::on_vblank, this));
+ screen_device &screen = *iter.current();
+ m_symtable->add("beamx", [&screen]() { return screen.hpos(); });
+ m_symtable->add("beamy", [&screen]() { return screen.vpos(); });
+ m_symtable->add("frame", [&screen]() { return screen.frame_number(); });
+ screen.register_vblank_callback(vblank_state_delegate(&debugger_cpu::on_vblank, this));
}
else if (count > 1)
{
for (uint32_t i = 0; i < count; i++, iter++)
{
- m_symtable->add(string_format("beamx%d", i).c_str(), std::bind(&debugger_cpu::get_beamx, this, _1, iter.current()));
- m_symtable->add(string_format("beamy%d", i).c_str(), std::bind(&debugger_cpu::get_beamy, this, _1, iter.current()));
- m_symtable->add(string_format("frame%d", i).c_str(), std::bind(&debugger_cpu::get_frame, this, _1, iter.current()));
- iter.current()->register_vblank_callback(vblank_state_delegate(&debugger_cpu::on_vblank, this));
+ screen_device &screen = *iter.current();
+ m_symtable->add(string_format("beamx%d", i).c_str(), [&screen]() { return screen.hpos(); });
+ m_symtable->add(string_format("beamy%d", i).c_str(), [&screen]() { return screen.vpos(); });
+ m_symtable->add(string_format("frame%d", i).c_str(), [&screen]() { return screen.frame_number(); });
+ screen.register_vblank_callback(vblank_state_delegate(&debugger_cpu::on_vblank, this));
}
}
@@ -91,31 +93,11 @@ 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]);
}
-
- /* first CPU is visible by default */
- for (device_t &device : device_iterator(m_machine.root_device()))
- {
- auto *cpu = dynamic_cast<cpu_device *>(&device);
- if (cpu != nullptr)
- {
- m_visiblecpu = cpu;
- break;
- }
- }
}
-void debugger_cpu::configure_memory(symbol_table &table)
-{
- using namespace std::placeholders;
- table.configure_memory(
- &m_machine,
- std::bind(&debugger_cpu::expression_validate, this, _1, _2, _3),
- std::bind(&debugger_cpu::expression_read_memory, this, _1, _2, _3, _4, _5, _6),
- std::bind(&debugger_cpu::expression_write_memory, this, _1, _2, _3, _4, _5, _6, _7));
-}
/*-------------------------------------------------
flush_traces - flushes all traces; this is
@@ -127,29 +109,13 @@ 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();
}
-/***************************************************************************
- SYMBOL TABLE INTERFACES
-***************************************************************************/
-
-/*-------------------------------------------------
- get_visible_symtable - return the
- locally-visible symbol table
--------------------------------------------------*/
-
-symbol_table* debugger_cpu::get_visible_symtable()
-{
- return &m_visiblecpu->debug()->symtable();
-}
-
-
-
//**************************************************************************
// MEMORY AND DISASSEMBLY HELPERS
//**************************************************************************
@@ -185,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
@@ -204,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;
@@ -230,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
@@ -288,294 +254,6 @@ bool debugger_cpu::comment_load(bool is_inline)
/***************************************************************************
- DEBUGGER MEMORY ACCESSORS
-***************************************************************************/
-
-/*-------------------------------------------------
- read_byte - return a byte from the specified
- memory space
--------------------------------------------------*/
-
-u8 debugger_cpu::read_byte(address_space &space, offs_t address, bool apply_translation)
-{
- device_memory_interface &memory = space.device().memory();
-
- if (apply_translation)
- {
- /* mask against the logical byte mask */
- address &= space.logaddrmask();
-
- /* translate if necessary; if not mapped, return 0xff */
- if (!memory.translate(space.spacenum(), TRANSLATE_READ_DEBUG, address))
- return 0xff;
- }
-
- /* otherwise, call the byte reading function for the translated address */
- return space.read_byte(address);
-}
-
-
-/*-------------------------------------------------
- read_word - return a word from the specified
- memory space
--------------------------------------------------*/
-
-u16 debugger_cpu::read_word(address_space &space, offs_t address, bool apply_translation)
-{
- device_memory_interface &memory = space.device().memory();
-
- if (apply_translation)
- {
- /* mask against the logical byte mask */
- address &= space.logaddrmask();
-
- /* translate if necessary; if not mapped, return 0xffff */
- if (!memory.translate(space.spacenum(), TRANSLATE_READ_DEBUG, address))
- return 0xffff;
- }
-
- /* otherwise, call the byte reading function for the translated address */
- return space.read_word_unaligned(address);
-}
-
-
-/*-------------------------------------------------
- read_dword - return a dword from the specified
- memory space
--------------------------------------------------*/
-
-u32 debugger_cpu::read_dword(address_space &space, offs_t address, bool apply_translation)
-{
- device_memory_interface &memory = space.device().memory();
-
- if (apply_translation)
- {
- /* mask against the logical byte mask */
- address &= space.logaddrmask();
-
- /* translate if necessary; if not mapped, return 0xffffffff */
- if (!memory.translate(space.spacenum(), TRANSLATE_READ_DEBUG, address))
- return 0xffffffff;
- }
-
- /* otherwise, call the byte reading function for the translated address */
- return space.read_dword_unaligned(address);
-}
-
-
-/*-------------------------------------------------
- read_qword - return a qword from the specified
- memory space
--------------------------------------------------*/
-
-u64 debugger_cpu::read_qword(address_space &space, offs_t address, bool apply_translation)
-{
- device_memory_interface &memory = space.device().memory();
-
- /* translate if necessary; if not mapped, return 0xffffffffffffffff */
- if (apply_translation)
- {
- /* mask against the logical byte mask */
- address &= space.logaddrmask();
-
- /* translate if necessary; if not mapped, return 0xffffffff */
- if (!memory.translate(space.spacenum(), TRANSLATE_READ_DEBUG, address))
- return ~u64(0);
- }
-
- /* otherwise, call the byte reading function for the translated address */
- return space.read_qword_unaligned(address);
-}
-
-
-/*-------------------------------------------------
- read_memory - return 1,2,4 or 8 bytes
- from the specified memory space
--------------------------------------------------*/
-
-u64 debugger_cpu::read_memory(address_space &space, offs_t address, int size, bool apply_translation)
-{
- u64 result = ~u64(0) >> (64 - 8*size);
- switch (size)
- {
- case 1: result = read_byte(space, address, apply_translation); break;
- case 2: result = read_word(space, address, apply_translation); break;
- case 4: result = read_dword(space, address, apply_translation); break;
- case 8: result = read_qword(space, address, apply_translation); break;
- }
- return result;
-}
-
-
-/*-------------------------------------------------
- write_byte - write a byte to the specified
- memory space
--------------------------------------------------*/
-
-void debugger_cpu::write_byte(address_space &space, offs_t address, u8 data, bool apply_translation)
-{
- device_memory_interface &memory = space.device().memory();
-
- if (apply_translation)
- {
- /* mask against the logical byte mask */
- address &= space.logaddrmask();
-
- /* translate if necessary; if not mapped, we're done */
- if (!memory.translate(space.spacenum(), TRANSLATE_WRITE_DEBUG, address))
- return;
- }
-
- /* otherwise, call the byte reading function for the translated address */
- space.write_byte(address, data);
-
- m_memory_modified = true;
-}
-
-
-/*-------------------------------------------------
- write_word - write a word to the specified
- memory space
--------------------------------------------------*/
-
-void debugger_cpu::write_word(address_space &space, offs_t address, u16 data, bool apply_translation)
-{
- device_memory_interface &memory = space.device().memory();
-
- if (apply_translation)
- {
- /* mask against the logical byte mask */
- address &= space.logaddrmask();
-
- /* translate if necessary; if not mapped, we're done */
- if (!memory.translate(space.spacenum(), TRANSLATE_WRITE_DEBUG, address))
- return;
- }
-
- /* otherwise, call the byte reading function for the translated address */
- space.write_word_unaligned(address, data);
-
- m_memory_modified = true;
-}
-
-
-/*-------------------------------------------------
- write_dword - write a dword to the specified
- memory space
--------------------------------------------------*/
-
-void debugger_cpu::write_dword(address_space &space, offs_t address, u32 data, bool apply_translation)
-{
- device_memory_interface &memory = space.device().memory();
-
- if (apply_translation)
- {
- /* mask against the logical byte mask */
- address &= space.logaddrmask();
-
- /* translate if necessary; if not mapped, we're done */
- if (!memory.translate(space.spacenum(), TRANSLATE_WRITE_DEBUG, address))
- return;
- }
-
- /* otherwise, call the byte reading function for the translated address */
- space.write_dword_unaligned(address, data);
-
- m_memory_modified = true;
-}
-
-
-/*-------------------------------------------------
- write_qword - write a qword to the specified
- memory space
--------------------------------------------------*/
-
-void debugger_cpu::write_qword(address_space &space, offs_t address, u64 data, bool apply_translation)
-{
- device_memory_interface &memory = space.device().memory();
-
- if (apply_translation)
- {
- /* mask against the logical byte mask */
- address &= space.logaddrmask();
-
- /* translate if necessary; if not mapped, we're done */
- if (!memory.translate(space.spacenum(), TRANSLATE_WRITE_DEBUG, address))
- return;
- }
-
- /* otherwise, call the byte reading function for the translated address */
- space.write_qword_unaligned(address, data);
-
- m_memory_modified = true;
-}
-
-
-/*-------------------------------------------------
- write_memory - write 1,2,4 or 8 bytes to the
- specified memory space
--------------------------------------------------*/
-
-void debugger_cpu::write_memory(address_space &space, offs_t address, u64 data, int size, bool apply_translation)
-{
- switch (size)
- {
- case 1: write_byte(space, address, data, apply_translation); break;
- case 2: write_word(space, address, data, apply_translation); break;
- case 4: write_dword(space, address, data, apply_translation); break;
- case 8: write_qword(space, address, data, apply_translation); break;
- }
-}
-
-
-/*-------------------------------------------------
- read_opcode - read 1,2,4 or 8 bytes at the
- given offset from opcode space
--------------------------------------------------*/
-
-u64 debugger_cpu::read_opcode(address_space &space, offs_t address, int size)
-{
- device_memory_interface &memory = space.device().memory();
-
- u64 result = ~u64(0) & (~u64(0) >> (64 - 8*size));
-
- /* keep in logical range */
- address &= space.logaddrmask();
-
- /* translate to physical first */
- if (!memory.translate(space.spacenum(), TRANSLATE_FETCH_DEBUG, address))
- return result;
-
- /* keep in physical range */
- address &= space.addrmask();
-
- /* switch off the size and handle unaligned accesses */
- switch (size)
- {
- case 1:
- result = space.read_byte(address);
- break;
-
- case 2:
- result = space.read_word_unaligned(address);
- break;
-
- case 4:
- result = space.read_dword_unaligned(address);
- break;
-
- case 6:
- case 8:
- result = space.read_qword_unaligned(address);
- break;
- }
-
- return result;
-}
-
-
-
-/***************************************************************************
INTERNAL HELPERS
***************************************************************************/
@@ -599,572 +277,15 @@ 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;
}
-
-/***************************************************************************
- EXPRESSION HANDLERS
-***************************************************************************/
-
-/*-------------------------------------------------
- expression_get_device - return a device
- based on a case insensitive tag search
--------------------------------------------------*/
-
-device_t* debugger_cpu::expression_get_device(const char *tag)
-{
- // convert to lowercase then lookup the name (tags are enforced to be all lower case)
- std::string fullname(tag);
- strmakelower(fullname);
- return m_machine.root_device().subdevice(fullname.c_str());
-}
-
-
-/*-------------------------------------------------
- expression_read_memory - read 1,2,4 or 8 bytes
- at the given offset in the given address
- space
--------------------------------------------------*/
-
-u64 debugger_cpu::expression_read_memory(void *param, const char *name, expression_space spacenum, u32 address, int size, bool disable_se)
-{
- switch (spacenum)
- {
- case EXPSPACE_PROGRAM_LOGICAL:
- case EXPSPACE_DATA_LOGICAL:
- case EXPSPACE_IO_LOGICAL:
- case EXPSPACE_SPACE3_LOGICAL:
- {
- device_t *device = nullptr;
- device_memory_interface *memory;
-
- if (name != nullptr)
- device = expression_get_device(name);
- if (device == nullptr || !device->interface(memory))
- {
- device = get_visible_cpu();
- memory = &device->memory();
- }
- if (memory->has_space(AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_LOGICAL)))
- {
- address_space &space = memory->space(AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_LOGICAL));
- auto dis = m_machine.disable_side_effects(disable_se);
- return read_memory(space, address, size, true);
- }
- break;
- }
-
- case EXPSPACE_PROGRAM_PHYSICAL:
- case EXPSPACE_DATA_PHYSICAL:
- case EXPSPACE_IO_PHYSICAL:
- case EXPSPACE_SPACE3_PHYSICAL:
- {
- device_t *device = nullptr;
- device_memory_interface *memory;
-
- if (name != nullptr)
- device = expression_get_device(name);
- if (device == nullptr || !device->interface(memory))
- {
- device = get_visible_cpu();
- memory = &device->memory();
- }
- if (memory->has_space(AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_PHYSICAL)))
- {
- address_space &space = memory->space(AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_PHYSICAL));
- auto dis = m_machine.disable_side_effects(disable_se);
- return read_memory(space, address, size, false);
- }
- break;
- }
-
- case EXPSPACE_RAMWRITE:
- {
- device_t *device = nullptr;
- device_memory_interface *memory;
-
- if (name != nullptr)
- device = expression_get_device(name);
- if (device == nullptr || !device->interface(memory))
- {
- device = get_visible_cpu();
- memory = &device->memory();
- }
- auto dis = m_machine.disable_side_effects(disable_se);
- return expression_read_program_direct(memory->space(AS_PROGRAM), (spacenum == EXPSPACE_OPCODE), address, size);
- break;
- }
-
- case EXPSPACE_OPCODE:
- {
- device_t *device = nullptr;
- device_memory_interface *memory;
-
- if (name != nullptr)
- device = expression_get_device(name);
- if (device == nullptr || !device->interface(memory))
- {
- device = get_visible_cpu();
- memory = &device->memory();
- }
- auto dis = m_machine.disable_side_effects(disable_se);
- return expression_read_program_direct(memory->space(AS_OPCODES), (spacenum == EXPSPACE_OPCODE), address, size);
- break;
- }
-
- case EXPSPACE_REGION:
- if (name == nullptr)
- break;
- return expression_read_memory_region(name, address, size);
- break;
-
- default:
- break;
- }
-
- return 0;
-}
-
-
-/*-------------------------------------------------
- expression_read_program_direct - read memory
- directly from an opcode or RAM pointer
--------------------------------------------------*/
-
-u64 debugger_cpu::expression_read_program_direct(address_space &space, int opcode, offs_t address, int size)
-{
- u8 *base;
-
- /* adjust the address into a byte address, but not if being called recursively */
- if ((opcode & 2) == 0)
- address = space.address_to_byte(address);
-
- /* call ourself recursively until we are byte-sized */
- if (size > 1)
- {
- int halfsize = size / 2;
-
- /* read each half, from lower address to upper address */
- u64 r0 = expression_read_program_direct(space, opcode | 2, address + 0, halfsize);
- u64 r1 = expression_read_program_direct(space, opcode | 2, address + halfsize, halfsize);
-
- /* assemble based on the target endianness */
- if (space.endianness() == ENDIANNESS_LITTLE)
- return r0 | (r1 << (8 * halfsize));
- else
- return r1 | (r0 << (8 * halfsize));
- }
-
- /* handle the byte-sized final requests */
- else
- {
- /* lowmask specified which address bits are within the databus width */
- offs_t lowmask = space.data_width() / 8 - 1;
-
- /* get the base of memory, aligned to the address minus the lowbits */
- base = (u8 *)space.get_read_ptr(address & ~lowmask);
-
- /* if we have a valid base, return the appropriate byte */
- if (base != nullptr)
- {
- if (space.endianness() == ENDIANNESS_LITTLE)
- return base[BYTE8_XOR_LE(address) & lowmask];
- else
- return base[BYTE8_XOR_BE(address) & lowmask];
- }
- }
-
- return 0;
-}
-
-
-/*-------------------------------------------------
- expression_read_memory_region - read memory
- from a memory region
--------------------------------------------------*/
-
-u64 debugger_cpu::expression_read_memory_region(const char *rgntag, offs_t address, int size)
-{
- memory_region *region = m_machine.root_device().memregion(rgntag);
- u64 result = ~u64(0) >> (64 - 8*size);
-
- /* make sure we get a valid base before proceeding */
- if (region != nullptr)
- {
- /* call ourself recursively until we are byte-sized */
- if (size > 1)
- {
- int halfsize = size / 2;
- u64 r0, r1;
-
- /* read each half, from lower address to upper address */
- r0 = expression_read_memory_region(rgntag, address + 0, halfsize);
- r1 = expression_read_memory_region(rgntag, address + halfsize, halfsize);
-
- /* assemble based on the target endianness */
- if (region->endianness() == ENDIANNESS_LITTLE)
- result = r0 | (r1 << (8 * halfsize));
- else
- result = r1 | (r0 << (8 * halfsize));
- }
-
- /* only process if we're within range */
- else if (address < region->bytes())
- {
- /* lowmask specified which address bits are within the databus width */
- u32 lowmask = region->bytewidth() - 1;
- u8 *base = region->base() + (address & ~lowmask);
-
- /* if we have a valid base, return the appropriate byte */
- if (region->endianness() == ENDIANNESS_LITTLE)
- result = base[BYTE8_XOR_LE(address) & lowmask];
- else
- result = base[BYTE8_XOR_BE(address) & lowmask];
- }
- }
- return result;
-}
-
-
-/*-------------------------------------------------
- expression_write_memory - write 1,2,4 or 8
- bytes at the given offset in the given address
- space
--------------------------------------------------*/
-
-void debugger_cpu::expression_write_memory(void *param, const char *name, expression_space spacenum, u32 address, int size, u64 data, bool disable_se)
-{
- device_t *device = nullptr;
- device_memory_interface *memory;
-
- switch (spacenum)
- {
- case EXPSPACE_PROGRAM_LOGICAL:
- case EXPSPACE_DATA_LOGICAL:
- case EXPSPACE_IO_LOGICAL:
- case EXPSPACE_SPACE3_LOGICAL:
- if (name != nullptr)
- device = expression_get_device(name);
- if (device == nullptr || !device->interface(memory))
- {
- device = get_visible_cpu();
- memory = &device->memory();
- }
- if (memory->has_space(AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_LOGICAL)))
- {
- address_space &space = memory->space(AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_LOGICAL));
- auto dis = m_machine.disable_side_effects(disable_se);
- write_memory(space, address, data, size, true);
- }
- break;
-
- case EXPSPACE_PROGRAM_PHYSICAL:
- case EXPSPACE_DATA_PHYSICAL:
- case EXPSPACE_IO_PHYSICAL:
- case EXPSPACE_SPACE3_PHYSICAL:
- if (name != nullptr)
- device = expression_get_device(name);
- if (device == nullptr || !device->interface(memory))
- {
- device = get_visible_cpu();
- memory = &device->memory();
- }
- if (memory->has_space(AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_PHYSICAL)))
- {
- address_space &space = memory->space(AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_PHYSICAL));
- auto dis = m_machine.disable_side_effects(disable_se);
- write_memory(space, address, data, size, false);
- }
- break;
-
- case EXPSPACE_RAMWRITE: {
- if (name != nullptr)
- device = expression_get_device(name);
- if (device == nullptr || !device->interface(memory))
- {
- device = get_visible_cpu();
- memory = &device->memory();
- }
- auto dis = m_machine.disable_side_effects(disable_se);
- expression_write_program_direct(memory->space(AS_PROGRAM), (spacenum == EXPSPACE_OPCODE), address, size, data);
- break;
- }
-
- case EXPSPACE_OPCODE: {
- if (name != nullptr)
- device = expression_get_device(name);
- if (device == nullptr || !device->interface(memory))
- {
- device = get_visible_cpu();
- memory = &device->memory();
- }
- auto dis = m_machine.disable_side_effects(disable_se);
- expression_write_program_direct(memory->space(AS_OPCODES), (spacenum == EXPSPACE_OPCODE), address, size, data);
- break;
- }
-
- case EXPSPACE_REGION:
- if (name == nullptr)
- break;
- expression_write_memory_region(name, address, size, data);
- break;
-
- default:
- break;
- }
-}
-
-
-/*-------------------------------------------------
- expression_write_program_direct - write memory
- directly to an opcode or RAM pointer
--------------------------------------------------*/
-
-void debugger_cpu::expression_write_program_direct(address_space &space, int opcode, offs_t address, int size, u64 data)
-{
- /* adjust the address into a byte address, but not if being called recursively */
- if ((opcode & 2) == 0)
- address = space.address_to_byte(address);
-
- /* call ourself recursively until we are byte-sized */
- if (size > 1)
- {
- int halfsize = size / 2;
-
- /* break apart based on the target endianness */
- u64 halfmask = ~u64(0) >> (64 - 8 * halfsize);
- u64 r0, r1;
- if (space.endianness() == ENDIANNESS_LITTLE)
- {
- r0 = data & halfmask;
- r1 = (data >> (8 * halfsize)) & halfmask;
- }
- else
- {
- r0 = (data >> (8 * halfsize)) & halfmask;
- r1 = data & halfmask;
- }
-
- /* write each half, from lower address to upper address */
- expression_write_program_direct(space, opcode | 2, address + 0, halfsize, r0);
- expression_write_program_direct(space, opcode | 2, address + halfsize, halfsize, r1);
- }
-
- /* handle the byte-sized final case */
- else
- {
- /* lowmask specified which address bits are within the databus width */
- offs_t lowmask = space.data_width() / 8 - 1;
-
- /* get the base of memory, aligned to the address minus the lowbits */
- u8 *base = (u8 *)space.get_read_ptr(address & ~lowmask);
-
- /* if we have a valid base, write the appropriate byte */
- if (base != nullptr)
- {
- if (space.endianness() == ENDIANNESS_LITTLE)
- base[BYTE8_XOR_LE(address) & lowmask] = data;
- else
- base[BYTE8_XOR_BE(address) & lowmask] = data;
- m_memory_modified = true;
- }
- }
-}
-
-
-/*-------------------------------------------------
- expression_write_memory_region - write memory
- from a memory region
--------------------------------------------------*/
-
-void debugger_cpu::expression_write_memory_region(const char *rgntag, offs_t address, int size, u64 data)
-{
- memory_region *region = m_machine.root_device().memregion(rgntag);
-
- /* make sure we get a valid base before proceeding */
- if (region != nullptr)
- {
- /* call ourself recursively until we are byte-sized */
- if (size > 1)
- {
- int halfsize = size / 2;
-
- /* break apart based on the target endianness */
- u64 halfmask = ~u64(0) >> (64 - 8 * halfsize);
- u64 r0, r1;
- if (region->endianness() == ENDIANNESS_LITTLE)
- {
- r0 = data & halfmask;
- r1 = (data >> (8 * halfsize)) & halfmask;
- }
- else
- {
- r0 = (data >> (8 * halfsize)) & halfmask;
- r1 = data & halfmask;
- }
-
- /* write each half, from lower address to upper address */
- expression_write_memory_region(rgntag, address + 0, halfsize, r0);
- expression_write_memory_region(rgntag, address + halfsize, halfsize, r1);
- }
-
- /* only process if we're within range */
- else if (address < region->bytes())
- {
- /* lowmask specified which address bits are within the databus width */
- u32 lowmask = region->bytewidth() - 1;
- u8 *base = region->base() + (address & ~lowmask);
-
- /* if we have a valid base, set the appropriate byte */
- if (region->endianness() == ENDIANNESS_LITTLE)
- {
- base[BYTE8_XOR_LE(address) & lowmask] = data;
- }
- else
- {
- base[BYTE8_XOR_BE(address) & lowmask] = data;
- }
- m_memory_modified = true;
- }
- }
-}
-
-
-/*-------------------------------------------------
- expression_validate - validate that the
- provided expression references an
- appropriate name
--------------------------------------------------*/
-
-expression_error::error_code debugger_cpu::expression_validate(void *param, const char *name, expression_space space)
-{
- device_t *device = nullptr;
- device_memory_interface *memory;
-
- switch (space)
- {
- case EXPSPACE_PROGRAM_LOGICAL:
- case EXPSPACE_DATA_LOGICAL:
- case EXPSPACE_IO_LOGICAL:
- case EXPSPACE_SPACE3_LOGICAL:
- if (name)
- {
- device = expression_get_device(name);
- if (device == nullptr)
- return expression_error::INVALID_MEMORY_NAME;
- }
- if (!device)
- device = get_visible_cpu();
- if (!device->interface(memory) || !memory->has_space(AS_PROGRAM + (space - EXPSPACE_PROGRAM_LOGICAL)))
- return expression_error::NO_SUCH_MEMORY_SPACE;
- break;
-
- case EXPSPACE_PROGRAM_PHYSICAL:
- case EXPSPACE_DATA_PHYSICAL:
- case EXPSPACE_IO_PHYSICAL:
- case EXPSPACE_SPACE3_PHYSICAL:
- if (name)
- {
- device = expression_get_device(name);
- if (device == nullptr)
- return expression_error::INVALID_MEMORY_NAME;
- }
- if (!device)
- device = get_visible_cpu();
- if (!device->interface(memory) || !memory->has_space(AS_PROGRAM + (space - EXPSPACE_PROGRAM_PHYSICAL)))
- return expression_error::NO_SUCH_MEMORY_SPACE;
- break;
-
- case EXPSPACE_RAMWRITE:
- if (name)
- {
- device = expression_get_device(name);
- if (device == nullptr || !device->interface(memory))
- return expression_error::INVALID_MEMORY_NAME;
- }
- if (!device)
- device = get_visible_cpu();
- if (!device->interface(memory) || !memory->has_space(AS_PROGRAM))
- return expression_error::NO_SUCH_MEMORY_SPACE;
- break;
-
- case EXPSPACE_OPCODE:
- if (name)
- {
- device = expression_get_device(name);
- if (device == nullptr || !device->interface(memory))
- return expression_error::INVALID_MEMORY_NAME;
- }
- if (!device)
- device = get_visible_cpu();
- if (!device->interface(memory) || !memory->has_space(AS_OPCODES))
- return expression_error::NO_SUCH_MEMORY_SPACE;
- break;
-
- case EXPSPACE_REGION:
- if (!name)
- return expression_error::MISSING_MEMORY_NAME;
- if (!m_machine.root_device().memregion(name) || !m_machine.root_device().memregion(name)->base())
- return expression_error::INVALID_MEMORY_NAME;
- break;
-
- default:
- return expression_error::NO_SUCH_MEMORY_SPACE;
- }
- return expression_error::NONE;
-}
-
-
-
-/***************************************************************************
- VARIABLE GETTERS/SETTERS
-***************************************************************************/
-
-/*-------------------------------------------------
- get_beamx - get beam horizontal position
--------------------------------------------------*/
-
-u64 debugger_cpu::get_beamx(symbol_table &table, screen_device *screen)
-{
- return (screen != nullptr) ? screen->hpos() : 0;
-}
-
-
-/*-------------------------------------------------
- get_beamy - get beam vertical position
--------------------------------------------------*/
-
-u64 debugger_cpu::get_beamy(symbol_table &table, screen_device *screen)
-{
- return (screen != nullptr) ? screen->vpos() : 0;
-}
-
-
-/*-------------------------------------------------
- get_frame - get current frame number
--------------------------------------------------*/
-
-u64 debugger_cpu::get_frame(symbol_table &table, screen_device *screen)
-{
- return (screen != nullptr) ? screen->frame_number() : 0;
-}
-
-
-/*-------------------------------------------------
- get_cpunum - getter callback for the
- 'cpunum' symbol
--------------------------------------------------*/
-
-u64 debugger_cpu::get_cpunum(symbol_table &table)
-{
- execute_interface_iterator iter(m_machine.root_device());
- return iter.indexof(m_visiblecpu->execute());
-}
-
+//**************************************************************************
+// EXECUTION HOOKS
+//**************************************************************************
void debugger_cpu::start_hook(device_t *device, bool stop_on_vblank)
{
@@ -1172,8 +293,15 @@ void debugger_cpu::start_hook(device_t *device, bool stop_on_vblank)
assert(m_livecpu == nullptr);
m_livecpu = device;
+ // can't stop on a device without a state interface
+ if (m_execution_state == exec_state::STOPPED && dynamic_cast<device_state_interface *>(device) == nullptr)
+ {
+ if (m_stop_when_not_device == nullptr)
+ m_stop_when_not_device = device;
+ m_execution_state = exec_state::RUNNING;
+ }
// if we're a new device, stop now
- if (m_stop_when_not_device != nullptr && m_stop_when_not_device != device)
+ else if (m_stop_when_not_device != nullptr && m_stop_when_not_device != device && device->debug()->observing())
{
m_stop_when_not_device = nullptr;
m_execution_state = exec_state::STOPPED;
@@ -1183,13 +311,14 @@ void debugger_cpu::start_hook(device_t *device, bool stop_on_vblank)
// if we're running, do some periodic updating
if (m_execution_state != exec_state::STOPPED)
{
- if (device == m_visiblecpu && osd_ticks() > m_last_periodic_update_time + osd_ticks_per_second() / 4)
+ device_t *visiblecpu = m_machine.debugger().console().get_visible_cpu();
+ if (device == visiblecpu && osd_ticks() > m_last_periodic_update_time + osd_ticks_per_second() / 4)
{ // check for periodic updates
m_machine.debug_view().update_all();
m_machine.debug_view().flush_osd_updates();
m_last_periodic_update_time = osd_ticks();
}
- else if (device == m_breakcpu)
+ if (device == m_breakcpu)
{ // check for pending breaks
m_execution_state = exec_state::STOPPED;
m_breakcpu = nullptr;
@@ -1209,7 +338,10 @@ void debugger_cpu::start_hook(device_t *device, bool stop_on_vblank)
}
// check for debug keypresses
if (m_machine.ui_input().pressed(IPT_UI_DEBUG_BREAK))
- m_visiblecpu->debug()->halt_on_next_instruction("User-initiated break\n");
+ {
+ visiblecpu->debug()->ignore(false);
+ visiblecpu->debug()->halt_on_next_instruction("User-initiated break\n");
+ }
}
}
@@ -1217,6 +349,13 @@ void debugger_cpu::stop_hook(device_t *device)
{
assert(m_livecpu == device);
+ // if we are supposed to be stopped at this point (most likely because of a watchpoint), keep going until this CPU is live again
+ if (m_execution_state == exec_state::STOPPED)
+ {
+ m_breakcpu = device;
+ m_execution_state = exec_state::RUNNING;
+ }
+
// clear the live CPU
m_livecpu = nullptr;
}
@@ -1248,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)
@@ -1270,6 +409,70 @@ void debugger_cpu::halt_on_next_instruction(device_t *device, util::format_argum
}
}
+
+//-------------------------------------------------
+// wait_for_debugger - pause during execution to
+// allow debugging
+//-------------------------------------------------
+
+void debugger_cpu::wait_for_debugger(device_t &device)
+{
+ assert(is_stopped());
+ assert(within_instruction_hook());
+
+ bool firststop = true;
+
+ // load comments if we haven't yet
+ ensure_comments_loaded();
+
+ // reset any transient state
+ reset_transient_flags();
+ set_break_cpu(nullptr);
+
+ // remember the last visible CPU in the debugger
+ m_machine.debugger().console().set_visible_cpu(&device);
+
+ // update all views
+ m_machine.debug_view().update_all();
+ m_machine.debugger().refresh_display();
+
+ // wait for the debugger; during this time, disable sound output
+ m_machine.sound().debugger_mute(true);
+ while (is_stopped())
+ {
+ // flush any pending updates before waiting again
+ m_machine.debug_view().flush_osd_updates();
+
+ emulator_info::periodic_check();
+
+ // clear the memory modified flag and wait
+ set_memory_modified(false);
+ if (m_machine.debug_flags & DEBUG_FLAG_OSD_ENABLED)
+ m_machine.osd().wait_for_debugger(device, firststop);
+ firststop = false;
+
+ // if something modified memory, update the screen
+ if (memory_modified())
+ {
+ m_machine.debug_view().update_all(DVT_DISASSEMBLY);
+ m_machine.debug_view().update_all(DVT_STATE);
+ m_machine.debugger().refresh_display();
+ }
+
+ // check for commands in the source file
+ m_machine.debugger().console().process_source_file();
+
+ // if an event got scheduled, resume
+ if (m_machine.scheduled_event_pending())
+ set_execution_running();
+ }
+ m_machine.sound().debugger_mute(false);
+
+ // remember the last visible CPU in the debugger
+ m_machine.debugger().console().set_visible_cpu(&device);
+}
+
+
//**************************************************************************
// DEVICE DEBUG
//**************************************************************************
@@ -1285,10 +488,10 @@ device_debug::device_debug(device_t &device)
, m_state(nullptr)
, m_disasm(nullptr)
, m_flags(0)
- , m_symtable(&device, device.machine().debugger().cpu().get_global_symtable())
- , m_instrhook(nullptr)
+ , m_symtable(std::make_unique<symbol_table>(device.machine(), &device.machine().debugger().cpu().global_symtable(), &device))
, m_stepaddr(0)
, m_stepsleft(0)
+ , m_delay_steps(0)
, m_stopaddr(0)
, m_stoptime(attotime::zero)
, m_stopirq(0)
@@ -1296,13 +499,15 @@ device_debug::device_debug(device_t &device)
, m_endexectime(attotime::zero)
, m_total_cycles(0)
, m_last_total_cycles(0)
+ , m_was_waiting(true)
, m_pc_history_index(0)
+ , m_pc_history_valid(0)
, m_bplist()
, 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()
@@ -1321,15 +526,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
@@ -1338,49 +542,48 @@ device_debug::device_debug(device_t &device)
// add global symbol for cycles and totalcycles
if (m_exec != nullptr)
{
- m_symtable.add("cycles", get_cycles);
- m_symtable.add("totalcycles", get_totalcycles);
- m_symtable.add("lastinstructioncycles", get_lastinstructioncycles);
+ m_symtable->add("cycles", [this]() { return m_exec->cycles_remaining(); });
+ m_symtable->add("totalcycles", symbol_table::READ_ONLY, &m_total_cycles);
+ m_symtable->add("lastinstructioncycles", [this]() { return m_total_cycles - m_last_total_cycles; });
}
// add entries to enable/disable unmap reporting for each space
if (m_memory != nullptr)
{
if (m_memory->has_space(AS_PROGRAM))
- m_symtable.add(
+ m_symtable->add(
"logunmap",
- [&space = m_memory->space(AS_PROGRAM)] (symbol_table &table) { return space.log_unmap(); },
- [&space = m_memory->space(AS_PROGRAM)] (symbol_table &table, u64 value) { return space.set_log_unmap(bool(value)); });
+ [&space = m_memory->space(AS_PROGRAM)] () { return space.log_unmap(); },
+ [&space = m_memory->space(AS_PROGRAM)] (u64 value) { return space.set_log_unmap(bool(value)); });
if (m_memory->has_space(AS_DATA))
- m_symtable.add(
+ m_symtable->add(
"logunmap",
- [&space = m_memory->space(AS_DATA)] (symbol_table &table) { return space.log_unmap(); },
- [&space = m_memory->space(AS_DATA)] (symbol_table &table, u64 value) { return space.set_log_unmap(bool(value)); });
+ [&space = m_memory->space(AS_DATA)] () { return space.log_unmap(); },
+ [&space = m_memory->space(AS_DATA)] (u64 value) { return space.set_log_unmap(bool(value)); });
if (m_memory->has_space(AS_IO))
- m_symtable.add(
+ m_symtable->add(
"logunmap",
- [&space = m_memory->space(AS_IO)] (symbol_table &table) { return space.log_unmap(); },
- [&space = m_memory->space(AS_IO)] (symbol_table &table, u64 value) { return space.set_log_unmap(bool(value)); });
+ [&space = m_memory->space(AS_IO)] () { return space.log_unmap(); },
+ [&space = m_memory->space(AS_IO)] (u64 value) { return space.set_log_unmap(bool(value)); });
if (m_memory->has_space(AS_OPCODES))
- m_symtable.add(
+ m_symtable->add(
"logunmap",
- [&space = m_memory->space(AS_OPCODES)] (symbol_table &table) { return space.log_unmap(); },
- [&space = m_memory->space(AS_OPCODES)] (symbol_table &table, u64 value) { return space.set_log_unmap(bool(value)); });
+ [&space = m_memory->space(AS_OPCODES)] () { return space.log_unmap(); },
+ [&space = m_memory->space(AS_OPCODES)] (u64 value) { return space.set_log_unmap(bool(value)); });
}
// 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()));
- m_symtable.add(
+ std::string tempstr(strmakelower(entry->symbol()));
+ m_symtable->add(
tempstr.c_str(),
- std::bind(&device_debug::get_state, _1, entry->index()),
- entry->writeable() ? std::bind(&device_debug::set_state, _1, entry->index(), _2) : symbol_table::setter_func(nullptr),
+ std::bind(&device_state_entry::value, entry.get()),
+ entry->writeable() ? std::bind(&device_state_entry::set_value, entry.get(), _1) : symbol_table::setter_func(nullptr),
entry->format_string());
}
}
@@ -1392,8 +595,8 @@ device_debug::device_debug(device_t &device)
m_flags = DEBUG_FLAG_OBSERVING | DEBUG_FLAG_HISTORY;
// if no curpc, add one
- if (m_state && !m_symtable.find("curpc"))
- m_symtable.add("curpc", get_current_pc);
+ if (m_state && !m_symtable->find("curpc"))
+ m_symtable->add("curpc", std::bind(&device_state_interface::pcbase, m_state));
}
// set up trace
@@ -1412,11 +615,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;
@@ -1425,30 +629,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;
}
}
}
@@ -1462,6 +652,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
//-------------------------------------------------
@@ -1496,15 +701,53 @@ void device_debug::stop_hook()
// acknowledged
//-------------------------------------------------
-void device_debug::interrupt_hook(int irqline)
+void device_debug::interrupt_hook(int irqline, offs_t pc)
{
+ // CPU is presumably no longer waiting if it acknowledges an interrupt
+ if (m_was_waiting)
+ {
+ m_was_waiting = false;
+ compute_debug_flags();
+ }
+
// 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;
+ }
}
@@ -1518,9 +761,54 @@ void device_debug::exception_hook(int exception)
// see if this matches an exception breakpoint
if ((m_flags & DEBUG_FLAG_STOP_EXCEPTION) != 0 && (m_stopexception == -1 || m_stopexception == exception))
{
- m_device.machine().debugger().cpu().set_execution_stopped();
- m_device.machine().debugger().console().printf("Stopped on exception (CPU '%s', exception %d)\n", m_device.tag(), exception);
- compute_debug_flags();
+ bool matched = true;
+ if (m_exception_condition && !m_exception_condition->is_empty())
+ {
+ try
+ {
+ matched = m_exception_condition->execute();
+ }
+ catch (expression_error &)
+ {
+ return;
+ }
+ }
+
+ if (matched)
+ {
+ m_device.machine().debugger().cpu().set_execution_stopped();
+ 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;
+ }
+ }
}
}
@@ -1532,18 +820,18 @@ void device_debug::exception_hook(int exception)
void device_debug::privilege_hook()
{
- bool matched = 1;
-
if ((m_flags & DEBUG_FLAG_STOP_PRIVILEGE) != 0)
{
- if (m_privilege_condition && !m_privilege_condition->is_empty())
+ bool matched = true;
+ if (m_stop_condition && !m_stop_condition->is_empty())
{
try
{
- matched = m_privilege_condition->execute();
+ matched = m_stop_condition->execute();
}
- catch (...)
+ catch (expression_error &)
{
+ return;
}
}
@@ -1556,6 +844,7 @@ void device_debug::privilege_hook()
}
}
+
//-------------------------------------------------
// instruction_hook - called by the CPU cores
// before executing each instruction
@@ -1564,17 +853,25 @@ void device_debug::privilege_hook()
void device_debug::instruction_hook(offs_t curpc)
{
running_machine &machine = m_device.machine();
- debugger_cpu& debugcpu = machine.debugger().cpu();
+ debugger_cpu &debugcpu = machine.debugger().cpu();
// note that we are in the debugger code
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;
m_total_cycles = m_exec->total_cycles();
+ if (m_was_waiting)
+ {
+ m_was_waiting = false;
+ compute_debug_flags();
+ }
// are we tracking our recent pc visits?
if (m_track_pc)
@@ -1587,26 +884,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();
@@ -1616,7 +943,7 @@ void device_debug::instruction_hook(offs_t curpc)
}
// handle breakpoints
- if (!debugcpu.is_stopped() && (m_flags & (DEBUG_FLAG_STOP_TIME | DEBUG_FLAG_STOP_PC | DEBUG_FLAG_LIVE_BP)) != 0)
+ if (!debugcpu.is_stopped() && (m_flags & (DEBUG_FLAG_STOP_TIME | DEBUG_FLAG_STOP_PC | DEBUG_FLAG_LIVE_BP | DEBUG_FLAG_LIVE_RP)) != 0)
{
// see if we hit a target time
if ((m_flags & DEBUG_FLAG_STOP_TIME) != 0 && machine.time() >= m_stoptime)
@@ -1628,72 +955,76 @@ 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();
}
// check for execution breakpoints
- else if ((m_flags & DEBUG_FLAG_LIVE_BP) != 0)
- breakpoint_check(curpc);
+ else
+ {
+ if ((m_flags & DEBUG_FLAG_LIVE_BP) != 0)
+ breakpoint_check(curpc);
+ if ((m_flags & DEBUG_FLAG_LIVE_RP) != 0)
+ registerpoint_check();
+ }
}
// if we are supposed to halt, do it now
if (debugcpu.is_stopped())
- {
- bool firststop = true;
-
- // load comments if we haven't yet
- debugcpu.ensure_comments_loaded();
-
- // reset any transient state
- debugcpu.reset_transient_flags();
- debugcpu.set_break_cpu(nullptr);
+ debugcpu.wait_for_debugger(m_device);
- // remember the last visible CPU in the debugger
- debugcpu.set_visible_cpu(&m_device);
+ // handle step out/over on the instruction we are about to execute
+ 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());
- // update all views
- machine.debug_view().update_all();
- machine.debugger().refresh_display();
+ // no longer in debugger code
+ debugcpu.set_within_instruction(false);
+}
- // wait for the debugger; during this time, disable sound output
- m_device.machine().sound().debugger_mute(true);
- while (debugcpu.is_stopped())
- {
- // flush any pending updates before waiting again
- machine.debug_view().flush_osd_updates();
- emulator_info::periodic_check();
+//-------------------------------------------------
+// wait_hook - called by the CPU cores while
+// waiting indefinitely for some kind of event
+//-------------------------------------------------
- // clear the memory modified flag and wait
- debugcpu.set_memory_modified(false);
- if (machine.debug_flags & DEBUG_FLAG_OSD_ENABLED)
- machine.osd().wait_for_debugger(m_device, firststop);
- firststop = false;
+void device_debug::wait_hook()
+{
+ running_machine &machine = m_device.machine();
+ debugger_cpu &debugcpu = machine.debugger().cpu();
- // if something modified memory, update the screen
- if (debugcpu.memory_modified())
- {
- machine.debug_view().update_all(DVT_DISASSEMBLY);
- machine.debugger().refresh_display();
- }
+ // note that we are in the debugger code
+ debugcpu.set_within_instruction(true);
- // check for commands in the source file
- machine.debugger().console().process_source_file();
+ // update total cycles
+ m_last_total_cycles = m_total_cycles;
+ m_total_cycles = m_exec->total_cycles();
- // if an event got scheduled, resume
- if (machine.scheduled_event_pending())
- debugcpu.set_execution_running();
+ // handle registerpoints (but not breakpoints)
+ if (!debugcpu.is_stopped() && (m_flags & (DEBUG_FLAG_STOP_TIME | DEBUG_FLAG_LIVE_RP)) != 0)
+ {
+ // see if we hit a target time
+ if ((m_flags & DEBUG_FLAG_STOP_TIME) != 0 && machine.time() >= m_stoptime)
+ {
+ machine.debugger().console().printf("Stopped at time interval %.1g\n", machine.time().as_double());
+ debugcpu.set_execution_stopped();
}
- m_device.machine().sound().debugger_mute(false);
-
- // remember the last visible CPU in the debugger
- debugcpu.set_visible_cpu(&m_device);
+ else if ((m_flags & DEBUG_FLAG_LIVE_RP) != 0)
+ registerpoint_check();
}
- // 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)
- prepare_for_step_overout(m_state->pcbase());
+ // if we are supposed to halt, do it now
+ if (debugcpu.is_stopped())
+ {
+ if (!m_was_waiting)
+ {
+ machine.debugger().console().printf("CPU waiting after PC=%s\n", m_state->state_string(STATE_GENPCBASE));
+ m_was_waiting = true;
+ }
+ debugcpu.wait_for_debugger(m_device);
+ }
// no longer in debugger code
debugcpu.set_within_instruction(false);
@@ -1701,22 +1032,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
//-------------------------------------------------
@@ -1771,8 +1086,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();
}
@@ -1788,8 +1104,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();
}
@@ -1804,9 +1121,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();
}
@@ -1866,12 +1185,13 @@ void device_debug::go_next_device()
// exception fires on the visible CPU
//-------------------------------------------------
-void device_debug::go_exception(int exception)
+void device_debug::go_exception(int exception, const char *condition)
{
assert(m_exec != nullptr);
m_device.machine().rewind_invalidate();
m_stopexception = exception;
+ m_exception_condition = std::make_unique<parsed_expression>(*m_symtable, condition);
m_flags |= DEBUG_FLAG_STOP_EXCEPTION;
m_device.machine().debugger().cpu().set_execution_running();
}
@@ -1902,20 +1222,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));
@@ -1926,11 +1264,11 @@ void device_debug::halt_on_next_instruction_impl(util::format_argument_pack<std:
// given address, or nullptr if none exists there
//-------------------------------------------------
-const device_debug::breakpoint *device_debug::breakpoint_find(offs_t address) const
+const debug_breakpoint *device_debug::breakpoint_find(offs_t address) const
{
- for (const breakpoint &bp : m_bplist)
- if (bp.address() == address)
- return &bp;
+ auto bpitp = m_bplist.equal_range(address);
+ if (bpitp.first != bpitp.second)
+ return bpitp.first->second.get();
return nullptr;
}
@@ -1940,15 +1278,15 @@ const device_debug::breakpoint *device_debug::breakpoint_find(offs_t address) co
// 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();
- m_bplist.emplace_front(this, m_symtable, id, address, condition, action);
+ m_bplist.emplace(address, std::make_unique<debug_breakpoint>(this, *m_symtable, id, address, condition, action));
// update the flags and return the index
breakpoint_update_flags();
- return m_bplist.front().m_index;
+ return id;
}
@@ -1960,10 +1298,10 @@ int device_debug::breakpoint_set(offs_t address, const char *condition, const ch
bool device_debug::breakpoint_clear(int index)
{
// scan the list to see if we own this breakpoint
- for (auto bbp = m_bplist.before_begin(); std::next(bbp) != m_bplist.end(); ++bbp)
- if (std::next(bbp)->m_index == index)
+ for (auto bpit = m_bplist.begin(); bpit != m_bplist.end(); ++bpit)
+ if (bpit->second->m_index == index)
{
- m_bplist.erase_after(bbp);
+ m_bplist.erase(bpit);
breakpoint_update_flags();
return true;
}
@@ -1993,13 +1331,16 @@ void device_debug::breakpoint_clear_all()
bool device_debug::breakpoint_enable(int index, bool enable)
{
// scan the list to see if we own this breakpoint
- for (breakpoint &bp : m_bplist)
+ for (auto &bpp : m_bplist)
+ {
+ debug_breakpoint &bp = *bpp.second;
if (bp.m_index == index)
{
bp.m_enabled = enable;
breakpoint_update_flags();
return true;
}
+ }
// we don't own it, return false
return false;
@@ -2014,8 +1355,8 @@ bool device_debug::breakpoint_enable(int index, bool enable)
void device_debug::breakpoint_enable_all(bool enable)
{
// apply the enable to all breakpoints we own
- for (breakpoint &bp : m_bplist)
- breakpoint_enable(bp.index(), enable);
+ for (auto &bpp : m_bplist)
+ breakpoint_enable(bpp.second->index(), enable);
}
@@ -2024,14 +1365,14 @@ 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);
// allocate a new one
u32 id = m_device.machine().debugger().cpu().get_watchpoint_index();
- m_wplist[space.spacenum()].emplace_back(std::make_unique<watchpoint>(this, m_symtable, id, space, type, address, length, condition, action));
+ m_wplist[space.spacenum()].emplace_back(std::make_unique<debug_watchpoint>(this, *m_symtable, id, space, type, address, length, condition, action));
return id;
}
@@ -2111,11 +1452,11 @@ 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();
@@ -2164,7 +1505,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 (registerpoint &rp : m_rplist)
+ for (debug_registerpoint &rp : m_rplist)
if (rp.m_index == index)
{
rp.m_enabled = enable;
@@ -2185,32 +1526,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 (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));
+
+ // 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;
+}
- // 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]));
- // fill in the info
- m_hotspot_threshhold = threshhold;
+//-------------------------------------------------
+// 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
+//-------------------------------------------------
+
+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);
}
@@ -2219,13 +1621,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;
}
@@ -2233,10 +1650,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;
@@ -2247,10 +1663,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));
@@ -2336,7 +1751,7 @@ bool device_debug::comment_export(util::xml::data_node &curnode)
// iterate through the comments
for (const auto & elem : m_comment_set)
{
- util::xml::data_node *datanode = curnode.add_child("comment", util::xml::normalize_string(elem.m_text.c_str()));
+ util::xml::data_node *datanode = curnode.add_child("comment", elem.m_text.c_str());
if (datanode == nullptr)
return false;
datanode->set_attribute_int("address", elem.m_address);
@@ -2389,7 +1804,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());
}
@@ -2397,31 +1812,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);
}
@@ -2433,7 +1831,7 @@ void device_debug::trace_printf(const char *fmt, ...)
void device_debug::compute_debug_flags()
{
running_machine &machine = m_device.machine();
- debugger_cpu& debugcpu = machine.debugger().cpu();
+ debugger_cpu &debugcpu = machine.debugger().cpu();
// clear out global flags by default, keep DEBUG_FLAG_OSD_ENABLED
machine.debug_flags &= DEBUG_FLAG_OSD_ENABLED;
@@ -2449,7 +1847,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 | DEBUG_FLAG_LIVE_RP)) != 0)
machine.debug_flags |= DEBUG_FLAG_CALL_HOOK;
// also call if we are tracing
@@ -2459,6 +1857,10 @@ void device_debug::compute_debug_flags()
// if we are stopping at a particular time and that time is within the current timeslice, we need to be called
if ((m_flags & DEBUG_FLAG_STOP_TIME) && m_endexectime <= m_stoptime)
machine.debug_flags |= DEBUG_FLAG_CALL_HOOK;
+
+ // if we were waiting, call if only to clear
+ if (m_was_waiting)
+ machine.debug_flags |= DEBUG_FLAG_CALL_HOOK;
}
@@ -2473,12 +1875,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) {
@@ -2486,15 +1908,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;
+ }
}
}
@@ -2507,23 +1941,21 @@ void device_debug::prepare_for_step_overout(offs_t pc)
void device_debug::breakpoint_update_flags()
{
// see if there are any enabled breakpoints
- m_flags &= ~DEBUG_FLAG_LIVE_BP;
- for (breakpoint &bp : m_bplist)
- if (bp.m_enabled)
+ m_flags &= ~(DEBUG_FLAG_LIVE_BP | DEBUG_FLAG_LIVE_RP);
+ for (auto &bpp : m_bplist)
+ if (bpp.second->m_enabled)
{
m_flags |= DEBUG_FLAG_LIVE_BP;
break;
}
- if ( ! ( m_flags & DEBUG_FLAG_LIVE_BP ) )
+ // see if there are any enabled registerpoints
+ for (debug_registerpoint &rp : m_rplist)
{
- // see if there are any enabled registerpoints
- for (registerpoint &rp : m_rplist)
+ if (rp.m_enabled)
{
- if (rp.m_enabled)
- {
- m_flags |= DEBUG_FLAG_LIVE_BP;
- }
+ m_flags |= DEBUG_FLAG_LIVE_RP;
+ break;
}
}
@@ -2540,12 +1972,15 @@ void device_debug::breakpoint_update_flags()
void device_debug::breakpoint_check(offs_t pc)
{
- debugger_cpu& debugcpu = m_device.machine().debugger().cpu();
-
// see if we match
- for (breakpoint &bp : m_bplist)
+ auto bpitp = m_bplist.equal_range(pc);
+ for (auto bpit = bpitp.first; bpit != bpitp.second; ++bpit)
+ {
+ debug_breakpoint &bp = *bpit->second;
if (bp.hit(pc))
{
+ debugger_cpu &debugcpu = m_device.machine().debugger().cpu();
+
// halt in the debugger by default
debugcpu.set_execution_stopped();
@@ -2561,12 +1996,24 @@ void device_debug::breakpoint_check(offs_t pc)
}
break;
}
+ }
+}
+
+//-------------------------------------------------
+// registerpoint_check - check the registerpoints
+// for a given device
+//-------------------------------------------------
+
+void device_debug::registerpoint_check()
+{
// see if we have any matching registerpoints
- for (registerpoint &rp : m_rplist)
+ for (debug_registerpoint &rp : m_rplist)
{
if (rp.hit())
{
+ debugger_cpu &debugcpu = m_device.machine().debugger().cpu();
+
// halt in the debugger by default
debugcpu.set_execution_stopped();
@@ -2587,546 +2034,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;
- }
- }
-}
-
-//-------------------------------------------------
-// get_current_pc - getter callback for a device's
-// current instruction pointer
-//-------------------------------------------------
-
-u64 device_debug::get_current_pc(symbol_table &table)
-{
- device_t *device = reinterpret_cast<device_t *>(table.globalref());
- return device->state().pcbase();
-}
-
-
-//-------------------------------------------------
-// get_cycles - getter callback for the
-// 'cycles' symbol
-//-------------------------------------------------
-
-u64 device_debug::get_cycles(symbol_table &table)
-{
- device_t *device = reinterpret_cast<device_t *>(table.globalref());
- return device->debug()->m_exec->cycles_remaining();
-}
-
-
-//-------------------------------------------------
-// get_totalcycles - getter callback for the
-// 'totalcycles' symbol
-//-------------------------------------------------
-
-u64 device_debug::get_totalcycles(symbol_table &table)
-{
- device_t *device = reinterpret_cast<device_t *>(table.globalref());
- return device->debug()->m_total_cycles;
-}
-
-
-//-------------------------------------------------
-// get_lastinstructioncycles - getter callback for the
-// 'lastinstructioncycles' symbol
-//-------------------------------------------------
-
-u64 device_debug::get_lastinstructioncycles(symbol_table &table)
-{
- device_t *device = reinterpret_cast<device_t *>(table.globalref());
- device_debug *debug = device->debug();
- return debug->m_total_cycles - debug->m_last_total_cycles;
-}
-
-
-//-------------------------------------------------
-// get_state - getter callback for a device's
-// state symbols
-//-------------------------------------------------
-
-u64 device_debug::get_state(symbol_table &table, int index)
-{
- device_t *device = reinterpret_cast<device_t *>(table.globalref());
- return device->debug()->m_state->state_int(index);
-}
-
-
-//-------------------------------------------------
-// set_state - setter callback for a device's
-// state symbols
-//-------------------------------------------------
-
-void device_debug::set_state(symbol_table &table, int index, u64 value)
-{
- device_t *device = reinterpret_cast<device_t *>(table.globalref());
- device->debug()->m_state->set_state_int(index, value);
-}
-
-
-
-//**************************************************************************
-// DEBUG BREAKPOINT
-//**************************************************************************
-
-//-------------------------------------------------
-// breakpoint - constructor
-//-------------------------------------------------
-
-device_debug::breakpoint::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 device_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
-//**************************************************************************
-
-//-------------------------------------------------
-// watchpoint - constructor
-//-------------------------------------------------
-
-device_debug::watchpoint::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);
- }
- });
-}
-
-device_debug::watchpoint::~watchpoint()
-{
- m_space.remove_change_notifier(m_notifier);
- if (m_phr)
- m_phr->remove();
- if (m_phw)
- m_phw->remove();
-}
-
-void device_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 device_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 device_debug::watchpoint::triggered(read_or_write type, offs_t address, u64 data, u64 mem_mask)
-{
- auto &machine = m_debugInterface->m_device.machine();
- auto &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
- 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();
- m_debugInterface->set_triggered_watchpoint(this);
- }
-
- debug.cpu().set_within_instruction(false);
-}
-
-//**************************************************************************
-// DEBUG REGISTERPOINT
-//**************************************************************************
-
-//-------------------------------------------------
-// registerpoint - constructor
-//-------------------------------------------------
-
-device_debug::registerpoint::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 device_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;
-}
-
-
//**************************************************************************
// TRACER
@@ -3136,10 +2043,10 @@ bool device_debug::registerpoint::hit()
// 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)
@@ -3158,7 +2065,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();
}
@@ -3194,7 +2101,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;
}
@@ -3209,7 +2116,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)
@@ -3227,7 +2134,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();
}
@@ -3235,11 +2168,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();
}
@@ -3250,7 +2183,7 @@ void device_debug::tracer::vprintf(const char *format, va_list va)
void device_debug::tracer::flush()
{
- fflush(&m_file);
+ m_file->flush();
}