summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/debug
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/debug')
-rw-r--r--src/emu/debug/debugbuf.cpp364
-rw-r--r--src/emu/debug/debugcmd.cpp3571
-rw-r--r--src/emu/debug/debugcmd.h220
-rw-r--r--src/emu/debug/debugcon.cpp1043
-rw-r--r--src/emu/debug/debugcon.h179
-rw-r--r--src/emu/debug/debugcpu.cpp2317
-rw-r--r--src/emu/debug/debugcpu.h333
-rw-r--r--src/emu/debug/debughlp.cpp1567
-rw-r--r--src/emu/debug/debughlp.h6
-rw-r--r--src/emu/debug/debugvw.cpp69
-rw-r--r--src/emu/debug/debugvw.h36
-rw-r--r--src/emu/debug/dvbpoints.cpp66
-rw-r--r--src/emu/debug/dvbpoints.h14
-rw-r--r--src/emu/debug/dvdisasm.cpp120
-rw-r--r--src/emu/debug/dvdisasm.h14
-rw-r--r--src/emu/debug/dvepoints.cpp314
-rw-r--r--src/emu/debug/dvepoints.h49
-rw-r--r--src/emu/debug/dvmemory.cpp655
-rw-r--r--src/emu/debug/dvmemory.h71
-rw-r--r--src/emu/debug/dvrpoints.cpp298
-rw-r--r--src/emu/debug/dvrpoints.h52
-rw-r--r--src/emu/debug/dvstate.cpp48
-rw-r--r--src/emu/debug/dvstate.h4
-rw-r--r--src/emu/debug/dvtext.cpp41
-rw-r--r--src/emu/debug/dvtext.h13
-rw-r--r--src/emu/debug/dvwpoints.cpp65
-rw-r--r--src/emu/debug/dvwpoints.h8
-rw-r--r--src/emu/debug/express.cpp1165
-rw-r--r--src/emu/debug/express.h137
-rw-r--r--src/emu/debug/points.cpp525
-rw-r--r--src/emu/debug/points.h186
-rw-r--r--src/emu/debug/textbuf.cpp332
-rw-r--r--src/emu/debug/textbuf.h83
33 files changed, 8445 insertions, 5520 deletions
diff --git a/src/emu/debug/debugbuf.cpp b/src/emu/debug/debugbuf.cpp
index 4e232ce16a0..616567b398e 100644
--- a/src/emu/debug/debugbuf.cpp
+++ b/src/emu/debug/debugbuf.cpp
@@ -82,6 +82,8 @@ void debug_disasm_buffer::debug_data_buffer::fill(offs_t lstart, offs_t size) co
}
}
+ // FIXME: This buffer tends to hog more memory than necessary for typical disassembly tasks.
+ // If the PC values supplied are far enough apart, the buffer may suddenly increase in size to a gigabyte or more.
if(m_buffer.empty()) {
m_lstart = lstart;
m_lend = lend;
@@ -179,6 +181,7 @@ void debug_disasm_buffer::debug_data_buffer::setup_methods()
int shift = space->addr_shift();
int alignment = m_intf.opcode_alignment();
endianness_t endian = space->endianness();
+ bool is_octal = space->is_octal();
m_pc_mask = space->logaddrmask();
@@ -208,8 +211,9 @@ void debug_disasm_buffer::debug_data_buffer::setup_methods()
u16 *dest = get_ptr<u16>(lstart);
for(offs_t lpc = lstart; lpc != lend; lpc = (lpc + 1) & m_pc_mask) {
offs_t tpc = m_intf.pc_linear_to_real(lpc);
- if (m_space->device().memory().translate(m_space->spacenum(), TRANSLATE_FETCH_DEBUG, tpc))
- *dest++ = m_space->read_word(tpc);
+ address_space *space;
+ if (m_space->device().memory().translate(m_space->spacenum(), device_memory_interface::TR_FETCH, tpc, space))
+ *dest++ = space->read_word(tpc);
else
*dest++ = 0;
}
@@ -219,14 +223,13 @@ void debug_disasm_buffer::debug_data_buffer::setup_methods()
m_do_fill = [this](offs_t lstart, offs_t lend) {
auto dis = m_space->device().machine().disable_side_effects();
u8 *dest = get_ptr<u8>(lstart);
- u32 steps = 0;
for(offs_t lpc = lstart; lpc != lend; lpc = (lpc + 1) & m_pc_mask) {
offs_t tpc = m_intf.pc_linear_to_real(lpc);
- if (m_space->device().memory().translate(m_space->spacenum(), TRANSLATE_FETCH_DEBUG, tpc))
- *dest++ = m_space->read_byte(tpc);
+ address_space *space;
+ if (m_space->device().memory().translate(m_space->spacenum(), device_memory_interface::TR_FETCH, tpc, space))
+ *dest++ = space->read_byte(tpc);
else
*dest++ = 0;
- steps++;
}
};
break;
@@ -240,8 +243,9 @@ void debug_disasm_buffer::debug_data_buffer::setup_methods()
u64 *dest = get_ptr<u64>(lstart);
for(offs_t lpc = lstart; lpc != lend; lpc = (lpc + 1) & m_pc_mask) {
offs_t tpc = lpc;
- if (m_space->device().memory().translate(m_space->spacenum(), TRANSLATE_FETCH_DEBUG, tpc))
- *dest++ = m_space->read_qword(tpc);
+ address_space *space;
+ if (m_space->device().memory().translate(m_space->spacenum(), device_memory_interface::TR_FETCH, tpc, space))
+ *dest++ = space->read_qword(tpc);
else
*dest++ = 0;
}
@@ -254,8 +258,9 @@ void debug_disasm_buffer::debug_data_buffer::setup_methods()
u32 *dest = get_ptr<u32>(lstart);
for(offs_t lpc = lstart; lpc != lend; lpc = (lpc + 1) & m_pc_mask) {
offs_t tpc = lpc;
- if (m_space->device().memory().translate(m_space->spacenum(), TRANSLATE_FETCH_DEBUG, tpc))
- *dest++ = m_space->read_dword(tpc);
+ address_space *space;
+ if (m_space->device().memory().translate(m_space->spacenum(), device_memory_interface::TR_FETCH, tpc, space))
+ *dest++ = space->read_dword(tpc);
else
*dest++ = 0;
}
@@ -268,8 +273,9 @@ void debug_disasm_buffer::debug_data_buffer::setup_methods()
u16 *dest = get_ptr<u16>(lstart);
for(offs_t lpc = lstart; lpc != lend; lpc = (lpc + 1) & m_pc_mask) {
offs_t tpc = lpc;
- if (m_space->device().memory().translate(m_space->spacenum(), TRANSLATE_FETCH_DEBUG, tpc))
- *dest++ = m_space->read_word(tpc);
+ address_space *space;
+ if (m_space->device().memory().translate(m_space->spacenum(), device_memory_interface::TR_FETCH, tpc, space))
+ *dest++ = space->read_word(tpc);
else
*dest++ = 0;
}
@@ -282,8 +288,9 @@ void debug_disasm_buffer::debug_data_buffer::setup_methods()
u8 *dest = get_ptr<u8>(lstart);
for(offs_t lpc = lstart; lpc != lend; lpc = (lpc + 1) & m_pc_mask) {
offs_t tpc = lpc;
- if (m_space->device().memory().translate(m_space->spacenum(), TRANSLATE_FETCH_DEBUG, tpc))
- *dest++ = m_space->read_byte(tpc);
+ address_space *space;
+ if (m_space->device().memory().translate(m_space->spacenum(), device_memory_interface::TR_FETCH, tpc, space))
+ *dest++ = space->read_byte(tpc);
else
*dest++ = 0;
}
@@ -296,8 +303,9 @@ void debug_disasm_buffer::debug_data_buffer::setup_methods()
u16 *dest = reinterpret_cast<u16 *>(&m_buffer[0]) + ((lstart - m_lstart) >> 4);
for(offs_t lpc = lstart; lpc != lend; lpc = (lpc + 0x10) & m_pc_mask) {
offs_t tpc = lpc;
- if (m_space->device().memory().translate(m_space->spacenum(), TRANSLATE_FETCH_DEBUG, tpc))
- *dest++ = m_space->read_word(tpc);
+ address_space *space;
+ if (m_space->device().memory().translate(m_space->spacenum(), device_memory_interface::TR_FETCH, tpc, space))
+ *dest++ = space->read_word(tpc);
else
*dest++ = 0;
}
@@ -954,44 +962,80 @@ void debug_disasm_buffer::debug_data_buffer::setup_methods()
// Define the data -> string conversion
switch(shift) {
case -3:
- m_data_to_string = [this](offs_t pc, offs_t size) {
- std::ostringstream out;
- for(offs_t i=0; i != size; i++) {
- if(i)
- out << ' ';
- util::stream_format(out, "%016X", r64(pc));
- pc = m_next_pc_wrap(pc, 1);
- }
- return out.str();
- };
- break;
-
- case -2:
- switch(alignment) {
- case 1:
+ if(is_octal)
m_data_to_string = [this](offs_t pc, offs_t size) {
std::ostringstream out;
for(offs_t i=0; i != size; i++) {
if(i)
out << ' ';
- util::stream_format(out, "%08X", r32(pc));
+ util::stream_format(out, "%022o", r64(pc));
pc = m_next_pc_wrap(pc, 1);
}
return out.str();
};
- break;
-
- case 2:
+ else
m_data_to_string = [this](offs_t pc, offs_t size) {
std::ostringstream out;
- for(offs_t i=0; i != size; i += 2) {
+ for(offs_t i=0; i != size; i++) {
if(i)
out << ' ';
util::stream_format(out, "%016X", r64(pc));
- pc = m_next_pc_wrap(pc, 2);
+ pc = m_next_pc_wrap(pc, 1);
}
return out.str();
};
+ break;
+
+ case -2:
+ switch(alignment) {
+ case 1:
+ if(is_octal)
+ m_data_to_string = [this](offs_t pc, offs_t size) {
+ std::ostringstream out;
+ for(offs_t i=0; i != size; i++) {
+ if(i)
+ out << ' ';
+ util::stream_format(out, "%011o", r32(pc));
+ pc = m_next_pc_wrap(pc, 1);
+ }
+ return out.str();
+ };
+ else
+ m_data_to_string = [this](offs_t pc, offs_t size) {
+ std::ostringstream out;
+ for(offs_t i=0; i != size; i++) {
+ if(i)
+ out << ' ';
+ util::stream_format(out, "%08X", r32(pc));
+ pc = m_next_pc_wrap(pc, 1);
+ }
+ return out.str();
+ };
+ break;
+
+ case 2:
+ if(is_octal)
+ m_data_to_string = [this](offs_t pc, offs_t size) {
+ std::ostringstream out;
+ for(offs_t i=0; i != size; i += 2) {
+ if(i)
+ out << ' ';
+ util::stream_format(out, "%022o", r64(pc));
+ pc = m_next_pc_wrap(pc, 2);
+ }
+ return out.str();
+ };
+ else
+ m_data_to_string = [this](offs_t pc, offs_t size) {
+ std::ostringstream out;
+ for(offs_t i=0; i != size; i += 2) {
+ if(i)
+ out << ' ';
+ util::stream_format(out, "%016X", r64(pc));
+ pc = m_next_pc_wrap(pc, 2);
+ }
+ return out.str();
+ };
break;
}
break;
@@ -999,42 +1043,78 @@ void debug_disasm_buffer::debug_data_buffer::setup_methods()
case -1:
switch(alignment) {
case 1:
- m_data_to_string = [this](offs_t pc, offs_t size) {
- std::ostringstream out;
- for(offs_t i=0; i != size; i++) {
- if(i)
- out << ' ';
- util::stream_format(out, "%04X", r16(pc));
- pc = m_next_pc_wrap(pc, 1);
- }
- return out.str();
- };
+ if(is_octal)
+ m_data_to_string = [this](offs_t pc, offs_t size) {
+ std::ostringstream out;
+ for(offs_t i=0; i != size; i++) {
+ if(i)
+ out << ' ';
+ util::stream_format(out, "%06o", r16(pc));
+ pc = m_next_pc_wrap(pc, 1);
+ }
+ return out.str();
+ };
+ else
+ m_data_to_string = [this](offs_t pc, offs_t size) {
+ std::ostringstream out;
+ for(offs_t i=0; i != size; i++) {
+ if(i)
+ out << ' ';
+ util::stream_format(out, "%04X", r16(pc));
+ pc = m_next_pc_wrap(pc, 1);
+ }
+ return out.str();
+ };
break;
case 2:
- m_data_to_string = [this](offs_t pc, offs_t size) {
- std::ostringstream out;
- for(offs_t i=0; i != size; i += 2) {
- if(i)
+ if(is_octal)
+ m_data_to_string = [this](offs_t pc, offs_t size) {
+ std::ostringstream out;
+ for(offs_t i=0; i != size; i += 2) {
+ if(i)
+ out << ' ';
+ util::stream_format(out, "%011o", r32(pc));
+ pc = m_next_pc_wrap(pc, 2);
+ }
+ return out.str();
+ };
+ else
+ m_data_to_string = [this](offs_t pc, offs_t size) {
+ std::ostringstream out;
+ for(offs_t i=0; i != size; i += 2) {
+ if(i)
out << ' ';
- util::stream_format(out, "%08X", r32(pc));
- pc = m_next_pc_wrap(pc, 2);
- }
- return out.str();
- };
+ util::stream_format(out, "%08X", r32(pc));
+ pc = m_next_pc_wrap(pc, 2);
+ }
+ return out.str();
+ };
break;
case 4:
- m_data_to_string = [this](offs_t pc, offs_t size) {
- std::ostringstream out;
- for(offs_t i=0; i != size; i += 4) {
- if(i)
- out << ' ';
- util::stream_format(out, "%016X", r64(pc));
- pc = m_next_pc_wrap(pc, 4);
- }
- return out.str();
- };
+ if(is_octal)
+ m_data_to_string = [this](offs_t pc, offs_t size) {
+ std::ostringstream out;
+ for(offs_t i=0; i != size; i += 4) {
+ if(i)
+ out << ' ';
+ util::stream_format(out, "%022o", r64(pc));
+ pc = m_next_pc_wrap(pc, 4);
+ }
+ return out.str();
+ };
+ else
+ m_data_to_string = [this](offs_t pc, offs_t size) {
+ std::ostringstream out;
+ for(offs_t i=0; i != size; i += 4) {
+ if(i)
+ out << ' ';
+ util::stream_format(out, "%016X", r64(pc));
+ pc = m_next_pc_wrap(pc, 4);
+ }
+ return out.str();
+ };
break;
}
break;
@@ -1042,70 +1122,130 @@ void debug_disasm_buffer::debug_data_buffer::setup_methods()
case 0:
switch(alignment) {
case 1:
- m_data_to_string = [this](offs_t pc, offs_t size) {
- std::ostringstream out;
- for(offs_t i=0; i != size; i++) {
- if(i)
- out << ' ';
- util::stream_format(out, "%02X", r8(pc));
- pc = m_next_pc_wrap(pc, 1);
- }
- return out.str();
- };
+ if(is_octal)
+ m_data_to_string = [this](offs_t pc, offs_t size) {
+ std::ostringstream out;
+ for(offs_t i=0; i != size; i++) {
+ if(i)
+ out << ' ';
+ util::stream_format(out, "%03o", r8(pc));
+ pc = m_next_pc_wrap(pc, 1);
+ }
+ return out.str();
+ };
+ else
+ m_data_to_string = [this](offs_t pc, offs_t size) {
+ std::ostringstream out;
+ for(offs_t i=0; i != size; i++) {
+ if(i)
+ out << ' ';
+ util::stream_format(out, "%02X", r8(pc));
+ pc = m_next_pc_wrap(pc, 1);
+ }
+ return out.str();
+ };
break;
case 2:
- m_data_to_string = [this](offs_t pc, offs_t size) {
- std::ostringstream out;
- for(offs_t i=0; i != size; i += 2) {
- if(i)
- out << ' ';
- util::stream_format(out, "%04X", r16(pc));
- pc = m_next_pc_wrap(pc, 2);
- }
- return out.str();
- };
+ if(is_octal)
+ m_data_to_string = [this](offs_t pc, offs_t size) {
+ std::ostringstream out;
+ for(offs_t i=0; i != size; i += 2) {
+ if(i)
+ out << ' ';
+ util::stream_format(out, "%06o", r16(pc));
+ pc = m_next_pc_wrap(pc, 2);
+ }
+ return out.str();
+ };
+ else
+ m_data_to_string = [this](offs_t pc, offs_t size) {
+ std::ostringstream out;
+ for(offs_t i=0; i != size; i += 2) {
+ if(i)
+ out << ' ';
+ util::stream_format(out, "%04X", r16(pc));
+ pc = m_next_pc_wrap(pc, 2);
+ }
+ return out.str();
+ };
break;
case 4:
+ if(is_octal)
+ m_data_to_string = [this](offs_t pc, offs_t size) {
+ std::ostringstream out;
+ for(offs_t i=0; i != size; i += 4) {
+ if(i)
+ out << ' ';
+ util::stream_format(out, "%011o", r32(pc));
+ pc = m_next_pc_wrap(pc, 4);
+ }
+ return out.str();
+ };
+ else
+ m_data_to_string = [this](offs_t pc, offs_t size) {
+ std::ostringstream out;
+ for(offs_t i=0; i != size; i += 4) {
+ if(i)
+ out << ' ';
+ util::stream_format(out, "%08X", r32(pc));
+ pc = m_next_pc_wrap(pc, 4);
+ }
+ return out.str();
+ };
+ break;
+
+ case 8:
+ if(is_octal)
+ m_data_to_string = [this](offs_t pc, offs_t size) {
+ std::ostringstream out;
+ for(offs_t i=0; i != size; i += 8) {
+ if(i)
+ out << ' ';
+ util::stream_format(out, "%022o", r64(pc));
+ pc = m_next_pc_wrap(pc, 8);
+ }
+ return out.str();
+ };
+ else
+ m_data_to_string = [this](offs_t pc, offs_t size) {
+ std::ostringstream out;
+ for(offs_t i=0; i != size; i += 8) {
+ if(i)
+ out << ' ';
+ util::stream_format(out, "%016X", r64(pc));
+ pc = m_next_pc_wrap(pc, 8);
+ }
+ return out.str();
+ };
+ break;
+ }
+ break;
+
+ case 3:
+ if(is_octal)
m_data_to_string = [this](offs_t pc, offs_t size) {
std::ostringstream out;
- for(offs_t i=0; i != size; i += 4) {
+ for(offs_t i=0; i != size; i += 16) {
if(i)
out << ' ';
- util::stream_format(out, "%08X", r32(pc));
- pc = m_next_pc_wrap(pc, 4);
+ util::stream_format(out, "%06o", r16(pc));
+ pc = m_next_pc_wrap(pc, 16);
}
return out.str();
};
- break;
-
- case 8:
+ else
m_data_to_string = [this](offs_t pc, offs_t size) {
std::ostringstream out;
- for(offs_t i=0; i != size; i += 8) {
+ for(offs_t i=0; i != size; i += 16) {
if(i)
out << ' ';
- util::stream_format(out, "%016X", r64(pc));
- pc = m_next_pc_wrap(pc, 8);
+ util::stream_format(out, "%04X", r16(pc));
+ pc = m_next_pc_wrap(pc, 16);
}
return out.str();
};
- break;
- }
- break;
-
- case 3:
- m_data_to_string = [this](offs_t pc, offs_t size) {
- std::ostringstream out;
- for(offs_t i=0; i != size; i += 16) {
- if(i)
- out << ' ';
- util::stream_format(out, "%04X", r16(pc));
- pc = m_next_pc_wrap(pc, 16);
- }
- return out.str();
- };
break;
}
@@ -1257,7 +1397,7 @@ debug_disasm_buffer::debug_disasm_buffer(device_t &device) :
// pc to string conversion
int aw = pspace.logaddr_width();
bool is_octal = pspace.is_octal();
- if((m_flags & util::disasm_interface::PAGED2LEVEL) == util::disasm_interface::PAGED2LEVEL) {
+ if((m_flags & util::disasm_interface::PAGED2LEVEL) == util::disasm_interface::PAGED2LEVEL && aw > (m_dintf.page_address_bits() + m_dintf.page2_address_bits())) {
int bits1 = m_dintf.page_address_bits();
int bits2 = m_dintf.page2_address_bits();
int bits3 = aw - bits1 - bits2;
@@ -1288,7 +1428,7 @@ debug_disasm_buffer::debug_disasm_buffer(device_t &device) :
};
}
- } else if(m_flags & util::disasm_interface::PAGED) {
+ } else if((m_flags & util::disasm_interface::PAGED) && aw > m_dintf.page_address_bits()) {
int bits1 = m_dintf.page_address_bits();
int bits2 = aw - bits1;
offs_t sm1 = (1 << bits1) - 1;
diff --git a/src/emu/debug/debugcmd.cpp b/src/emu/debug/debugcmd.cpp
index cd9d9dcf743..0b7efa44a93 100644
--- a/src/emu/debug/debugcmd.cpp
+++ b/src/emu/debug/debugcmd.cpp
@@ -2,27 +2,37 @@
// copyright-holders:Aaron Giles
/*********************************************************************
- debugcmd.c
+ debugcmd.cpp
Debugger command interface engine.
*********************************************************************/
#include "emu.h"
-#include "emuopts.h"
-#include "debugger.h"
#include "debugcmd.h"
+
+#include "debugbuf.h"
#include "debugcon.h"
#include "debugcpu.h"
-#include "debugbuf.h"
-#include "express.h"
#include "debughlp.h"
#include "debugvw.h"
+#include "express.h"
+#include "points.h"
+
+#include "debugger.h"
+#include "emuopts.h"
+#include "fileio.h"
#include "natkeyboard.h"
#include "render.h"
-#include <ctype.h>
+#include "screen.h"
+#include "softlist.h"
+
+#include "corestr.h"
+
#include <algorithm>
+#include <cctype>
#include <fstream>
+#include <sstream>
@@ -43,7 +53,8 @@ const size_t debugger_commands::MAX_GLOBALS = 1000;
bool debugger_commands::cheat_address_is_valid(address_space &space, offs_t address)
{
- return space.device().memory().translate(space.spacenum(), TRANSLATE_READ, address) && (space.get_write_ptr(address) != nullptr);
+ address_space *tspace;
+ return space.device().memory().translate(space.spacenum(), device_memory_interface::TR_READ, address, tspace) && (tspace->get_write_ptr(address) != nullptr);
}
@@ -52,15 +63,15 @@ bool debugger_commands::cheat_address_is_valid(address_space &space, offs_t addr
the current cheat width, if signed
-------------------------------------------------*/
-u64 debugger_commands::cheat_sign_extend(const cheat_system *cheatsys, u64 value)
+inline u64 debugger_commands::cheat_system::sign_extend(u64 value) const
{
- if (cheatsys->signed_cheat)
+ if (signed_cheat)
{
- switch (cheatsys->width)
+ switch (width)
{
- case 1: value = s8(value); break;
- case 2: value = s16(value); break;
- case 4: value = s32(value); break;
+ case 1: value = s8(value); break;
+ case 2: value = s16(value); break;
+ case 4: value = s32(value); break;
}
}
return value;
@@ -70,16 +81,15 @@ u64 debugger_commands::cheat_sign_extend(const cheat_system *cheatsys, u64 value
cheat_byte_swap - swap a value
-------------------------------------------------*/
-u64 debugger_commands::cheat_byte_swap(const cheat_system *cheatsys, u64 value)
+inline u64 debugger_commands::cheat_system::byte_swap(u64 value) const
{
- if (cheatsys->swapped_cheat)
+ if (swapped_cheat)
{
- switch (cheatsys->width)
+ switch (width)
{
- case 2: value = ((value >> 8) & 0x00ff) | ((value << 8) & 0xff00); break;
- case 4: value = ((value >> 24) & 0x000000ff) | ((value >> 8) & 0x0000ff00) | ((value << 8) & 0x00ff0000) | ((value << 24) & 0xff000000); break;
- case 8: value = ((value >> 56) & 0x00000000000000ffU) | ((value >> 40) & 0x000000000000ff00U) | ((value >> 24) & 0x0000000000ff0000U) | ((value >> 8) & 0x00000000ff000000U) |
- ((value << 8) & 0x000000ff00000000U) | ((value << 24) & 0x0000ff0000000000U) | ((value << 40) & 0x00ff000000000000U) | ((value << 56) & 0xff00000000000000U); break;
+ case 2: value = swapendian_int16(value); break;
+ case 4: value = swapendian_int32(value); break;
+ case 8: value = swapendian_int64(value); break;
}
}
return value;
@@ -91,247 +101,296 @@ u64 debugger_commands::cheat_byte_swap(const cheat_system *cheatsys, u64 value)
and swapping if necessary
-------------------------------------------------*/
-u64 debugger_commands::cheat_read_extended(const cheat_system *cheatsys, address_space &space, offs_t address)
+u64 debugger_commands::cheat_system::read_extended(offs_t address) const
{
- return cheat_sign_extend(cheatsys, cheat_byte_swap(cheatsys, m_cpu.read_memory(space, address, cheatsys->width, true)));
+ address &= space->logaddrmask();
+ u64 value = space->unmap();
+ address_space *tspace;
+ if (space->device().memory().translate(space->spacenum(), device_memory_interface::TR_READ, address, tspace))
+ {
+ switch (width)
+ {
+ case 1: value = tspace->read_byte(address); break;
+ case 2: value = tspace->read_word_unaligned(address); break;
+ case 4: value = tspace->read_dword_unaligned(address); break;
+ case 8: value = tspace->read_qword_unaligned(address); break;
+ }
+ }
+ return sign_extend(byte_swap(value));
}
debugger_commands::debugger_commands(running_machine& machine, debugger_cpu& cpu, debugger_console& console)
: m_machine(machine)
- , m_cpu(cpu)
, m_console(console)
{
- m_global_array = std::make_unique<global_entry []>(MAX_GLOBALS);
-
- symbol_table *symtable = m_cpu.get_global_symtable();
-
- /* add a few simple global functions */
using namespace std::placeholders;
- symtable->add("min", 2, 2, std::bind(&debugger_commands::execute_min, this, _1, _2, _3));
- symtable->add("max", 2, 2, std::bind(&debugger_commands::execute_max, this, _1, _2, _3));
- symtable->add("if", 3, 3, std::bind(&debugger_commands::execute_if, this, _1, _2, _3));
+ m_global_array = std::make_unique<global_entry []>(MAX_GLOBALS);
- /* add all single-entry save state globals */
+ symbol_table &symtable = cpu.global_symtable();
+
+ // add a few simple global functions
+ symtable.add("min", 2, 2, // lower of two values
+ [] (int params, const u64 *param) -> u64
+ { return (std::min)(param[0], param[1]); });
+ symtable.add("max", 2, 2, // higher of two values
+ [] (int params, const u64 *param) -> u64
+ { return (std::max)(param[0], param[1]); });
+ symtable.add("if", 3, 3, // a ? b : c
+ [] (int params, const u64 *param) -> u64
+ { return param[0] ? param[1] : param[2]; });
+ symtable.add("abs", 1, 1, // absolute value of signed number
+ [] (int params, const u64 *param) -> u64
+ { return std::abs(s64(param[0])); });
+ symtable.add("bit", 2, 3, // extract bit field
+ [] (int params, const u64 *param) -> u64
+ { return (params == 2) ? BIT(param[0], param[1]) : BIT(param[0], param[1], param[2]); });
+ symtable.add("s8", 1, 1, // sign-extend from 8 bits
+ [] (int params, const u64 *param) -> u64
+ { return s64(s8(u8(param[0]))); });
+ symtable.add("s16", 1, 1, // sign-extend from 16 bits
+ [] (int params, const u64 *param) -> u64
+ { return s64(s16(u16(param[0]))); });
+ symtable.add("s32", 1, 1, // sign-extend from 32 bits
+ [] (int params, const u64 *param) -> u64
+ { return s64(s32(u32(param[0]))); });
+ symtable.add("cpunum", std::bind(&debugger_commands::get_cpunum, this));
+
+ // add all single-entry save state globals
for (int itemnum = 0; itemnum < MAX_GLOBALS; itemnum++)
{
- u32 valsize, valcount;
void *base;
+ u32 valsize, valcount, blockcount, stride;
- /* stop when we run out of items */
- const char* name = m_machine.save().indexed_item(itemnum, base, valsize, valcount);
- if (name == nullptr)
+ // stop when we run out of items
+ const char* name = m_machine.save().indexed_item(itemnum, base, valsize, valcount, blockcount, stride);
+ if (!name)
break;
- /* if this is a single-entry global, add it */
- if (valcount == 1 && strstr(name, "/globals/"))
+ // if this is a single-entry global, add it
+ if ((valcount == 1) && (blockcount == 1) && strstr(name, "/globals/"))
{
char symname[100];
- sprintf(symname, ".%s", strrchr(name, '/') + 1);
+ snprintf(symname, 100, ".%s", strrchr(name, '/') + 1);
m_global_array[itemnum].base = base;
m_global_array[itemnum].size = valsize;
- symtable->add(
+ symtable.add(
symname,
- std::bind(&debugger_commands::global_get, this, _1, &m_global_array[itemnum]),
- std::bind(&debugger_commands::global_set, this, _1, &m_global_array[itemnum], _2));
- }
- }
-
- /* add all the commands */
- m_console.register_command("help", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_help, this, _1, _2));
- m_console.register_command("print", CMDFLAG_NONE, 0, 1, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_print, this, _1, _2));
- m_console.register_command("printf", CMDFLAG_NONE, 0, 1, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_printf, this, _1, _2));
- m_console.register_command("logerror", CMDFLAG_NONE, 0, 1, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_logerror, this, _1, _2));
- m_console.register_command("tracelog", CMDFLAG_NONE, 0, 1, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_tracelog, this, _1, _2));
- m_console.register_command("tracesym", CMDFLAG_NONE, 0, 1, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_tracesym, this, _1, _2));
- m_console.register_command("quit", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_quit, this, _1, _2));
- m_console.register_command("exit", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_quit, this, _1, _2));
- m_console.register_command("do", CMDFLAG_NONE, 0, 1, 1, std::bind(&debugger_commands::execute_do, this, _1, _2));
- m_console.register_command("step", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_step, this, _1, _2));
- m_console.register_command("s", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_step, this, _1, _2));
- m_console.register_command("over", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_over, this, _1, _2));
- m_console.register_command("o", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_over, this, _1, _2));
- m_console.register_command("out" , CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_out, this, _1, _2));
- m_console.register_command("go", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_go, this, _1, _2));
- m_console.register_command("g", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_go, this, _1, _2));
- m_console.register_command("gvblank", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_go_vblank, this, _1, _2));
- m_console.register_command("gv", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_go_vblank, this, _1, _2));
- m_console.register_command("gint", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_go_interrupt, this, _1, _2));
- m_console.register_command("gi", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_go_interrupt, this, _1, _2));
- m_console.register_command("gex", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_go_exception, this, _1, _2));
- m_console.register_command("ge", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_go_exception, this, _1, _2));
- m_console.register_command("gtime", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_go_time, this, _1, _2));
- m_console.register_command("gt", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_go_time, this, _1, _2));
- m_console.register_command("gp", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_go_privilege, this, _1, _2));
- m_console.register_command("next", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_next, this, _1, _2));
- m_console.register_command("n", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_next, this, _1, _2));
- m_console.register_command("focus", CMDFLAG_NONE, 0, 1, 1, std::bind(&debugger_commands::execute_focus, this, _1, _2));
- m_console.register_command("ignore", CMDFLAG_NONE, 0, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_ignore, this, _1, _2));
- m_console.register_command("observe", CMDFLAG_NONE, 0, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_observe, this, _1, _2));
- m_console.register_command("suspend", CMDFLAG_NONE, 0, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_suspend, this, _1, _2));
- m_console.register_command("resume", CMDFLAG_NONE, 0, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_resume, this, _1, _2));
-
- m_console.register_command("comadd", CMDFLAG_NONE, 0, 1, 2, std::bind(&debugger_commands::execute_comment_add, this, _1, _2));
- m_console.register_command("//", CMDFLAG_NONE, 0, 1, 2, std::bind(&debugger_commands::execute_comment_add, this, _1, _2));
- m_console.register_command("comdelete", CMDFLAG_NONE, 0, 1, 1, std::bind(&debugger_commands::execute_comment_del, this, _1, _2));
- m_console.register_command("comsave", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_comment_save, this, _1, _2));
- m_console.register_command("comlist", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_comment_list, this, _1, _2));
- m_console.register_command("commit", CMDFLAG_NONE, 0, 1, 2, std::bind(&debugger_commands::execute_comment_commit, this, _1, _2));
- m_console.register_command("/*", CMDFLAG_NONE, 0, 1, 2, std::bind(&debugger_commands::execute_comment_commit, this, _1, _2));
-
- m_console.register_command("bpset", CMDFLAG_NONE, 0, 1, 3, std::bind(&debugger_commands::execute_bpset, this, _1, _2));
- m_console.register_command("bp", CMDFLAG_NONE, 0, 1, 3, std::bind(&debugger_commands::execute_bpset, this, _1, _2));
- m_console.register_command("bpclear", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_bpclear, this, _1, _2));
- m_console.register_command("bpdisable", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_bpdisenable, this, _1, _2));
- m_console.register_command("bpenable", CMDFLAG_NONE, 1, 0, 1, std::bind(&debugger_commands::execute_bpdisenable, this, _1, _2));
- m_console.register_command("bplist", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_bplist, this, _1, _2));
-
- m_console.register_command("wpset", CMDFLAG_NONE, AS_PROGRAM, 3, 5, std::bind(&debugger_commands::execute_wpset, this, _1, _2));
- m_console.register_command("wp", CMDFLAG_NONE, AS_PROGRAM, 3, 5, std::bind(&debugger_commands::execute_wpset, this, _1, _2));
- m_console.register_command("wpdset", CMDFLAG_NONE, AS_DATA, 3, 5, std::bind(&debugger_commands::execute_wpset, this, _1, _2));
- m_console.register_command("wpd", CMDFLAG_NONE, AS_DATA, 3, 5, std::bind(&debugger_commands::execute_wpset, this, _1, _2));
- m_console.register_command("wpiset", CMDFLAG_NONE, AS_IO, 3, 5, std::bind(&debugger_commands::execute_wpset, this, _1, _2));
- m_console.register_command("wpi", CMDFLAG_NONE, AS_IO, 3, 5, std::bind(&debugger_commands::execute_wpset, this, _1, _2));
- m_console.register_command("wposet", CMDFLAG_NONE, AS_OPCODES, 3, 5, std::bind(&debugger_commands::execute_wpset, this, _1, _2));
- m_console.register_command("wpo", CMDFLAG_NONE, AS_OPCODES, 3, 5, std::bind(&debugger_commands::execute_wpset, this, _1, _2));
- m_console.register_command("wpclear", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_wpclear, this, _1, _2));
- m_console.register_command("wpdisable", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_wpdisenable, this, _1, _2));
- m_console.register_command("wpenable", CMDFLAG_NONE, 1, 0, 1, std::bind(&debugger_commands::execute_wpdisenable, this, _1, _2));
- m_console.register_command("wplist", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_wplist, this, _1, _2));
-
- m_console.register_command("rpset", CMDFLAG_NONE, 0, 1, 2, std::bind(&debugger_commands::execute_rpset, this, _1, _2));
- m_console.register_command("rp", CMDFLAG_NONE, 0, 1, 2, std::bind(&debugger_commands::execute_rpset, this, _1, _2));
- m_console.register_command("rpclear", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_rpclear, this, _1, _2));
- m_console.register_command("rpdisable", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_rpdisenable, this, _1, _2));
- m_console.register_command("rpenable", CMDFLAG_NONE, 1, 0, 1, std::bind(&debugger_commands::execute_rpdisenable, this, _1, _2));
- m_console.register_command("rplist", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_rplist, this, _1, _2));
-
- m_console.register_command("hotspot", CMDFLAG_NONE, 0, 0, 3, std::bind(&debugger_commands::execute_hotspot, this, _1, _2));
-
- m_console.register_command("statesave", CMDFLAG_NONE, 0, 1, 1, std::bind(&debugger_commands::execute_statesave, this, _1, _2));
- m_console.register_command("ss", CMDFLAG_NONE, 0, 1, 1, std::bind(&debugger_commands::execute_statesave, this, _1, _2));
- m_console.register_command("stateload", CMDFLAG_NONE, 0, 1, 1, std::bind(&debugger_commands::execute_stateload, this, _1, _2));
- m_console.register_command("sl", CMDFLAG_NONE, 0, 1, 1, std::bind(&debugger_commands::execute_stateload, this, _1, _2));
-
- m_console.register_command("rewind", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_rewind, this, _1, _2));
- m_console.register_command("rw", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_rewind, this, _1, _2));
-
- m_console.register_command("save", CMDFLAG_NONE, AS_PROGRAM, 3, 4, std::bind(&debugger_commands::execute_save, this, _1, _2));
- m_console.register_command("saved", CMDFLAG_NONE, AS_DATA, 3, 4, std::bind(&debugger_commands::execute_save, this, _1, _2));
- m_console.register_command("savei", CMDFLAG_NONE, AS_IO, 3, 4, std::bind(&debugger_commands::execute_save, this, _1, _2));
- m_console.register_command("saveo", CMDFLAG_NONE, AS_OPCODES, 3, 4, std::bind(&debugger_commands::execute_save, this, _1, _2));
-
- m_console.register_command("load", CMDFLAG_NONE, AS_PROGRAM, 2, 4, std::bind(&debugger_commands::execute_load, this, _1, _2));
- m_console.register_command("loadd", CMDFLAG_NONE, AS_DATA, 2, 4, std::bind(&debugger_commands::execute_load, this, _1, _2));
- m_console.register_command("loadi", CMDFLAG_NONE, AS_IO, 2, 4, std::bind(&debugger_commands::execute_load, this, _1, _2));
- m_console.register_command("loado", CMDFLAG_NONE, AS_OPCODES, 2, 4, std::bind(&debugger_commands::execute_load, this, _1, _2));
-
- m_console.register_command("dump", CMDFLAG_NONE, AS_PROGRAM, 3, 7, std::bind(&debugger_commands::execute_dump, this, _1, _2));
- m_console.register_command("dumpd", CMDFLAG_NONE, AS_DATA, 3, 7, std::bind(&debugger_commands::execute_dump, this, _1, _2));
- m_console.register_command("dumpi", CMDFLAG_NONE, AS_IO, 3, 7, std::bind(&debugger_commands::execute_dump, this, _1, _2));
- m_console.register_command("dumpo", CMDFLAG_NONE, AS_OPCODES, 3, 7, std::bind(&debugger_commands::execute_dump, this, _1, _2));
-
- m_console.register_command("cheatinit", CMDFLAG_NONE, 0, 0, 4, std::bind(&debugger_commands::execute_cheatinit, this, _1, _2));
- m_console.register_command("ci", CMDFLAG_NONE, 0, 0, 4, std::bind(&debugger_commands::execute_cheatinit, this, _1, _2));
-
- m_console.register_command("cheatrange",CMDFLAG_NONE, 1, 2, 2, std::bind(&debugger_commands::execute_cheatinit, this, _1, _2));
- m_console.register_command("cr", CMDFLAG_NONE, 1, 2, 2, std::bind(&debugger_commands::execute_cheatinit, this, _1, _2));
-
- m_console.register_command("cheatnext", CMDFLAG_NONE, 0, 1, 2, std::bind(&debugger_commands::execute_cheatnext, this, _1, _2));
- m_console.register_command("cn", CMDFLAG_NONE, 0, 1, 2, std::bind(&debugger_commands::execute_cheatnext, this, _1, _2));
- m_console.register_command("cheatnextf",CMDFLAG_NONE, 1, 1, 2, std::bind(&debugger_commands::execute_cheatnext, this, _1, _2));
- m_console.register_command("cnf", CMDFLAG_NONE, 1, 1, 2, std::bind(&debugger_commands::execute_cheatnext, this, _1, _2));
-
- m_console.register_command("cheatlist", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_cheatlist, this, _1, _2));
- m_console.register_command("cl", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_cheatlist, this, _1, _2));
-
- m_console.register_command("cheatundo", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_cheatundo, this, _1, _2));
- m_console.register_command("cu", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_cheatundo, this, _1, _2));
-
- m_console.register_command("f", CMDFLAG_KEEP_QUOTES, AS_PROGRAM, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_find, this, _1, _2));
- m_console.register_command("find", CMDFLAG_KEEP_QUOTES, AS_PROGRAM, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_find, this, _1, _2));
- m_console.register_command("fd", CMDFLAG_KEEP_QUOTES, AS_DATA, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_find, this, _1, _2));
- m_console.register_command("findd", CMDFLAG_KEEP_QUOTES, AS_DATA, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_find, this, _1, _2));
- m_console.register_command("fi", CMDFLAG_KEEP_QUOTES, AS_IO, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_find, this, _1, _2));
- m_console.register_command("findi", CMDFLAG_KEEP_QUOTES, AS_IO, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_find, this, _1, _2));
- m_console.register_command("fo", CMDFLAG_KEEP_QUOTES, AS_OPCODES, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_find, this, _1, _2));
- m_console.register_command("findo", CMDFLAG_KEEP_QUOTES, AS_OPCODES, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_find, this, _1, _2));
-
- m_console.register_command("dasm", CMDFLAG_NONE, 0, 3, 5, std::bind(&debugger_commands::execute_dasm, this, _1, _2));
-
- m_console.register_command("trace", CMDFLAG_NONE, 0, 1, 4, std::bind(&debugger_commands::execute_trace, this, _1, _2));
- m_console.register_command("traceover", CMDFLAG_NONE, 0, 1, 4, std::bind(&debugger_commands::execute_traceover, this, _1, _2));
- m_console.register_command("traceflush",CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_traceflush, this, _1, _2));
-
- m_console.register_command("history", CMDFLAG_NONE, 0, 0, 2, std::bind(&debugger_commands::execute_history, this, _1, _2));
- m_console.register_command("trackpc", CMDFLAG_NONE, 0, 0, 3, std::bind(&debugger_commands::execute_trackpc, this, _1, _2));
-
- m_console.register_command("trackmem", CMDFLAG_NONE, 0, 0, 3, std::bind(&debugger_commands::execute_trackmem, this, _1, _2));
- m_console.register_command("pcatmemp", CMDFLAG_NONE, AS_PROGRAM, 1, 2, std::bind(&debugger_commands::execute_pcatmem, this, _1, _2));
- m_console.register_command("pcatmemd", CMDFLAG_NONE, AS_DATA, 1, 2, std::bind(&debugger_commands::execute_pcatmem, this, _1, _2));
- m_console.register_command("pcatmemi", CMDFLAG_NONE, AS_IO, 1, 2, std::bind(&debugger_commands::execute_pcatmem, this, _1, _2));
- m_console.register_command("pcatmemo", CMDFLAG_NONE, AS_OPCODES, 1, 2, std::bind(&debugger_commands::execute_pcatmem, this, _1, _2));
-
- m_console.register_command("snap", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_snap, this, _1, _2));
-
- m_console.register_command("source", CMDFLAG_NONE, 0, 1, 1, std::bind(&debugger_commands::execute_source, this, _1, _2));
-
- m_console.register_command("map", CMDFLAG_NONE, AS_PROGRAM, 1, 1, std::bind(&debugger_commands::execute_map, this, _1, _2));
- m_console.register_command("mapd", CMDFLAG_NONE, AS_DATA, 1, 1, std::bind(&debugger_commands::execute_map, this, _1, _2));
- m_console.register_command("mapi", CMDFLAG_NONE, AS_IO, 1, 1, std::bind(&debugger_commands::execute_map, this, _1, _2));
- m_console.register_command("mapo", CMDFLAG_NONE, AS_OPCODES, 1, 1, std::bind(&debugger_commands::execute_map, this, _1, _2));
- m_console.register_command("memdump", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_memdump, this, _1, _2));
-
- m_console.register_command("symlist", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_symlist, this, _1, _2));
-
- m_console.register_command("softreset", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_softreset, this, _1, _2));
- m_console.register_command("hardreset", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_hardreset, this, _1, _2));
-
- m_console.register_command("images", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_images, this, _1, _2));
- m_console.register_command("mount", CMDFLAG_NONE, 0, 2, 2, std::bind(&debugger_commands::execute_mount, this, _1, _2));
- m_console.register_command("unmount", CMDFLAG_NONE, 0, 1, 1, std::bind(&debugger_commands::execute_unmount, this, _1, _2));
-
- m_console.register_command("input", CMDFLAG_NONE, 0, 1, 1, std::bind(&debugger_commands::execute_input, this, _1, _2));
- m_console.register_command("dumpkbd", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_dumpkbd, this, _1, _2));
+ std::bind(&debugger_commands::global_get, this, &m_global_array[itemnum]),
+ std::bind(&debugger_commands::global_set, this, &m_global_array[itemnum], _1));
+ }
+ }
+
+ // add all the commands
+ m_console.register_command("help", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_help, this, _1));
+ m_console.register_command("print", CMDFLAG_NONE, 1, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_print, this, _1));
+ m_console.register_command("printf", CMDFLAG_NONE, 1, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_printf, this, _1));
+ m_console.register_command("logerror", CMDFLAG_NONE, 1, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_logerror, this, _1));
+ m_console.register_command("tracelog", CMDFLAG_NONE, 1, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_tracelog, this, _1));
+ m_console.register_command("tracesym", CMDFLAG_NONE, 1, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_tracesym, this, _1));
+ m_console.register_command("cls", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_cls, this, _1));
+ m_console.register_command("quit", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_quit, this, _1));
+ m_console.register_command("exit", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_quit, this, _1));
+ m_console.register_command("do", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_do, this, _1));
+ m_console.register_command("step", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_step, this, _1));
+ m_console.register_command("s", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_step, this, _1));
+ m_console.register_command("over", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_over, this, _1));
+ m_console.register_command("o", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_over, this, _1));
+ m_console.register_command("out" , CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_out, this, _1));
+ m_console.register_command("go", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_go, this, _1));
+ m_console.register_command("g", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_go, this, _1));
+ m_console.register_command("gvblank", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_go_vblank, this, _1));
+ m_console.register_command("gv", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_go_vblank, this, _1));
+ m_console.register_command("gint", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_go_interrupt, this, _1));
+ m_console.register_command("gi", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_go_interrupt, this, _1));
+ m_console.register_command("gex", CMDFLAG_NONE, 0, 2, std::bind(&debugger_commands::execute_go_exception, this, _1));
+ m_console.register_command("ge", CMDFLAG_NONE, 0, 2, std::bind(&debugger_commands::execute_go_exception, this, _1));
+ m_console.register_command("gtime", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_go_time, this, _1));
+ m_console.register_command("gt", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_go_time, this, _1));
+ m_console.register_command("gp", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_go_privilege, this, _1));
+ m_console.register_command("gbt", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_go_branch, this, true, _1));
+ m_console.register_command("gbf", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_go_branch, this, false, _1));
+ m_console.register_command("gni", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_go_next_instruction, this, _1));
+ m_console.register_command("next", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_next, this, _1));
+ m_console.register_command("n", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_next, this, _1));
+ m_console.register_command("focus", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_focus, this, _1));
+ m_console.register_command("ignore", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_ignore, this, _1));
+ m_console.register_command("observe", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_observe, this, _1));
+ m_console.register_command("suspend", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_suspend, this, _1));
+ m_console.register_command("resume", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_resume, this, _1));
+ m_console.register_command("cpulist", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_cpulist, this, _1));
+ m_console.register_command("time", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_time, this, _1));
+
+ m_console.register_command("comadd", CMDFLAG_NONE, 1, 2, std::bind(&debugger_commands::execute_comment_add, this, _1));
+ m_console.register_command("//", CMDFLAG_NONE, 1, 2, std::bind(&debugger_commands::execute_comment_add, this, _1));
+ m_console.register_command("comdelete", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_comment_del, this, _1));
+ m_console.register_command("comsave", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_comment_save, this, _1));
+ m_console.register_command("comlist", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_comment_list, this, _1));
+ m_console.register_command("commit", CMDFLAG_NONE, 1, 2, std::bind(&debugger_commands::execute_comment_commit, this, _1));
+ m_console.register_command("/*", CMDFLAG_NONE, 1, 2, std::bind(&debugger_commands::execute_comment_commit, this, _1));
+
+ m_console.register_command("bpset", CMDFLAG_NONE, 1, 3, std::bind(&debugger_commands::execute_bpset, this, _1));
+ m_console.register_command("bp", CMDFLAG_NONE, 1, 3, std::bind(&debugger_commands::execute_bpset, this, _1));
+ m_console.register_command("bpclear", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_bpclear, this, _1));
+ m_console.register_command("bpdisable", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_bpdisenable, this, false, _1));
+ m_console.register_command("bpenable", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_bpdisenable, this, true, _1));
+ m_console.register_command("bplist", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_bplist, this, _1));
+
+ m_console.register_command("wpset", CMDFLAG_NONE, 3, 5, std::bind(&debugger_commands::execute_wpset, this, -1, _1));
+ m_console.register_command("wp", CMDFLAG_NONE, 3, 5, std::bind(&debugger_commands::execute_wpset, this, -1, _1));
+ m_console.register_command("wpdset", CMDFLAG_NONE, 3, 5, std::bind(&debugger_commands::execute_wpset, this, AS_DATA, _1));
+ m_console.register_command("wpd", CMDFLAG_NONE, 3, 5, std::bind(&debugger_commands::execute_wpset, this, AS_DATA, _1));
+ m_console.register_command("wpiset", CMDFLAG_NONE, 3, 5, std::bind(&debugger_commands::execute_wpset, this, AS_IO, _1));
+ m_console.register_command("wpi", CMDFLAG_NONE, 3, 5, std::bind(&debugger_commands::execute_wpset, this, AS_IO, _1));
+ m_console.register_command("wposet", CMDFLAG_NONE, 3, 5, std::bind(&debugger_commands::execute_wpset, this, AS_OPCODES, _1));
+ m_console.register_command("wpo", CMDFLAG_NONE, 3, 5, std::bind(&debugger_commands::execute_wpset, this, AS_OPCODES, _1));
+ m_console.register_command("wpclear", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_wpclear, this, _1));
+ m_console.register_command("wpdisable", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_wpdisenable, this, false, _1));
+ m_console.register_command("wpenable", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_wpdisenable, this, true, _1));
+ m_console.register_command("wplist", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_wplist, this, _1));
+
+ m_console.register_command("rpset", CMDFLAG_NONE, 1, 2, std::bind(&debugger_commands::execute_rpset, this, _1));
+ m_console.register_command("rp", CMDFLAG_NONE, 1, 2, std::bind(&debugger_commands::execute_rpset, this, _1));
+ m_console.register_command("rpclear", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_rpclear, this, _1));
+ m_console.register_command("rpdisable", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_rpdisenable, this, false, _1));
+ m_console.register_command("rpenable", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_rpdisenable, this, true, _1));
+ m_console.register_command("rplist", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_rplist, this, _1));
+
+ m_console.register_command("epset", CMDFLAG_NONE, 1, 3, std::bind(&debugger_commands::execute_epset, this, _1));
+ m_console.register_command("ep", CMDFLAG_NONE, 1, 3, std::bind(&debugger_commands::execute_epset, this, _1));
+ m_console.register_command("epclear", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_epclear, this, _1));
+ m_console.register_command("epdisable", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_epdisenable, this, false, _1));
+ m_console.register_command("epenable", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_epdisenable, this, true, _1));
+ m_console.register_command("eplist", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_eplist, this, _1));
+
+ m_console.register_command("statesave", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_statesave, this, _1));
+ m_console.register_command("ss", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_statesave, this, _1));
+ m_console.register_command("stateload", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_stateload, this, _1));
+ m_console.register_command("sl", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_stateload, this, _1));
+
+ m_console.register_command("rewind", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_rewind, this, _1));
+ m_console.register_command("rw", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_rewind, this, _1));
+
+ m_console.register_command("save", CMDFLAG_NONE, 3, 3, std::bind(&debugger_commands::execute_save, this, -1, _1));
+ m_console.register_command("saved", CMDFLAG_NONE, 3, 3, std::bind(&debugger_commands::execute_save, this, AS_DATA, _1));
+ m_console.register_command("savei", CMDFLAG_NONE, 3, 3, std::bind(&debugger_commands::execute_save, this, AS_IO, _1));
+ m_console.register_command("saveo", CMDFLAG_NONE, 3, 3, std::bind(&debugger_commands::execute_save, this, AS_OPCODES, _1));
+ m_console.register_command("saver", CMDFLAG_NONE, 4, 4, std::bind(&debugger_commands::execute_saveregion, this, _1));
+
+ m_console.register_command("load", CMDFLAG_NONE, 2, 3, std::bind(&debugger_commands::execute_load, this, -1, _1));
+ m_console.register_command("loadd", CMDFLAG_NONE, 2, 3, std::bind(&debugger_commands::execute_load, this, AS_DATA, _1));
+ m_console.register_command("loadi", CMDFLAG_NONE, 2, 3, std::bind(&debugger_commands::execute_load, this, AS_IO, _1));
+ m_console.register_command("loado", CMDFLAG_NONE, 2, 3, std::bind(&debugger_commands::execute_load, this, AS_OPCODES, _1));
+ m_console.register_command("loadr", CMDFLAG_NONE, 4, 4, std::bind(&debugger_commands::execute_loadregion, this, _1));
+
+ m_console.register_command("dump", CMDFLAG_NONE, 3, 6, std::bind(&debugger_commands::execute_dump, this, -1, _1));
+ m_console.register_command("dumpd", CMDFLAG_NONE, 3, 6, std::bind(&debugger_commands::execute_dump, this, AS_DATA, _1));
+ m_console.register_command("dumpi", CMDFLAG_NONE, 3, 6, std::bind(&debugger_commands::execute_dump, this, AS_IO, _1));
+ m_console.register_command("dumpo", CMDFLAG_NONE, 3, 6, std::bind(&debugger_commands::execute_dump, this, AS_OPCODES, _1));
+
+ m_console.register_command("strdump", CMDFLAG_NONE, 3, 4, std::bind(&debugger_commands::execute_strdump, this, -1, _1));
+ m_console.register_command("strdumpd", CMDFLAG_NONE, 3, 4, std::bind(&debugger_commands::execute_strdump, this, AS_DATA, _1));
+ m_console.register_command("strdumpi", CMDFLAG_NONE, 3, 4, std::bind(&debugger_commands::execute_strdump, this, AS_IO, _1));
+ m_console.register_command("strdumpo", CMDFLAG_NONE, 3, 4, std::bind(&debugger_commands::execute_strdump, this, AS_OPCODES, _1));
+
+ m_console.register_command("cheatinit", CMDFLAG_NONE, 0, 4, std::bind(&debugger_commands::execute_cheatrange, this, true, _1));
+ m_console.register_command("ci", CMDFLAG_NONE, 0, 4, std::bind(&debugger_commands::execute_cheatrange, this, true, _1));
+
+ m_console.register_command("cheatrange",CMDFLAG_NONE, 2, 2, std::bind(&debugger_commands::execute_cheatrange, this, false, _1));
+ m_console.register_command("cr", CMDFLAG_NONE, 2, 2, std::bind(&debugger_commands::execute_cheatrange, this, false, _1));
+
+ m_console.register_command("cheatnext", CMDFLAG_NONE, 1, 2, std::bind(&debugger_commands::execute_cheatnext, this, false, _1));
+ m_console.register_command("cn", CMDFLAG_NONE, 1, 2, std::bind(&debugger_commands::execute_cheatnext, this, false, _1));
+ m_console.register_command("cheatnextf",CMDFLAG_NONE, 1, 2, std::bind(&debugger_commands::execute_cheatnext, this, true, _1));
+ m_console.register_command("cnf", CMDFLAG_NONE, 1, 2, std::bind(&debugger_commands::execute_cheatnext, this, true, _1));
+
+ m_console.register_command("cheatlist", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_cheatlist, this, _1));
+ m_console.register_command("cl", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_cheatlist, this, _1));
+
+ m_console.register_command("cheatundo", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_cheatundo, this, _1));
+ m_console.register_command("cu", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_cheatundo, this, _1));
+
+ m_console.register_command("f", CMDFLAG_KEEP_QUOTES, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_find, this, -1, _1));
+ m_console.register_command("find", CMDFLAG_KEEP_QUOTES, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_find, this, -1, _1));
+ m_console.register_command("fd", CMDFLAG_KEEP_QUOTES, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_find, this, AS_DATA, _1));
+ m_console.register_command("findd", CMDFLAG_KEEP_QUOTES, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_find, this, AS_DATA, _1));
+ m_console.register_command("fi", CMDFLAG_KEEP_QUOTES, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_find, this, AS_IO, _1));
+ m_console.register_command("findi", CMDFLAG_KEEP_QUOTES, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_find, this, AS_IO, _1));
+ m_console.register_command("fo", CMDFLAG_KEEP_QUOTES, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_find, this, AS_OPCODES, _1));
+ m_console.register_command("findo", CMDFLAG_KEEP_QUOTES, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_find, this, AS_OPCODES, _1));
+
+ m_console.register_command("fill", CMDFLAG_KEEP_QUOTES, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_fill, this, -1, _1));
+ m_console.register_command("filld", CMDFLAG_KEEP_QUOTES, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_fill, this, AS_DATA, _1));
+ m_console.register_command("filli", CMDFLAG_KEEP_QUOTES, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_fill, this, AS_IO, _1));
+ m_console.register_command("fillo", CMDFLAG_KEEP_QUOTES, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_fill, this, AS_OPCODES, _1));
+
+ m_console.register_command("dasm", CMDFLAG_NONE, 3, 5, std::bind(&debugger_commands::execute_dasm, this, _1));
+
+ m_console.register_command("trace", CMDFLAG_NONE, 1, 4, std::bind(&debugger_commands::execute_trace, this, _1, false));
+ m_console.register_command("traceover", CMDFLAG_NONE, 1, 4, std::bind(&debugger_commands::execute_trace, this, _1, true));
+ m_console.register_command("traceflush",CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_traceflush, this, _1));
+
+ m_console.register_command("history", CMDFLAG_NONE, 0, 2, std::bind(&debugger_commands::execute_history, this, _1));
+ m_console.register_command("trackpc", CMDFLAG_NONE, 0, 3, std::bind(&debugger_commands::execute_trackpc, this, _1));
+
+ m_console.register_command("trackmem", CMDFLAG_NONE, 0, 3, std::bind(&debugger_commands::execute_trackmem, this, _1));
+ m_console.register_command("pcatmem", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_pcatmem, this, -1, _1));
+ m_console.register_command("pcatmemd", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_pcatmem, this, AS_DATA, _1));
+ m_console.register_command("pcatmemi", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_pcatmem, this, AS_IO, _1));
+ m_console.register_command("pcatmemo", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_pcatmem, this, AS_OPCODES, _1));
+
+ m_console.register_command("snap", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_snap, this, _1));
+
+ m_console.register_command("source", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_source, this, _1));
+
+ m_console.register_command("map", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_map, this, -1, _1));
+ m_console.register_command("mapd", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_map, this, AS_DATA, _1));
+ m_console.register_command("mapi", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_map, this, AS_IO, _1));
+ m_console.register_command("mapo", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_map, this, AS_OPCODES, _1));
+ m_console.register_command("memdump", CMDFLAG_NONE, 0, 2, std::bind(&debugger_commands::execute_memdump, this, _1));
+
+ m_console.register_command("symlist", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_symlist, this, _1));
+
+ m_console.register_command("softreset", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_softreset, this, _1));
+ m_console.register_command("hardreset", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_hardreset, this, _1));
+
+ m_console.register_command("images", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_images, this, _1));
+ m_console.register_command("mount", CMDFLAG_NONE, 2, 2, std::bind(&debugger_commands::execute_mount, this, _1));
+ m_console.register_command("unmount", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_unmount, this, _1));
+
+ m_console.register_command("input", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_input, this, _1));
+ m_console.register_command("dumpkbd", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_dumpkbd, this, _1));
- /* set up the initial debugscript if specified */
+ // set up the initial debugscript if specified
const char* name = m_machine.options().debug_script();
if (name[0] != 0)
m_console.source_script(name);
- m_cheat.cpu[0] = m_cheat.cpu[1] = 0;
+ m_cheat.space = nullptr;
}
-/*-------------------------------------------------
- execute_min - return the minimum of two values
--------------------------------------------------*/
-
-u64 debugger_commands::execute_min(symbol_table &table, int params, const u64 *param)
-{
- return (param[0] < param[1]) ? param[0] : param[1];
-}
+//-------------------------------------------------
+// get_cpunum - getter callback for the
+// 'cpunum' symbol
+//-------------------------------------------------
-/*-------------------------------------------------
- execute_max - return the maximum of two values
--------------------------------------------------*/
-
-u64 debugger_commands::execute_max(symbol_table &table, int params, const u64 *param)
+u64 debugger_commands::get_cpunum()
{
- return (param[0] > param[1]) ? param[0] : param[1];
-}
-
-
-/*-------------------------------------------------
- execute_if - if (a) return b; else return c;
--------------------------------------------------*/
+ unsigned index = 0;
+ for (device_execute_interface &exec : execute_interface_enumerator(m_machine.root_device()))
+ {
+ if (m_console.get_visible_cpu() == &exec.device())
+ return index;
-u64 debugger_commands::execute_if(symbol_table &table, int params, const u64 *param)
-{
- return param[0] ? param[1] : param[2];
+ // real CPUs should have pcbase
+ device_state_interface const *state;
+ if (exec.device().interface(state) && state->state_find_entry(STATE_GENPCBASE))
+ ++index;
+ }
+ return u64(s64(-1));
}
-
/***************************************************************************
GLOBAL ACCESSORS
***************************************************************************/
@@ -340,14 +399,14 @@ u64 debugger_commands::execute_if(symbol_table &table, int params, const u64 *pa
global_get - symbol table getter for globals
-------------------------------------------------*/
-u64 debugger_commands::global_get(symbol_table &table, global_entry *global)
+u64 debugger_commands::global_get(global_entry *global)
{
switch (global->size)
{
- case 1: return *(u8 *)global->base;
- case 2: return *(u16 *)global->base;
- case 4: return *(u32 *)global->base;
- case 8: return *(u64 *)global->base;
+ case 1: return *(u8 *)global->base;
+ case 2: return *(u16 *)global->base;
+ case 4: return *(u32 *)global->base;
+ case 8: return *(u64 *)global->base;
}
return ~0;
}
@@ -357,210 +416,33 @@ u64 debugger_commands::global_get(symbol_table &table, global_entry *global)
global_set - symbol table setter for globals
-------------------------------------------------*/
-void debugger_commands::global_set(symbol_table &table, global_entry *global, u64 value)
+void debugger_commands::global_set(global_entry *global, u64 value)
{
switch (global->size)
{
- case 1: *(u8 *)global->base = value; break;
- case 2: *(u16 *)global->base = value; break;
- case 4: *(u32 *)global->base = value; break;
- case 8: *(u64 *)global->base = value; break;
- }
-}
-
-
-
-/***************************************************************************
- PARAMETER VALIDATION HELPERS
-***************************************************************************/
-
-/*-------------------------------------------------
- validate_number_parameter - validates a
- number parameter
--------------------------------------------------*/
-
-bool debugger_commands::validate_number_parameter(const std::string &param, u64 &result)
-{
- /* evaluate the expression; success if no error */
- try
- {
- parsed_expression expression(m_cpu.get_visible_symtable(), param.c_str(), &result);
- return true;
- }
- catch (expression_error &error)
- {
- /* print an error pointing to the character that caused it */
- m_console.printf("Error in expression: %s\n", param);
- m_console.printf(" %*s^", error.offset(), "");
- m_console.printf("%s\n", error.code_string());
- return false;
+ case 1: *(u8 *)global->base = value; break;
+ case 2: *(u16 *)global->base = value; break;
+ case 4: *(u32 *)global->base = value; break;
+ case 8: *(u64 *)global->base = value; break;
}
}
-/*-------------------------------------------------
- validate_boolean_parameter - validates a
- boolean parameter
--------------------------------------------------*/
-
-bool debugger_commands::validate_boolean_parameter(const std::string &param, bool &result)
-{
- /* nullptr parameter does nothing and returns no error */
- if (param.empty())
- return true;
-
- /* evaluate the expression; success if no error */
- bool is_true = core_stricmp(param.c_str(), "true") == 0 || param == "1";
- bool is_false = core_stricmp(param.c_str(), "false") == 0 || param == "0";
-
- if (!is_true && !is_false)
- {
- m_console.printf("Invalid boolean '%s'\n", param);
- return false;
- }
-
- result = is_true;
-
- return true;
-}
-
-/*-------------------------------------------------
- validate_cpu_parameter - validates a
- parameter as a cpu
--------------------------------------------------*/
-
-bool debugger_commands::validate_cpu_parameter(const char *param, device_t *&result)
-{
- /* if no parameter, use the visible CPU */
- if (param == nullptr)
- {
- result = m_cpu.get_visible_cpu();
- if (!result)
- {
- m_console.printf("No valid CPU is currently selected\n");
- return false;
- }
- return true;
- }
-
- /* first look for a tag match */
- result = m_machine.root_device().subdevice(param);
- if (result)
- return true;
-
- /* then evaluate as an expression; on an error assume it was a tag */
- u64 cpunum;
- try
- {
- parsed_expression expression(m_cpu.get_visible_symtable(), param, &cpunum);
- }
- catch (expression_error &)
- {
- m_console.printf("Unable to find CPU '%s'\n", param);
- return false;
- }
-
- // attempt to find by numerical index
- int index = 0;
- for (device_execute_interface &exec : execute_interface_iterator(m_machine.root_device()))
- {
- // real CPUs should have pcbase
- const device_state_interface *state;
- if (exec.device().interface(state) && state->state_find_entry(STATE_GENPCBASE) != nullptr && index++ == cpunum)
- {
- result = &exec.device();
- return true;
- }
- }
-
- /* if out of range, complain */
- m_console.printf("Invalid CPU index %d\n", (int)cpunum);
- return false;
-}
-
-
-/*-------------------------------------------------
- validate_cpu_space_parameter - validates
- a parameter as a cpu and retrieves the given
- address space
--------------------------------------------------*/
-
-bool debugger_commands::validate_cpu_space_parameter(const char *param, int spacenum, address_space *&result)
-{
- /* first do the standard CPU thing */
- device_t *cpu;
- if (!validate_cpu_parameter(param, cpu))
- return false;
-
- /* fetch the space pointer */
- if (!cpu->memory().has_space(spacenum))
- {
- m_console.printf("No matching memory space found for CPU '%s'\n", cpu->tag());
- return false;
- }
- result = &cpu->memory().space(spacenum);
- return true;
-}
-
-
-/*-------------------------------------------------
- debug_command_parameter_expression - validates
- an expression parameter
--------------------------------------------------*/
-
-bool debugger_commands::debug_command_parameter_expression(const std::string &param, parsed_expression &result)
-{
- /* parse the expression; success if no error */
- try
- {
- result.parse(param.c_str());
- return true;
- }
- catch (expression_error &err)
- {
- /* output an error */
- m_console.printf("Error in expression: %s\n", param);
- m_console.printf(" %*s^", err.offset(), "");
- m_console.printf("%s\n", err.code_string());
- return false;
- }
-}
-
-
-/*-------------------------------------------------
- debug_command_parameter_command - validates a
- command parameter
--------------------------------------------------*/
-
-bool debugger_commands::debug_command_parameter_command(const char *param)
-{
- /* nullptr parameter does nothing and returns no error */
- if (param == nullptr)
- return true;
-
- /* validate the comment; success if no error */
- CMDERR err = m_console.validate_command(param);
- if (err == CMDERR_NONE)
- return true;
-
- /* output an error */
- m_console.printf("Error in command: %s\n", param);
- m_console.printf(" %*s^", CMDERR_ERROR_OFFSET(err), "");
- m_console.printf("%s\n", debugger_console::cmderr_to_string(err));
- return 0;
-}
+//**************************************************************************
+// COMMAND IMPLEMENTATIONS
+//**************************************************************************
/*-------------------------------------------------
execute_help - execute the help command
-------------------------------------------------*/
-void debugger_commands::execute_help(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_help(const std::vector<std::string_view> &params)
{
if (params.size() == 0)
- m_console.printf_wrap(80, "%s\n", debug_get_help(""));
+ m_console.printf_wrap(80, "%s\n", debug_get_help(std::string_view()));
else
- m_console.printf_wrap(80, "%s\n", debug_get_help(params[0].c_str()));
+ m_console.printf_wrap(80, "%s\n", debug_get_help(params[0]));
}
@@ -568,12 +450,12 @@ void debugger_commands::execute_help(int ref, const std::vector<std::string> &pa
execute_print - execute the print command
-------------------------------------------------*/
-void debugger_commands::execute_print(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_print(const std::vector<std::string_view> &params)
{
/* validate the other parameters */
u64 values[MAX_COMMAND_PARAMS];
for (int i = 0; i < params.size(); i++)
- if (!validate_number_parameter(params[i], values[i]))
+ if (!m_console.validate_number_parameter(params[i], values[i]))
return;
/* then print each one */
@@ -587,124 +469,221 @@ void debugger_commands::execute_print(int ref, const std::vector<std::string> &p
mini_printf - safe printf to a buffer
-------------------------------------------------*/
-int debugger_commands::mini_printf(char *buffer, const char *format, int params, u64 *param)
+bool debugger_commands::mini_printf(std::ostream &stream, const std::vector<std::string_view> &params)
{
- const char *f = format;
- char *p = buffer;
+ std::string_view const format(params[0]);
+ auto f = format.begin();
- /* parse the string looking for % signs */
- for (;;)
+ int param = 1;
+ u64 number;
+
+ // parse the string looking for % signs
+ while (f != format.end())
{
char c = *f++;
- if (!c) break;
- /* escape sequences */
+ // escape sequences
if (c == '\\')
{
+ if (f == format.end()) break;
c = *f++;
- if (!c) break;
switch (c)
{
- case '\\': *p++ = c; break;
- case 'n': *p++ = '\n'; break;
+ case '\\': stream << c; break;
+ case 'n': stream << '\n'; break;
default: break;
}
continue;
}
- /* formatting */
+ // formatting
else if (c == '%')
{
+ bool left_justify = false;
+ bool zero_fill = false;
int width = 0;
- int zerofill = 0;
+ int precision = 0;
+
+ // parse optional left justification flag
+ if (f != format.end() && *f == '-')
+ {
+ left_justify = true;
+ f++;
+ }
- /* parse out the width */
- for (;;)
+ // parse optional zero fill flag
+ if (f != format.end() && *f == '0')
{
- c = *f++;
- if (!c || c < '0' || c > '9') break;
- if (c == '0' && width == 0)
- zerofill = 1;
- width = width * 10 + (c - '0');
+ zero_fill = true;
+ f++;
+ }
+
+ // parse optional width
+ while (f != format.end() && isdigit(*f))
+ width = width * 10 + (*f++ - '0');
+ if (f == format.end())
+ break;
+
+ // apply left justification
+ if (left_justify)
+ width = -width;
+
+ if ((c = *f++) == '.')
+ {
+ // parse optional precision
+ while (f != format.end() && isdigit(*f))
+ precision = precision * 10 + (*f++ - '0');
+
+ // get the format
+ if (f != format.end())
+ c = *f++;
+ else
+ break;
}
- if (!c) break;
- /* get the format */
switch (c)
{
case '%':
- *p++ = c;
+ stream << c;
break;
case 'X':
+ if (param < params.size() && m_console.validate_number_parameter(params[param++], number))
+ util::stream_format(stream, zero_fill ? "%0*X" : "%*X", width, number);
+ else
+ {
+ m_console.printf("Not enough parameters for format!\n");
+ return false;
+ }
+ break;
case 'x':
- if (params == 0)
+ if (param < params.size() && m_console.validate_number_parameter(params[param++], number))
+ util::stream_format(stream, zero_fill ? "%0*x" : "%*x", width, number);
+ else
{
m_console.printf("Not enough parameters for format!\n");
- return 0;
+ return false;
+ }
+ break;
+
+ case 'O':
+ case 'o':
+ if (param < params.size() && m_console.validate_number_parameter(params[param++], number))
+ util::stream_format(stream, zero_fill ? "%0*o" : "%*o", width, number);
+ else
+ {
+ m_console.printf("Not enough parameters for format!\n");
+ return false;
}
- if (u32(*param >> 32) != 0)
- p += sprintf(p, zerofill ? "%0*X" : "%*X", (width <= 8) ? 1 : width - 8, u32(*param >> 32));
- else if (width > 8)
- p += sprintf(p, zerofill ? "%0*X" : "%*X", width - 8, 0);
- p += sprintf(p, zerofill ? "%0*X" : "%*X", (width < 8) ? width : 8, u32(*param));
- param++;
- params--;
break;
case 'D':
case 'd':
- if (params == 0)
+ if (param < params.size() && m_console.validate_number_parameter(params[param++], number))
+ util::stream_format(stream, zero_fill ? "%0*d" : "%*d", width, number);
+ else
{
m_console.printf("Not enough parameters for format!\n");
- return 0;
+ return false;
}
- p += sprintf(p, zerofill ? "%0*d" : "%*d", width, u32(*param));
- param++;
- params--;
break;
+
case 'C':
case 'c':
- if (params == 0)
+ if (param < params.size() && m_console.validate_number_parameter(params[param++], number))
+ stream << char(number);
+ else
{
m_console.printf("Not enough parameters for format!\n");
- return 0;
+ return false;
}
- p += sprintf(p, "%c", char(*param));
- param++;
- params--;
break;
+ case 's':
+ {
+ address_space *space;
+ if (param < params.size() && m_console.validate_target_address_parameter(params[param++], -1, space, number))
+ {
+ address_space *tspace;
+ std::string s;
+
+ for (u32 address = u32(number), taddress; space->device().memory().translate(space->spacenum(), device_memory_interface::TR_READ, taddress = address, tspace); address++)
+ {
+ u8 const data = tspace->read_byte(taddress);
+
+ if (!data)
+ break;
+
+ s += data;
+
+ if (precision == 1)
+ break;
+ else if (precision)
+ precision--;
+ }
+
+ util::stream_format(stream, "%*s", width, s);
+ }
+ else
+ {
+ m_console.printf("Not enough parameters for format!\n");
+ return false;
+ }
+ }
+ break;
}
}
- /* normal stuff */
+ // normal stuff
else
- *p++ = c;
+ stream << c;
}
- /* NULL-terminate and exit */
- *p = 0;
- return 1;
+ return true;
}
/*-------------------------------------------------
- execute_printf - execute the printf command
+ execute_index_command - helper for commands
+ that take multiple indices as arguments
-------------------------------------------------*/
-void debugger_commands::execute_printf(int ref, const std::vector<std::string> &params)
+template <typename T>
+void debugger_commands::execute_index_command(std::vector<std::string_view> const &params, T &&apply, char const *unused_message)
{
- /* validate the other parameters */
- u64 values[MAX_COMMAND_PARAMS];
- for (int i = 1; i < params.size(); i++)
- if (!validate_number_parameter(params[i], values[i]))
+ std::vector<u64> index(params.size());
+ for (int paramnum = 0; paramnum < params.size(); paramnum++)
+ {
+ if (!m_console.validate_number_parameter(params[paramnum], index[paramnum]))
return;
+ }
+ for (device_t &device : device_enumerator(m_machine.root_device()))
+ {
+ for (auto param = index.begin(); index.end() != param; )
+ {
+ if (apply(device, *param))
+ param = index.erase(param);
+ else
+ ++param;
+ }
+ }
+
+ for (auto const &param : index)
+ m_console.printf(unused_message, param);
+}
+
+
+/*-------------------------------------------------
+ execute_printf - execute the printf command
+-------------------------------------------------*/
+
+void debugger_commands::execute_printf(const std::vector<std::string_view> &params)
+{
/* then do a printf */
- char buffer[1024];
- if (mini_printf(buffer, params[0].c_str(), params.size() - 1, &values[1]))
- m_console.printf("%s\n", buffer);
+ std::ostringstream buffer;
+ if (mini_printf(buffer, params))
+ m_console.printf("%s\n", std::move(buffer).str());
}
@@ -712,18 +691,12 @@ void debugger_commands::execute_printf(int ref, const std::vector<std::string> &
execute_logerror - execute the logerror command
-------------------------------------------------*/
-void debugger_commands::execute_logerror(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_logerror(const std::vector<std::string_view> &params)
{
- /* validate the other parameters */
- u64 values[MAX_COMMAND_PARAMS];
- for (int i = 1; i < params.size(); i++)
- if (!validate_number_parameter(params[i], values[i]))
- return;
-
/* then do a printf */
- char buffer[1024];
- if (mini_printf(buffer, params[0].c_str(), params.size() - 1, &values[1]))
- m_machine.logerror("%s", buffer);
+ std::ostringstream buffer;
+ if (mini_printf(buffer, params))
+ m_machine.logerror("%s", std::move(buffer).str());
}
@@ -731,18 +704,12 @@ void debugger_commands::execute_logerror(int ref, const std::vector<std::string>
execute_tracelog - execute the tracelog command
-------------------------------------------------*/
-void debugger_commands::execute_tracelog(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_tracelog(const std::vector<std::string_view> &params)
{
- /* validate the other parameters */
- u64 values[MAX_COMMAND_PARAMS];
- for (int i = 1; i < params.size(); i++)
- if (!validate_number_parameter(params[i], values[i]))
- return;
-
/* then do a printf */
- char buffer[1024];
- if (mini_printf(buffer, params[0].c_str(), params.size() - 1, &values[1]))
- m_cpu.get_visible_cpu()->debug()->trace_printf("%s", buffer);
+ std::ostringstream buffer;
+ if (mini_printf(buffer, params))
+ m_console.get_visible_cpu()->debug()->trace_printf("%s", std::move(buffer).str());
}
@@ -750,18 +717,17 @@ void debugger_commands::execute_tracelog(int ref, const std::vector<std::string>
execute_tracesym - execute the tracesym command
-------------------------------------------------*/
-void debugger_commands::execute_tracesym(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_tracesym(const std::vector<std::string_view> &params)
{
// build a format string appropriate for the parameters and validate them
- std::stringstream format;
- u64 values[MAX_COMMAND_PARAMS];
+ std::ostringstream format;
for (int i = 0; i < params.size(); i++)
{
// find this symbol
- symbol_entry *sym = m_cpu.get_visible_symtable()->find(params[i].c_str());
+ symbol_entry *const sym = m_console.visible_symtable().find(strmakelower(params[i]).c_str());
if (!sym)
{
- m_console.printf("Unknown symbol: %s\n", params[i].c_str());
+ m_console.printf("Unknown symbol: %s\n", params[i]);
return;
}
@@ -769,16 +735,29 @@ void debugger_commands::execute_tracesym(int ref, const std::vector<std::string>
util::stream_format(format, "%s=%s ",
params[i],
sym->format().empty() ? "%16X" : sym->format());
-
- // validate the parameter
- if (!validate_number_parameter(params[i], values[i]))
- return;
}
+ // build parameters for printf
+ auto const format_str = std::move(format).str(); // need this to stay put as long as the string_view exists
+ std::vector<std::string_view> printf_params;
+ printf_params.reserve(params.size() + 1);
+ printf_params.emplace_back(format_str);
+ std::copy(params.begin(), params.end(), std::back_inserter(printf_params));
+
// then do a printf
- char buffer[1024];
- if (mini_printf(buffer, format.str().c_str(), params.size(), values))
- m_cpu.get_visible_cpu()->debug()->trace_printf("%s", buffer);
+ std::ostringstream buffer;
+ if (mini_printf(buffer, printf_params))
+ m_console.get_visible_cpu()->debug()->trace_printf("%s", std::move(buffer).str());
+}
+
+
+/*-------------------------------------------------
+ execute_cls - execute the cls command
+-------------------------------------------------*/
+
+void debugger_commands::execute_cls(const std::vector<std::string_view> &params)
+{
+ text_buffer_clear(m_console.get_console_textbuf());
}
@@ -786,7 +765,7 @@ void debugger_commands::execute_tracesym(int ref, const std::vector<std::string>
execute_quit - execute the quit command
-------------------------------------------------*/
-void debugger_commands::execute_quit(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_quit(const std::vector<std::string_view> &params)
{
osd_printf_warning("Exited via the debugger\n");
m_machine.schedule_exit();
@@ -797,10 +776,10 @@ void debugger_commands::execute_quit(int ref, const std::vector<std::string> &pa
execute_do - execute the do command
-------------------------------------------------*/
-void debugger_commands::execute_do(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_do(const std::vector<std::string_view> &params)
{
u64 dummy;
- validate_number_parameter(params[0], dummy);
+ m_console.validate_number_parameter(params[0], dummy);
}
@@ -808,14 +787,14 @@ void debugger_commands::execute_do(int ref, const std::vector<std::string> &para
execute_step - execute the step command
-------------------------------------------------*/
-void debugger_commands::execute_step(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_step(const std::vector<std::string_view> &params)
{
/* if we have a parameter, use it */
u64 steps = 1;
- if (params.size() > 0 && !validate_number_parameter(params[0], steps))
+ if (params.size() > 0 && !m_console.validate_number_parameter(params[0], steps))
return;
- m_cpu.get_visible_cpu()->debug()->single_step(steps);
+ m_console.get_visible_cpu()->debug()->single_step(steps);
}
@@ -823,14 +802,14 @@ void debugger_commands::execute_step(int ref, const std::vector<std::string> &pa
execute_over - execute the over command
-------------------------------------------------*/
-void debugger_commands::execute_over(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_over(const std::vector<std::string_view> &params)
{
/* if we have a parameter, use it */
u64 steps = 1;
- if (params.size() > 0 && !validate_number_parameter(params[0], steps))
+ if (params.size() > 0 && !m_console.validate_number_parameter(params[0], steps))
return;
- m_cpu.get_visible_cpu()->debug()->single_step_over(steps);
+ m_console.get_visible_cpu()->debug()->single_step_over(steps);
}
@@ -838,9 +817,9 @@ void debugger_commands::execute_over(int ref, const std::vector<std::string> &pa
execute_out - execute the out command
-------------------------------------------------*/
-void debugger_commands::execute_out(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_out(const std::vector<std::string_view> &params)
{
- m_cpu.get_visible_cpu()->debug()->single_step_out();
+ m_console.get_visible_cpu()->debug()->single_step_out();
}
@@ -848,15 +827,15 @@ void debugger_commands::execute_out(int ref, const std::vector<std::string> &par
execute_go - execute the go command
-------------------------------------------------*/
-void debugger_commands::execute_go(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_go(const std::vector<std::string_view> &params)
{
u64 addr = ~0;
/* if we have a parameter, use it instead */
- if (params.size() > 0 && !validate_number_parameter(params[0], addr))
+ if (params.size() > 0 && !m_console.validate_number_parameter(params[0], addr))
return;
- m_cpu.get_visible_cpu()->debug()->go(addr);
+ m_console.get_visible_cpu()->debug()->go(addr);
}
@@ -865,9 +844,9 @@ void debugger_commands::execute_go(int ref, const std::vector<std::string> &para
command
-------------------------------------------------*/
-void debugger_commands::execute_go_vblank(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_go_vblank(const std::vector<std::string_view> &params)
{
- m_cpu.get_visible_cpu()->debug()->go_vblank();
+ m_console.get_visible_cpu()->debug()->go_vblank();
}
@@ -875,30 +854,34 @@ void debugger_commands::execute_go_vblank(int ref, const std::vector<std::string
execute_go_interrupt - execute the goint command
-------------------------------------------------*/
-void debugger_commands::execute_go_interrupt(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_go_interrupt(const std::vector<std::string_view> &params)
{
u64 irqline = -1;
/* if we have a parameter, use it instead */
- if (params.size() > 0 && !validate_number_parameter(params[0], irqline))
+ if (params.size() > 0 && !m_console.validate_number_parameter(params[0], irqline))
return;
- m_cpu.get_visible_cpu()->debug()->go_interrupt(irqline);
+ m_console.get_visible_cpu()->debug()->go_interrupt(irqline);
}
/*-------------------------------------------------
execute_go_exception - execute the goex command
-------------------------------------------------*/
-void debugger_commands::execute_go_exception(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_go_exception(const std::vector<std::string_view> &params)
{
u64 exception = -1;
/* if we have a parameter, use it instead */
- if (params.size() > 0 && !validate_number_parameter(params[0], exception))
+ if (params.size() > 0 && !m_console.validate_number_parameter(params[0], exception))
+ return;
+
+ parsed_expression condition(m_console.visible_symtable());
+ if (params.size() > 1 && !m_console.validate_expression_parameter(params[1], condition))
return;
- m_cpu.get_visible_cpu()->debug()->go_exception(exception);
+ m_console.get_visible_cpu()->debug()->go_exception(exception, condition.is_empty() ? "1" : condition.original_string());
}
@@ -906,15 +889,15 @@ void debugger_commands::execute_go_exception(int ref, const std::vector<std::str
execute_go_time - execute the gtime command
-------------------------------------------------*/
-void debugger_commands::execute_go_time(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_go_time(const std::vector<std::string_view> &params)
{
u64 milliseconds = -1;
/* if we have a parameter, use it instead */
- if (params.size() > 0 && !validate_number_parameter(params[0], milliseconds))
+ if (params.size() > 0 && !m_console.validate_number_parameter(params[0], milliseconds))
return;
- m_cpu.get_visible_cpu()->debug()->go_milliseconds(milliseconds);
+ m_console.get_visible_cpu()->debug()->go_milliseconds(milliseconds);
}
@@ -922,22 +905,78 @@ void debugger_commands::execute_go_time(int ref, const std::vector<std::string>
/*-------------------------------------------------
execute_go_privilege - execute the gp command
-------------------------------------------------*/
-void debugger_commands::execute_go_privilege(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_go_privilege(const std::vector<std::string_view> &params)
+{
+ parsed_expression condition(m_console.visible_symtable());
+ if (params.size() > 0 && !m_console.validate_expression_parameter(params[0], condition))
+ return;
+
+ m_console.get_visible_cpu()->debug()->go_privilege((condition.is_empty()) ? "1" : condition.original_string());
+}
+
+
+/*-------------------------------------------------
+ execute_go_branch - execute gbt or gbf command
+-------------------------------------------------*/
+
+void debugger_commands::execute_go_branch(bool sense, const std::vector<std::string_view> &params)
+{
+ parsed_expression condition(m_console.visible_symtable());
+ if (params.size() > 0 && !m_console.validate_expression_parameter(params[0], condition))
+ return;
+
+ m_console.get_visible_cpu()->debug()->go_branch(sense, (condition.is_empty()) ? "1" : condition.original_string());
+}
+
+
+/*-------------------------------------------------
+ execute_go_next_instruction - execute gni command
+-------------------------------------------------*/
+
+void debugger_commands::execute_go_next_instruction(const std::vector<std::string_view> &params)
{
- parsed_expression condition(&m_cpu.get_visible_cpu()->debug()->symtable());
- if (params.size() > 0 && !debug_command_parameter_expression(params[0], condition))
+ u64 count = 1;
+ static constexpr u64 MAX_COUNT = 512;
+
+ // if we have a parameter, use it instead */
+ if (params.size() > 0 && !m_console.validate_number_parameter(params[0], count))
+ return;
+ if (count == 0)
+ return;
+ if (count > MAX_COUNT)
+ {
+ m_console.printf("Too many instructions (must be %d or fewer)\n", MAX_COUNT);
+ return;
+ }
+
+ device_state_interface *stateintf;
+ device_t *cpu = m_console.get_visible_cpu();
+ if (!cpu->interface(stateintf))
+ {
+ m_console.printf("No state interface available for %s\n", cpu->name());
return;
+ }
+ u32 pc = stateintf->pcbase();
+
+ debug_disasm_buffer buffer(*cpu);
+ while (count-- != 0)
+ {
+ // disassemble the current instruction and get the length
+ u32 result = buffer.disassemble_info(pc);
+ pc = buffer.next_pc_wrap(pc, result & util::disasm_interface::LENGTHMASK);
+ }
- m_cpu.get_visible_cpu()->debug()->go_privilege((condition.is_empty()) ? "1" : condition.original_string());
+ cpu->debug()->go(pc);
}
+
/*-------------------------------------------------
execute_next - execute the next command
-------------------------------------------------*/
-void debugger_commands::execute_next(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_next(const std::vector<std::string_view> &params)
{
- m_cpu.get_visible_cpu()->debug()->go_next_device();
+ m_console.get_visible_cpu()->debug()->go_next_device();
}
@@ -945,18 +984,18 @@ void debugger_commands::execute_next(int ref, const std::vector<std::string> &pa
execute_focus - execute the focus command
-------------------------------------------------*/
-void debugger_commands::execute_focus(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_focus(const std::vector<std::string_view> &params)
{
- /* validate params */
+ // validate params
device_t *cpu;
- if (!validate_cpu_parameter(params[0].c_str(), cpu))
+ if (!m_console.validate_cpu_parameter(params[0], cpu))
return;
- /* first clear the ignore flag on the focused CPU */
+ // first clear the ignore flag on the focused CPU
cpu->debug()->ignore(false);
- /* then loop over CPUs and set the ignore flags on all other CPUs */
- for (device_execute_interface &exec : execute_interface_iterator(m_machine.root_device()))
+ // then loop over CPUs and set the ignore flags on all other CPUs
+ for (device_execute_interface &exec : execute_interface_enumerator(m_machine.root_device()))
if (&exec.device() != cpu)
exec.device().debug()->ignore(true);
m_console.printf("Now focused on CPU '%s'\n", cpu->tag());
@@ -967,17 +1006,17 @@ void debugger_commands::execute_focus(int ref, const std::vector<std::string> &p
execute_ignore - execute the ignore command
-------------------------------------------------*/
-void debugger_commands::execute_ignore(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_ignore(const std::vector<std::string_view> &params)
{
- /* if there are no parameters, dump the ignore list */
if (params.empty())
{
+ // if there are no parameters, dump the ignore list
std::string buffer;
- /* loop over all executable devices */
- for (device_execute_interface &exec : execute_interface_iterator(m_machine.root_device()))
-
- /* build up a comma-separated list */
+ // loop over all executable devices
+ for (device_execute_interface &exec : execute_interface_enumerator(m_machine.root_device()))
+ {
+ // build up a comma-separated list
if (!exec.device().debug()->observing())
{
if (buffer.empty())
@@ -985,29 +1024,29 @@ void debugger_commands::execute_ignore(int ref, const std::vector<std::string> &
else
buffer.append(string_format(", '%s'", exec.device().tag()));
}
+ }
- /* special message for none */
+ // special message for none
if (buffer.empty())
buffer = string_format("Not currently ignoring any devices");
- m_console.printf("%s\n", buffer.c_str());
+ m_console.printf("%s\n", buffer);
}
-
- /* otherwise clear the ignore flag on all requested CPUs */
else
{
+ // otherwise clear the ignore flag on all requested CPUs
device_t *devicelist[MAX_COMMAND_PARAMS];
- /* validate parameters */
+ // validate parameters
for (int paramnum = 0; paramnum < params.size(); paramnum++)
- if (!validate_cpu_parameter(params[paramnum].c_str(), devicelist[paramnum]))
+ if (!m_console.validate_cpu_parameter(params[paramnum], devicelist[paramnum]))
return;
- /* set the ignore flags */
+ // set the ignore flags
for (int paramnum = 0; paramnum < params.size(); paramnum++)
{
- /* make sure this isn't the last live CPU */
+ // make sure this isn't the last live CPU
bool gotone = false;
- for (device_execute_interface &exec : execute_interface_iterator(m_machine.root_device()))
+ for (device_execute_interface &exec : execute_interface_enumerator(m_machine.root_device()))
if (&exec.device() != devicelist[paramnum] && exec.device().debug()->observing())
{
gotone = true;
@@ -1030,17 +1069,17 @@ void debugger_commands::execute_ignore(int ref, const std::vector<std::string> &
execute_observe - execute the observe command
-------------------------------------------------*/
-void debugger_commands::execute_observe(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_observe(const std::vector<std::string_view> &params)
{
- /* if there are no parameters, dump the ignore list */
if (params.empty())
{
+ // if there are no parameters, dump the ignore list
std::string buffer;
- /* loop over all executable devices */
- for (device_execute_interface &exec : execute_interface_iterator(m_machine.root_device()))
-
- /* build up a comma-separated list */
+ // loop over all executable devices
+ for (device_execute_interface &exec : execute_interface_enumerator(m_machine.root_device()))
+ {
+ // build up a comma-separated list
if (exec.device().debug()->observing())
{
if (buffer.empty())
@@ -1048,24 +1087,24 @@ void debugger_commands::execute_observe(int ref, const std::vector<std::string>
else
buffer.append(string_format(", '%s'", exec.device().tag()));
}
+ }
- /* special message for none */
+ // special message for none
if (buffer.empty())
buffer = string_format("Not currently observing any devices");
- m_console.printf("%s\n", buffer.c_str());
+ m_console.printf("%s\n", buffer);
}
-
- /* otherwise set the ignore flag on all requested CPUs */
else
{
+ // otherwise set the ignore flag on all requested CPUs
device_t *devicelist[MAX_COMMAND_PARAMS];
- /* validate parameters */
+ // validate parameters
for (int paramnum = 0; paramnum < params.size(); paramnum++)
- if (!validate_cpu_parameter(params[paramnum].c_str(), devicelist[paramnum]))
+ if (!m_console.validate_cpu_parameter(params[paramnum], devicelist[paramnum]))
return;
- /* clear the ignore flags */
+ // clear the ignore flags
for (int paramnum = 0; paramnum < params.size(); paramnum++)
{
devicelist[paramnum]->debug()->ignore(false);
@@ -1078,17 +1117,17 @@ void debugger_commands::execute_observe(int ref, const std::vector<std::string>
execute_suspend - suspend execution on cpu
-------------------------------------------------*/
-void debugger_commands::execute_suspend(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_suspend(const std::vector<std::string_view> &params)
{
- /* if there are no parameters, dump the ignore list */
+ // if there are no parameters, dump the ignore list
if (params.empty())
{
std::string buffer;
- /* loop over all executable devices */
- for (device_execute_interface &exec : execute_interface_iterator(m_machine.root_device()))
+ // loop over all executable devices
+ for (device_execute_interface &exec : execute_interface_enumerator(m_machine.root_device()))
- /* build up a comma-separated list */
+ // build up a comma-separated list
if (exec.device().debug()->suspended())
{
if (buffer.empty())
@@ -1097,25 +1136,25 @@ void debugger_commands::execute_suspend(int ref, const std::vector<std::string>
buffer.append(string_format(", '%s'", exec.device().tag()));
}
- /* special message for none */
+ // special message for none
if (buffer.empty())
buffer = string_format("No currently suspended devices");
- m_console.printf("%s\n", buffer.c_str());
+ m_console.printf("%s\n", buffer);
}
else
{
device_t *devicelist[MAX_COMMAND_PARAMS];
- /* validate parameters */
+ // validate parameters
for (int paramnum = 0; paramnum < params.size(); paramnum++)
- if (!validate_cpu_parameter(params[paramnum].c_str(), devicelist[paramnum]))
+ if (!m_console.validate_cpu_parameter(params[paramnum], devicelist[paramnum]))
return;
for (int paramnum = 0; paramnum < params.size(); paramnum++)
{
- /* make sure this isn't the last live CPU */
+ // make sure this isn't the last live CPU
bool gotone = false;
- for (device_execute_interface &exec : execute_interface_iterator(m_machine.root_device()))
+ for (device_execute_interface &exec : execute_interface_enumerator(m_machine.root_device()))
if (&exec.device() != devicelist[paramnum] && !exec.device().debug()->suspended())
{
gotone = true;
@@ -1137,17 +1176,17 @@ void debugger_commands::execute_suspend(int ref, const std::vector<std::string>
execute_resume - Resume execution on CPU
-------------------------------------------------*/
-void debugger_commands::execute_resume(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_resume(const std::vector<std::string_view> &params)
{
- /* if there are no parameters, dump the ignore list */
+ // if there are no parameters, dump the ignore list
if (params.empty())
{
std::string buffer;
- /* loop over all executable devices */
- for (device_execute_interface &exec : execute_interface_iterator(m_machine.root_device()))
+ // loop over all executable devices
+ for (device_execute_interface &exec : execute_interface_enumerator(m_machine.root_device()))
- /* build up a comma-separated list */
+ // build up a comma-separated list
if (exec.device().debug()->suspended())
{
if (buffer.empty())
@@ -1156,18 +1195,18 @@ void debugger_commands::execute_resume(int ref, const std::vector<std::string> &
buffer.append(string_format(", '%s'", exec.device().tag()));
}
- /* special message for none */
+ // special message for none
if (buffer.empty())
buffer = string_format("No currently suspended devices");
- m_console.printf("%s\n", buffer.c_str());
+ m_console.printf("%s\n", buffer);
}
else
{
device_t *devicelist[MAX_COMMAND_PARAMS];
- /* validate parameters */
+ // validate parameters
for (int paramnum = 0; paramnum < params.size(); paramnum++)
- if (!validate_cpu_parameter(params[paramnum].c_str(), devicelist[paramnum]))
+ if (!m_console.validate_cpu_parameter(params[paramnum], devicelist[paramnum]))
return;
for (int paramnum = 0; paramnum < params.size(); paramnum++)
@@ -1178,32 +1217,56 @@ void debugger_commands::execute_resume(int ref, const std::vector<std::string> &
}
}
+//-------------------------------------------------
+// execute_cpulist - list all CPUs
+//-------------------------------------------------
+
+void debugger_commands::execute_cpulist(const std::vector<std::string_view> &params)
+{
+ int index = 0;
+ for (device_execute_interface &exec : execute_interface_enumerator(m_machine.root_device()))
+ {
+ const device_state_interface *state;
+ if (exec.device().interface(state) && state->state_find_entry(STATE_GENPCBASE) != nullptr)
+ m_console.printf("[%s%d] %s\n", &exec.device() == m_console.get_visible_cpu() ? "*" : "", index++, exec.device().tag());
+ }
+}
+
+//-------------------------------------------------
+// execute_time - execute the time command
+//-------------------------------------------------
+
+void debugger_commands::execute_time(const std::vector<std::string_view> &params)
+{
+ m_console.printf("%s\n", m_machine.time().as_string());
+}
+
/*-------------------------------------------------
execute_comment - add a comment to a line
-------------------------------------------------*/
-void debugger_commands::execute_comment_add(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_comment_add(const std::vector<std::string_view> &params)
{
- device_t *cpu;
+ // param 1 is the address for the comment
u64 address;
-
- /* param 1 is the address for the comment */
- if (!validate_number_parameter(params[0], address))
+ if (!m_console.validate_number_parameter(params[0], address))
return;
- /* CPU parameter is implicit */
- if (!validate_cpu_parameter(nullptr, cpu))
+ // CPU parameter is implicit
+ device_t *cpu;
+ if (!m_console.validate_cpu_parameter(std::string_view(), cpu))
return;
- /* make sure param 2 exists */
+ // make sure param 2 exists
if (params[1].empty())
{
m_console.printf("Error : comment text empty\n");
return;
}
- /* Now try adding the comment */
- cpu->debug()->comment_add(address, params[1].c_str(), 0x00ff0000);
+ // Now try adding the comment
+ std::string const text(params[1]);
+ cpu->debug()->comment_add(address, text.c_str(), 0x00ff0000);
cpu->machine().debug_view().update_all(DVT_DISASSEMBLY);
}
@@ -1212,57 +1275,56 @@ void debugger_commands::execute_comment_add(int ref, const std::vector<std::stri
execute_comment_del - remove a comment from an addr
--------------------------------------------------------*/
-void debugger_commands::execute_comment_del(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_comment_del(const std::vector<std::string_view> &params)
{
- device_t *cpu;
+ // param 1 can either be a command or the address for the comment
u64 address;
-
- /* param 1 can either be a command or the address for the comment */
- if (!validate_number_parameter(params[0], address))
+ if (!m_console.validate_number_parameter(params[0], address))
return;
- /* CPU parameter is implicit */
- if (!validate_cpu_parameter(nullptr, cpu))
+ // CPU parameter is implicit
+ device_t *cpu;
+ if (!m_console.validate_cpu_parameter(std::string_view(), cpu))
return;
- /* If it's a number, it must be an address */
- /* The bankoff and cbn will be pulled from what's currently active */
+ // If it's a number, it must be an address
+ // The bankoff and cbn will be pulled from what's currently active
cpu->debug()->comment_remove(address);
cpu->machine().debug_view().update_all(DVT_DISASSEMBLY);
}
/**
- * @fn void execute_comment_list(running_machine &machine, int ref, int params, const char *param[])
+ * @fn void execute_comment_list(const std::vector<std::string_view> &params)
* @brief Print current list of comments in debugger
*
*
*/
-void debugger_commands::execute_comment_list(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_comment_list(const std::vector<std::string_view> &params)
{
if (!m_machine.debugger().cpu().comment_load(false))
m_console.printf("Error while parsing XML file\n");
}
/**
- * @fn void execute_comment_commit(running_machine &machine, int ref, int params, const char *param[])
+ * @fn void execute_comment_commit(const std::vector<std::string_view> &params)
* @brief Add and Save current list of comments in debugger
*
*/
-void debugger_commands::execute_comment_commit(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_comment_commit(const std::vector<std::string_view> &params)
{
- execute_comment_add(ref, params);
- execute_comment_save(ref, params);
+ execute_comment_add(params);
+ execute_comment_save(params);
}
/*-------------------------------------------------
execute_comment - add a comment to a line
-------------------------------------------------*/
-void debugger_commands::execute_comment_save(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_comment_save(const std::vector<std::string_view> &params)
{
- if (m_cpu.comment_save())
+ if (m_machine.debugger().cpu().comment_save())
m_console.printf("Comment successfully saved\n");
else
m_console.printf("Comment not saved\n");
@@ -1270,7 +1332,7 @@ void debugger_commands::execute_comment_save(int ref, const std::vector<std::str
// TODO: add color hex editing capabilities for comments, see below for more info
/**
- * @fn void execute_comment_color(running_machine &machine, int ref, int params, const char *param[])
+ * @fn void execute_comment_color(const std::vector<std::string_view> &params)
* @brief Modifies comment given at address $xx with given color
* Useful for marking comment with a different color scheme (for example by marking start and end of a given function visually).
* @param[in] "address,color" First is the comment address in the current context, color can be hexadecimal or shorthanded to common 1bpp RGB names.
@@ -1287,32 +1349,40 @@ void debugger_commands::execute_comment_save(int ref, const std::vector<std::str
command
-------------------------------------------------*/
-void debugger_commands::execute_bpset(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_bpset(const std::vector<std::string_view> &params)
{
- device_t *cpu;
+ // param 1 is the address/CPU
u64 address;
- int bpnum;
- const char *action = nullptr;
+ address_space *space;
+ if (!m_console.validate_target_address_parameter(params[0], AS_PROGRAM, space, address))
+ return;
- /* CPU is implicit */
- if (!validate_cpu_parameter(nullptr, cpu))
+ device_execute_interface const *execute;
+ if (!space->device().interface(execute))
+ {
+ m_console.printf("Device %s is not a CPU\n", space->device().name());
return;
+ }
+ device_debug *const debug = space->device().debug();
- /* param 1 is the address */
- if (!validate_number_parameter(params[0], address))
+ if (space->spacenum() != AS_PROGRAM)
+ {
+ m_console.printf("Only program space breakpoints are supported\n");
return;
+ }
- /* param 2 is the condition */
- parsed_expression condition(&cpu->debug()->symtable());
- if (params.size() > 1 && !debug_command_parameter_expression(params[1], condition))
+ // param 2 is the condition
+ parsed_expression condition(debug->symtable());
+ if (params.size() > 1 && !m_console.validate_expression_parameter(params[1], condition))
return;
- /* param 3 is the action */
- if (params.size() > 2 && !debug_command_parameter_command(action = params[2].c_str()))
+ // param 3 is the action
+ std::string_view action;
+ if (params.size() > 2 && !m_console.validate_command_parameter(action = params[2]))
return;
- /* set the breakpoint */
- bpnum = cpu->debug()->breakpoint_set(address, (condition.is_empty()) ? nullptr : condition.original_string(), action);
+ // set the breakpoint
+ int const bpnum = debug->breakpoint_set(address, condition.is_empty() ? nullptr : condition.original_string(), action);
m_console.printf("Breakpoint %X set\n", bpnum);
}
@@ -1322,31 +1392,26 @@ void debugger_commands::execute_bpset(int ref, const std::vector<std::string> &p
clear command
-------------------------------------------------*/
-void debugger_commands::execute_bpclear(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_bpclear(const std::vector<std::string_view> &params)
{
- u64 bpindex;
-
- /* if 0 parameters, clear all */
- if (params.empty())
+ if (params.empty()) // if no parameters, clear all
{
- for (device_t &device : device_iterator(m_machine.root_device()))
+ for (device_t &device : device_enumerator(m_machine.root_device()))
device.debug()->breakpoint_clear_all();
m_console.printf("Cleared all breakpoints\n");
}
-
- /* otherwise, clear the specific one */
- else if (!validate_number_parameter(params[0], bpindex))
- return;
- else
+ else // otherwise, clear the specific ones
{
- bool found = false;
- for (device_t &device : device_iterator(m_machine.root_device()))
- if (device.debug()->breakpoint_clear(bpindex))
- found = true;
- if (found)
- m_console.printf("Breakpoint %X cleared\n", u32(bpindex));
- else
- m_console.printf("Invalid breakpoint number %X\n", u32(bpindex));
+ execute_index_command(
+ params,
+ [this] (device_t &device, u64 param) -> bool
+ {
+ if (!device.debug()->breakpoint_clear(param))
+ return false;
+ m_console.printf("Breakpoint %X cleared\n", param);
+ return true;
+ },
+ "Invalid breakpoint number %X\n");
}
}
@@ -1356,34 +1421,26 @@ void debugger_commands::execute_bpclear(int ref, const std::vector<std::string>
disable/enable commands
-------------------------------------------------*/
-void debugger_commands::execute_bpdisenable(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_bpdisenable(bool enable, const std::vector<std::string_view> &params)
{
- u64 bpindex;
-
- /* if 0 parameters, clear all */
- if (params.empty())
+ if (params.empty()) // if no parameters, disable/enable all
{
- for (device_t &device : device_iterator(m_machine.root_device()))
- device.debug()->breakpoint_enable_all(ref);
- if (ref == 0)
- m_console.printf("Disabled all breakpoints\n");
- else
- m_console.printf("Enabled all breakpoints\n");
+ for (device_t &device : device_enumerator(m_machine.root_device()))
+ device.debug()->breakpoint_enable_all(enable);
+ m_console.printf(enable ? "Enabled all breakpoints\n" : "Disabled all breakpoints\n");
}
-
- /* otherwise, clear the specific one */
- else if (!validate_number_parameter(params[0], bpindex))
- return;
- else
+ else // otherwise, disable/enable the specific ones
{
- bool found = false;
- for (device_t &device : device_iterator(m_machine.root_device()))
- if (device.debug()->breakpoint_enable(bpindex, ref))
- found = true;
- if (found)
- m_console.printf("Breakpoint %X %s\n", u32(bpindex), ref ? "enabled" : "disabled");
- else
- m_console.printf("Invalid breakpoint number %X\n", u32(bpindex));
+ execute_index_command(
+ params,
+ [this, enable] (device_t &device, u64 param) -> bool
+ {
+ if (!device.debug()->breakpoint_enable(param, enable))
+ return false;
+ m_console.printf(enable ? "Breakpoint %X enabled\n" : "Breakpoint %X disabled\n", param);
+ return true;
+ },
+ "Invalid breakpoint number %X\n");
}
}
@@ -1393,32 +1450,49 @@ void debugger_commands::execute_bpdisenable(int ref, const std::vector<std::stri
command
-------------------------------------------------*/
-void debugger_commands::execute_bplist(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_bplist(const std::vector<std::string_view> &params)
{
int printed = 0;
std::string buffer;
-
- /* loop over all CPUs */
- for (device_t &device : device_iterator(m_machine.root_device()))
- if (!device.debug()->breakpoint_list().empty())
- {
- m_console.printf("Device '%s' breakpoints:\n", device.tag());
-
- /* loop over the breakpoints */
- for (const device_debug::breakpoint &bp : device.debug()->breakpoint_list())
+ auto const apply =
+ [this, &printed, &buffer] (device_t &device)
{
- buffer = string_format("%c%4X @ %0*X", bp.enabled() ? ' ' : 'D', bp.index(), device.debug()->logaddrchars(), bp.address());
- if (std::string(bp.condition()).compare("1") != 0)
- buffer.append(string_format(" if %s", bp.condition()));
- if (std::string(bp.action()).compare("") != 0)
- buffer.append(string_format(" do %s", bp.action()));
- m_console.printf("%s\n", buffer.c_str());
- printed++;
- }
- }
+ if (!device.debug()->breakpoint_list().empty())
+ {
+ m_console.printf("Device '%s' breakpoints:\n", device.tag());
- if (printed == 0)
- m_console.printf("No breakpoints currently installed\n");
+ // loop over the breakpoints
+ for (const auto &bpp : device.debug()->breakpoint_list())
+ {
+ debug_breakpoint &bp = *bpp.second;
+ buffer = string_format("%c%4X @ %0*X", bp.enabled() ? ' ' : 'D', bp.index(), device.debug()->logaddrchars(), bp.address());
+ if (std::string(bp.condition()).compare("1") != 0)
+ buffer.append(string_format(" if %s", bp.condition()));
+ if (std::string(bp.action()).compare("") != 0)
+ buffer.append(string_format(" do %s", bp.action()));
+ m_console.printf("%s\n", buffer);
+ printed++;
+ }
+ }
+ };
+
+ if (!params.empty())
+ {
+ device_t *cpu;
+ if (!m_console.validate_cpu_parameter(params[0], cpu))
+ return;
+ apply(*cpu);
+ if (!printed)
+ m_console.printf("No breakpoints currently installed for CPU %s\n", cpu->tag());
+ }
+ else
+ {
+ // loop over all CPUs
+ for (device_t &device : device_enumerator(m_machine.root_device()))
+ apply(device);
+ if (!printed)
+ m_console.printf("No breakpoints currently installed\n");
+ }
}
@@ -1427,50 +1501,57 @@ void debugger_commands::execute_bplist(int ref, const std::vector<std::string> &
command
-------------------------------------------------*/
-void debugger_commands::execute_wpset(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_wpset(int spacenum, const std::vector<std::string_view> &params)
{
- address_space *space;
- const char *action = nullptr;
u64 address, length;
- read_or_write type;
- int wpnum;
+ address_space *space;
- /* CPU is implicit */
- if (!validate_cpu_space_parameter(nullptr, ref, space))
+ // param 1 is the address/CPU
+ if (!m_console.validate_target_address_parameter(params[0], spacenum, space, address))
return;
- /* param 1 is the address */
- if (!validate_number_parameter(params[0], address))
+ device_execute_interface const *execute;
+ if (!space->device().interface(execute))
+ {
+ m_console.printf("Device %s is not a CPU\n", space->device().name());
return;
+ }
+ device_debug *const debug = space->device().debug();
- /* param 2 is the length */
- if (!validate_number_parameter(params[1], length))
+ // param 2 is the length
+ if (!m_console.validate_number_parameter(params[1], length))
return;
- /* param 3 is the type */
- if (params[2] == "r")
- type = read_or_write::READ;
- else if (params[2] == "w")
- type = read_or_write::WRITE;
- else if (params[2] == "rw" || params[2] == "wr")
- type = read_or_write::READWRITE;
- else
+ // param 3 is the type
+ read_or_write type;
{
- m_console.printf("Invalid watchpoint type: expected r, w, or rw\n");
- return;
+ using util::streqlower;
+ using namespace std::literals;
+ if (streqlower(params[2], "r"sv))
+ type = read_or_write::READ;
+ else if (streqlower(params[2], "w"sv))
+ type = read_or_write::WRITE;
+ else if (streqlower(params[2], "rw"sv) || streqlower(params[2], "wr"sv))
+ type = read_or_write::READWRITE;
+ else
+ {
+ m_console.printf("Invalid watchpoint type: expected r, w, or rw\n");
+ return;
+ }
}
- /* param 4 is the condition */
- parsed_expression condition(&space->device().debug()->symtable());
- if (params.size() > 3 && !debug_command_parameter_expression(params[3], condition))
+ // param 4 is the condition
+ parsed_expression condition(debug->symtable());
+ if (params.size() > 3 && !m_console.validate_expression_parameter(params[3], condition))
return;
- /* param 5 is the action */
- if (params.size() > 4 && !debug_command_parameter_command(action = params[4].c_str()))
+ // param 5 is the action
+ std::string_view action;
+ if (params.size() > 4 && !m_console.validate_command_parameter(action = params[4]))
return;
- /* set the watchpoint */
- wpnum = space->device().debug()->watchpoint_set(*space, type, address, length, (condition.is_empty()) ? nullptr : condition.original_string(), action);
+ // set the watchpoint
+ int const wpnum = debug->watchpoint_set(*space, type, address, length, (condition.is_empty()) ? nullptr : condition.original_string(), action);
m_console.printf("Watchpoint %X set\n", wpnum);
}
@@ -1480,31 +1561,26 @@ void debugger_commands::execute_wpset(int ref, const std::vector<std::string> &p
clear command
-------------------------------------------------*/
-void debugger_commands::execute_wpclear(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_wpclear(const std::vector<std::string_view> &params)
{
- u64 wpindex;
-
- /* if 0 parameters, clear all */
- if (params.empty())
+ if (params.empty()) // if no parameters, clear all
{
- for (device_t &device : device_iterator(m_machine.root_device()))
+ for (device_t &device : device_enumerator(m_machine.root_device()))
device.debug()->watchpoint_clear_all();
m_console.printf("Cleared all watchpoints\n");
}
-
- /* otherwise, clear the specific one */
- else if (!validate_number_parameter(params[0], wpindex))
- return;
- else
+ else // otherwise, clear the specific ones
{
- bool found = false;
- for (device_t &device : device_iterator(m_machine.root_device()))
- if (device.debug()->watchpoint_clear(wpindex))
- found = true;
- if (found)
- m_console.printf("Watchpoint %X cleared\n", u32(wpindex));
- else
- m_console.printf("Invalid watchpoint number %X\n", u32(wpindex));
+ execute_index_command(
+ params,
+ [this] (device_t &device, u64 param) -> bool
+ {
+ if (!device.debug()->watchpoint_clear(param))
+ return false;
+ m_console.printf("Watchpoint %X cleared\n", param);
+ return true;
+ },
+ "Invalid watchpoint number %X\n");
}
}
@@ -1514,34 +1590,26 @@ void debugger_commands::execute_wpclear(int ref, const std::vector<std::string>
disable/enable commands
-------------------------------------------------*/
-void debugger_commands::execute_wpdisenable(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_wpdisenable(bool enable, const std::vector<std::string_view> &params)
{
- u64 wpindex;
-
- /* if 0 parameters, clear all */
- if (params.empty())
+ if (params.empty()) // if no parameters, disable/enable all
{
- for (device_t &device : device_iterator(m_machine.root_device()))
- device.debug()->watchpoint_enable_all(ref);
- if (ref == 0)
- m_console.printf("Disabled all watchpoints\n");
- else
- m_console.printf("Enabled all watchpoints\n");
+ for (device_t &device : device_enumerator(m_machine.root_device()))
+ device.debug()->watchpoint_enable_all(enable);
+ m_console.printf(enable ? "Enabled all watchpoints\n" : "Disabled all watchpoints\n");
}
-
- /* otherwise, clear the specific one */
- else if (!validate_number_parameter(params[0], wpindex))
- return;
- else
+ else // otherwise, disable/enable the specific ones
{
- bool found = false;
- for (device_t &device : device_iterator(m_machine.root_device()))
- if (device.debug()->watchpoint_enable(wpindex, ref))
- found = true;
- if (found)
- m_console.printf("Watchpoint %X %s\n", u32(wpindex), ref ? "enabled" : "disabled");
- else
- m_console.printf("Invalid watchpoint number %X\n", u32(wpindex));
+ execute_index_command(
+ params,
+ [this, enable] (device_t &device, u64 param) -> bool
+ {
+ if (!device.debug()->watchpoint_enable(param, enable))
+ return false;
+ m_console.printf(enable ? "Watchpoint %X enabled\n" : "Watchpoint %X disabled\n", param);
+ return true;
+ },
+ "Invalid watchpoint number %X\n");
}
}
@@ -1551,39 +1619,61 @@ void debugger_commands::execute_wpdisenable(int ref, const std::vector<std::stri
command
-------------------------------------------------*/
-void debugger_commands::execute_wplist(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_wplist(const std::vector<std::string_view> &params)
{
int printed = 0;
std::string buffer;
-
- /* loop over all CPUs */
- for (device_t &device : device_iterator(m_machine.root_device()))
- for (int spacenum = 0; spacenum < device.debug()->watchpoint_space_count(); ++spacenum)
- if (!device.debug()->watchpoint_vector(spacenum).empty())
+ auto const apply =
+ [this, &printed, &buffer] (device_t &device)
{
- static const char *const types[] = { "unkn ", "read ", "write", "r/w " };
+ for (int spacenum = 0; spacenum < device.debug()->watchpoint_space_count(); ++spacenum)
+ {
+ if (!device.debug()->watchpoint_vector(spacenum).empty())
+ {
+ static const char *const types[] = { "unkn ", "read ", "write", "r/w " };
- m_console.printf("Device '%s' %s space watchpoints:\n", device.tag(),
- device.debug()->watchpoint_vector(spacenum).front()->space().name());
+ m_console.printf(
+ "Device '%s' %s space watchpoints:\n",
+ device.tag(),
+ device.debug()->watchpoint_vector(spacenum).front()->space().name());
- /* loop over the watchpoints */
- for (const auto &wp : device.debug()->watchpoint_vector(spacenum))
- {
- buffer = string_format("%c%4X @ %0*X-%0*X %s", wp->enabled() ? ' ' : 'D', wp->index(),
- wp->space().addrchars(), wp->address(),
- wp->space().addrchars(), wp->address() + wp->length() - 1,
- types[int(wp->type())]);
- if (std::string(wp->condition()).compare("1") != 0)
- buffer.append(string_format(" if %s", wp->condition()));
- if (std::string(wp->action()).compare("") != 0)
- buffer.append(string_format(" do %s", wp->action()));
- m_console.printf("%s\n", buffer.c_str());
- printed++;
+ // loop over the watchpoints
+ for (const auto &wp : device.debug()->watchpoint_vector(spacenum))
+ {
+ buffer = string_format(
+ "%c%4X @ %0*X-%0*X %s",
+ wp->enabled() ? ' ' : 'D', wp->index(),
+ wp->space().addrchars(), wp->address(),
+ wp->space().addrchars(), wp->address() + wp->length() - 1,
+ types[int(wp->type())]);
+ if (std::string(wp->condition()).compare("1") != 0)
+ buffer.append(string_format(" if %s", wp->condition()));
+ if (std::string(wp->action()).compare("") != 0)
+ buffer.append(string_format(" do %s", wp->action()));
+ m_console.printf("%s\n", buffer);
+ printed++;
+ }
+ }
}
- }
+ };
- if (printed == 0)
- m_console.printf("No watchpoints currently installed\n");
+ if (!params.empty())
+ {
+ device_t *cpu;
+ if (!m_console.validate_cpu_parameter(params[0], cpu))
+ return;
+ apply(*cpu);
+ if (!printed)
+ m_console.printf("No watchpoints currently installed for CPU %s\n", cpu->tag());
+ }
+ else
+ {
+ // loop over all CPUs
+ for (device_t &device : device_enumerator(m_machine.root_device()))
+ apply(device);
+ if (!printed)
+ m_console.printf("No watchpoints currently installed\n");
+ }
}
@@ -1592,28 +1682,26 @@ void debugger_commands::execute_wplist(int ref, const std::vector<std::string> &
command
-------------------------------------------------*/
-void debugger_commands::execute_rpset(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_rpset(const std::vector<std::string_view> &params)
{
+ // CPU is implicit
device_t *cpu;
- const char *action = nullptr;
- int bpnum;
-
- /* CPU is implicit */
- if (!validate_cpu_parameter(nullptr, cpu))
+ if (!m_console.validate_cpu_parameter(std::string_view(), cpu))
return;
- /* param 1 is the condition */
- parsed_expression condition(&cpu->debug()->symtable());
- if (params.size() > 0 && !debug_command_parameter_expression(params[0], condition))
+ // param 1 is the condition
+ parsed_expression condition(cpu->debug()->symtable());
+ if (params.size() > 0 && !m_console.validate_expression_parameter(params[0], condition))
return;
- /* param 2 is the action */
- if (params.size() > 1 && !debug_command_parameter_command(action = params[1].c_str()))
+ // param 2 is the action
+ std::string_view action;
+ if (params.size() > 1 && !m_console.validate_command_parameter(action = params[1]))
return;
- /* set the breakpoint */
- bpnum = cpu->debug()->registerpoint_set(condition.original_string(), action);
- m_console.printf("Registerpoint %X set\n", bpnum);
+ // set the registerpoint
+ int const rpnum = cpu->debug()->registerpoint_set(condition.original_string(), action);
+ m_console.printf("Registerpoint %X set\n", rpnum);
}
@@ -1622,31 +1710,26 @@ void debugger_commands::execute_rpset(int ref, const std::vector<std::string> &p
clear command
-------------------------------------------------*/
-void debugger_commands::execute_rpclear(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_rpclear(const std::vector<std::string_view> &params)
{
- u64 rpindex;
-
- /* if 0 parameters, clear all */
- if (params.empty())
+ if (params.empty()) // if no parameters, clear all
{
- for (device_t &device : device_iterator(m_machine.root_device()))
+ for (device_t &device : device_enumerator(m_machine.root_device()))
device.debug()->registerpoint_clear_all();
m_console.printf("Cleared all registerpoints\n");
}
-
- /* otherwise, clear the specific one */
- else if (!validate_number_parameter(params[0], rpindex))
- return;
- else
+ else // otherwise, clear the specific ones
{
- bool found = false;
- for (device_t &device : device_iterator(m_machine.root_device()))
- if (device.debug()->registerpoint_clear(rpindex))
- found = true;
- if (found)
- m_console.printf("Registerpoint %X cleared\n", u32(rpindex));
- else
- m_console.printf("Invalid registerpoint number %X\n", u32(rpindex));
+ execute_index_command(
+ params,
+ [this] (device_t &device, u64 param) -> bool
+ {
+ if (!device.debug()->registerpoint_clear(param))
+ return false;
+ m_console.printf("Registerpoint %X cleared\n", param);
+ return true;
+ },
+ "Invalid registerpoint number %X\n");
}
}
@@ -1656,110 +1739,217 @@ void debugger_commands::execute_rpclear(int ref, const std::vector<std::string>
disable/enable commands
-------------------------------------------------*/
-void debugger_commands::execute_rpdisenable(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_rpdisenable(bool enable, const std::vector<std::string_view> &params)
{
- u64 rpindex;
-
- /* if 0 parameters, clear all */
- if (params.empty())
+ if (params.empty()) // if no parameters, disable/enable all
{
- for (device_t &device : device_iterator(m_machine.root_device()))
- device.debug()->registerpoint_enable_all(ref);
- if (ref == 0)
- m_console.printf("Disabled all registerpoints\n");
- else
- m_console.printf("Enabled all registeroints\n");
+ for (device_t &device : device_enumerator(m_machine.root_device()))
+ device.debug()->registerpoint_enable_all(enable);
+ m_console.printf(enable ? "Enabled all registerpoints\n" : "Disabled all registerpoints\n");
+ }
+ else // otherwise, disable/enable the specific ones
+ {
+ execute_index_command(
+ params,
+ [this, enable] (device_t &device, u64 param) -> bool
+ {
+ if (!device.debug()->registerpoint_enable(param, enable))
+ return false;
+ m_console.printf(enable ? "Registerpoint %X enabled\n" : "Breakpoint %X disabled\n", param);
+ return true;
+ },
+ "Invalid registerpoint number %X\n");
}
+}
+
- /* otherwise, clear the specific one */
- else if (!validate_number_parameter(params[0], rpindex))
+//-------------------------------------------------
+// execute_epset - execute the exception point
+// set command
+//-------------------------------------------------
+
+void debugger_commands::execute_epset(const std::vector<std::string_view> &params)
+{
+ // CPU is implicit
+ device_t *cpu;
+ if (!m_console.validate_cpu_parameter(std::string_view(), cpu))
return;
- else
+
+ // param 1 is the exception type
+ u64 type;
+ if (!m_console.validate_number_parameter(params[0], type))
+ return;
+
+ // param 2 is the condition
+ parsed_expression condition(cpu->debug()->symtable());
+ if (params.size() > 1 && !m_console.validate_expression_parameter(params[1], condition))
+ return;
+
+ // param 3 is the action
+ std::string_view action;
+ if (params.size() > 2 && !m_console.validate_command_parameter(action = params[2]))
+ return;
+
+ // set the exception point
+ int epnum = cpu->debug()->exceptionpoint_set(type, (condition.is_empty()) ? nullptr : condition.original_string(), action);
+ m_console.printf("Exception point %X set\n", epnum);
+}
+
+
+//-------------------------------------------------
+// execute_epclear - execute the exception point
+// clear command
+//-------------------------------------------------
+
+void debugger_commands::execute_epclear(const std::vector<std::string_view> &params)
+{
+ if (params.empty()) // if no parameters, clear all
{
- bool found = false;
- for (device_t &device : device_iterator(m_machine.root_device()))
- if (device.debug()->registerpoint_enable(rpindex, ref))
- found = true;
- if (found)
- m_console.printf("Registerpoint %X %s\n", u32(rpindex), ref ? "enabled" : "disabled");
- else
- m_console.printf("Invalid registerpoint number %X\n", u32(rpindex));
+ for (device_t &device : device_enumerator(m_machine.root_device()))
+ device.debug()->exceptionpoint_clear_all();
+ m_console.printf("Cleared all exception points\n");
+ }
+ else // otherwise, clear the specific ones
+ {
+ execute_index_command(
+ params,
+ [this] (device_t &device, u64 param) -> bool
+ {
+ if (!device.debug()->exceptionpoint_clear(param))
+ return false;
+ m_console.printf("Exception point %X cleared\n", param);
+ return true;
+ },
+ "Invalid exception point number %X\n");
}
}
-/*-------------------------------------------------
- execute_rplist - execute the registerpoint list
- command
--------------------------------------------------*/
+//-------------------------------------------------
+// execute_epdisenable - execute the exception
+// point disable/enable commands
+//-------------------------------------------------
-void debugger_commands::execute_rplist(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_epdisenable(bool enable, const std::vector<std::string_view> &params)
{
- int printed = 0;
- std::string buffer;
+ if (params.empty()) // if no parameters, disable/enable all
+ {
+ for (device_t &device : device_enumerator(m_machine.root_device()))
+ device.debug()->exceptionpoint_enable_all(enable);
+ m_console.printf(enable ? "Enabled all exception points\n" : "Disabled all exception points\n");
+ }
+ else // otherwise, disable/enable the specific ones
+ {
+ execute_index_command(
+ params,
+ [this, enable] (device_t &device, u64 param) -> bool
+ {
+ if (!device.debug()->exceptionpoint_enable(param, enable))
+ return false;
+ m_console.printf(enable ? "Exception point %X enabled\n" : "Exception point %X disabled\n", param);
+ return true;
+ },
+ "Invalid exception point number %X\n");
+ }
+}
- /* loop over all CPUs */
- for (device_t &device : device_iterator(m_machine.root_device()))
- if (!device.debug()->registerpoint_list().empty())
- {
- m_console.printf("Device '%s' registerpoints:\n", device.tag());
- /* loop over the breakpoints */
- for (const device_debug::registerpoint &rp : device.debug()->registerpoint_list())
+//-------------------------------------------------
+// execute_eplist - execute the exception point
+// list command
+//-------------------------------------------------
+
+void debugger_commands::execute_eplist(const std::vector<std::string_view> &params)
+{
+ int printed = 0;
+ std::string buffer;
+ auto const apply =
+ [this, &printed, &buffer] (device_t &device)
{
- buffer = string_format("%c%4X if %s", rp.enabled() ? ' ' : 'D', rp.index(), rp.condition());
- if (rp.action() != nullptr)
- buffer.append(string_format(" do %s", rp.action()));
- m_console.printf("%s\n", buffer.c_str());
- printed++;
- }
- }
+ if (!device.debug()->exceptionpoint_list().empty())
+ {
+ m_console.printf("Device '%s' exception points:\n", device.tag());
+
+ // loop over the exception points
+ for (const auto &epp : device.debug()->exceptionpoint_list())
+ {
+ debug_exceptionpoint &ep = *epp.second;
+ buffer = string_format("%c%4X : %X", ep.enabled() ? ' ' : 'D', ep.index(), ep.type());
+ if (std::string(ep.condition()).compare("1") != 0)
+ buffer.append(string_format(" if %s", ep.condition()));
+ if (!ep.action().empty())
+ buffer.append(string_format(" do %s", ep.action()));
+ m_console.printf("%s\n", buffer);
+ printed++;
+ }
+ }
+ };
- if (printed == 0)
- m_console.printf("No registerpoints currently installed\n");
+ if (!params.empty())
+ {
+ device_t *cpu;
+ if (!m_console.validate_cpu_parameter(params[0], cpu))
+ return;
+ apply(*cpu);
+ if (!printed)
+ m_console.printf("No exception points currently installed for CPU %s\n", cpu->tag());
+ }
+ else
+ {
+ // loop over all CPUs
+ for (device_t &device : device_enumerator(m_machine.root_device()))
+ apply(device);
+ if (!printed)
+ m_console.printf("No exception points currently installed\n");
+ }
}
/*-------------------------------------------------
- execute_hotspot - execute the hotspot
+ execute_rplist - execute the registerpoint list
command
-------------------------------------------------*/
-void debugger_commands::execute_hotspot(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_rplist(const std::vector<std::string_view> &params)
{
- /* if no params, and there are live hotspots, clear them */
- if (params.empty())
- {
- bool cleared = false;
-
- /* loop over CPUs and find live spots */
- for (device_t &device : device_iterator(m_machine.root_device()))
- if (device.debug()->hotspot_tracking_enabled())
+ int printed = 0;
+ std::string buffer;
+ auto const apply =
+ [this, &printed, &buffer] (device_t &device)
{
- device.debug()->hotspot_track(0, 0);
- m_console.printf("Cleared hotspot tracking on CPU '%s'\n", device.tag());
- cleared = true;
- }
+ if (!device.debug()->registerpoint_list().empty())
+ {
+ m_console.printf("Device '%s' registerpoints:\n", device.tag());
- /* if we cleared, we're done */
- if (cleared)
+ // loop over the registerpoints
+ for (const auto &rp : device.debug()->registerpoint_list())
+ {
+ buffer = string_format("%c%4X if %s", rp.enabled() ? ' ' : 'D', rp.index(), rp.condition());
+ if (!rp.action().empty())
+ buffer.append(string_format(" do %s", rp.action()));
+ m_console.printf("%s\n", buffer);
+ printed++;
+ }
+ }
+ };
+
+ if (!params.empty())
+ {
+ device_t *cpu;
+ if (!m_console.validate_cpu_parameter(params[0], cpu))
return;
+ apply(*cpu);
+ if (!printed)
+ m_console.printf("No registerpoints currently installed for CPU %s\n", cpu->tag());
+ }
+ else
+ {
+ // loop over all CPUs
+ for (device_t &device : device_enumerator(m_machine.root_device()))
+ apply(device);
+ if (!printed)
+ m_console.printf("No registerpoints currently installed\n");
}
-
- /* extract parameters */
- device_t *device = nullptr;
- if (!validate_cpu_parameter(!params.empty() ? params[0].c_str() : nullptr, device))
- return;
- u64 count = 64;
- if (params.size() > 1 && !validate_number_parameter(params[1], count))
- return;
- u64 threshhold = 250;
- if (params.size() > 2 && !validate_number_parameter(params[2], threshhold))
- return;
-
- /* attempt to install */
- device->debug()->hotspot_track(count, threshhold);
- m_console.printf("Now tracking hotspots on CPU '%s' using %d slots with a threshold of %d\n", device->tag(), (int)count, (int)threshhold);
}
@@ -1767,10 +1957,9 @@ void debugger_commands::execute_hotspot(int ref, const std::vector<std::string>
execute_statesave - execute the statesave command
-------------------------------------------------*/
-void debugger_commands::execute_statesave(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_statesave(const std::vector<std::string_view> &params)
{
- const std::string &filename(params[0]);
- m_machine.immediate_save(filename.c_str());
+ m_machine.immediate_save(params[0]);
m_console.printf("State save attempted. Please refer to window message popup for results.\n");
}
@@ -1779,13 +1968,12 @@ void debugger_commands::execute_statesave(int ref, const std::vector<std::string
execute_stateload - execute the stateload command
-------------------------------------------------*/
-void debugger_commands::execute_stateload(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_stateload(const std::vector<std::string_view> &params)
{
- const std::string &filename(params[0]);
- m_machine.immediate_load(filename.c_str());
+ m_machine.immediate_load(params[0]);
// clear all PC & memory tracks
- for (device_t &device : device_iterator(m_machine.root_device()))
+ for (device_t &device : device_enumerator(m_machine.root_device()))
{
device.debug()->track_pc_data_clear();
device.debug()->track_mem_data_clear();
@@ -1798,12 +1986,12 @@ void debugger_commands::execute_stateload(int ref, const std::vector<std::string
execute_rewind - execute the rewind command
-------------------------------------------------*/
-void debugger_commands::execute_rewind(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_rewind(const std::vector<std::string_view> &params)
{
bool success = m_machine.rewind_step();
if (success)
// clear all PC & memory tracks
- for (device_t &device : device_iterator(m_machine.root_device()))
+ for (device_t &device : device_enumerator(m_machine.root_device()))
{
device.debug()->track_pc_data_clear();
device.debug()->track_mem_data_clear();
@@ -1817,34 +2005,32 @@ void debugger_commands::execute_rewind(int ref, const std::vector<std::string> &
execute_save - execute the save command
-------------------------------------------------*/
-void debugger_commands::execute_save(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_save(int spacenum, const std::vector<std::string_view> &params)
{
u64 offset, endoffset, length;
address_space *space;
- FILE *f;
- /* validate parameters */
- if (!validate_number_parameter(params[1], offset))
- return;
- if (!validate_number_parameter(params[2], length))
+ // validate parameters
+ if (!m_console.validate_target_address_parameter(params[1], spacenum, space, offset))
return;
- if (!validate_cpu_space_parameter(params.size() > 3 ? params[3].c_str() : nullptr, ref, space))
+ if (!m_console.validate_number_parameter(params[2], length))
return;
- /* determine the addresses to write */
+ // determine the addresses to write
endoffset = (offset + length - 1) & space->addrmask();
offset = offset & space->addrmask();
- endoffset ++;
+ endoffset++;
- /* open the file */
- f = fopen(params[0].c_str(), "wb");
+ // open the file
+ std::string const filename(params[0]);
+ FILE *const f = fopen(filename.c_str(), "wb");
if (!f)
{
- m_console.printf("Error opening file '%s'\n", params[0].c_str());
+ m_console.printf("Error opening file '%s'\n", params[0]);
return;
}
- /* now write the data out */
+ // now write the data out
auto dis = space->device().machine().disable_side_effects();
switch (space->addr_shift())
{
@@ -1852,8 +2038,9 @@ void debugger_commands::execute_save(int ref, const std::vector<std::string> &pa
for (u64 i = offset; i != endoffset; i++)
{
offs_t curaddr = i;
- u64 data = space->device().memory().translate(space->spacenum(), TRANSLATE_READ_DEBUG, curaddr) ?
- space->read_qword(curaddr) : space->unmap();
+ address_space *tspace;
+ u64 data = space->device().memory().translate(space->spacenum(), device_memory_interface::TR_READ, curaddr, tspace) ?
+ tspace->read_qword(curaddr) : space->unmap();
fwrite(&data, 8, 1, f);
}
break;
@@ -1861,8 +2048,9 @@ void debugger_commands::execute_save(int ref, const std::vector<std::string> &pa
for (u64 i = offset; i != endoffset; i++)
{
offs_t curaddr = i;
- u32 data = space->device().memory().translate(space->spacenum(), TRANSLATE_READ_DEBUG, curaddr) ?
- space->read_dword(curaddr) : space->unmap();
+ address_space *tspace;
+ u32 data = space->device().memory().translate(space->spacenum(), device_memory_interface::TR_READ, curaddr, tspace) ?
+ tspace->read_dword(curaddr) : space->unmap();
fwrite(&data, 4, 1, f);
}
break;
@@ -1870,8 +2058,9 @@ void debugger_commands::execute_save(int ref, const std::vector<std::string> &pa
for (u64 i = offset; i != endoffset; i++)
{
offs_t curaddr = i;
- u16 data = space->device().memory().translate(space->spacenum(), TRANSLATE_READ_DEBUG, curaddr) ?
- space->read_word(curaddr) : space->unmap();
+ address_space *tspace;
+ u16 data = space->device().memory().translate(space->spacenum(), device_memory_interface::TR_READ, curaddr, tspace) ?
+ tspace->read_word(curaddr) : space->unmap();
fwrite(&data, 2, 1, f);
}
break;
@@ -1879,8 +2068,9 @@ void debugger_commands::execute_save(int ref, const std::vector<std::string> &pa
for (u64 i = offset; i != endoffset; i++)
{
offs_t curaddr = i;
- u8 data = space->device().memory().translate(space->spacenum(), TRANSLATE_READ_DEBUG, curaddr) ?
- space->read_byte(curaddr) : space->unmap();
+ address_space *tspace;
+ u8 data = space->device().memory().translate(space->spacenum(), device_memory_interface::TR_READ, curaddr, tspace) ?
+ tspace->read_byte(curaddr) : space->unmap();
fwrite(&data, 1, 1, f);
}
break;
@@ -1890,14 +2080,55 @@ void debugger_commands::execute_save(int ref, const std::vector<std::string> &pa
for (u64 i = offset; i != endoffset; i+=16)
{
offs_t curaddr = i;
- u16 data = space->device().memory().translate(space->spacenum(), TRANSLATE_READ_DEBUG, curaddr) ?
- space->read_word(curaddr) : space->unmap();
+ address_space *tspace;
+ u16 data = space->device().memory().translate(space->spacenum(), device_memory_interface::TR_READ, curaddr, tspace) ?
+ tspace->read_word(curaddr) : space->unmap();
fwrite(&data, 2, 1, f);
}
break;
}
- /* close the file */
+ // close the file
+ fclose(f);
+ m_console.printf("Data saved successfully\n");
+}
+
+
+/*-------------------------------------------------
+ execute_saveregion - execute the save command on region memory
+-------------------------------------------------*/
+
+void debugger_commands::execute_saveregion(const std::vector<std::string_view> &params)
+{
+ u64 offset, length;
+ memory_region *region;
+
+ // validate parameters
+ if (!m_console.validate_number_parameter(params[1], offset))
+ return;
+ if (!m_console.validate_number_parameter(params[2], length))
+ return;
+ if (!m_console.validate_memory_region_parameter(params[3], region))
+ return;
+
+ if (offset >= region->bytes())
+ {
+ m_console.printf("Invalid offset\n");
+ return;
+ }
+ if ((length <= 0) || ((length + offset) >= region->bytes()))
+ length = region->bytes() - offset;
+
+ /* open the file */
+ std::string const filename(params[0]);
+ FILE *f = fopen(filename.c_str(), "wb");
+ if (!f)
+ {
+ m_console.printf("Error opening file '%s'\n", params[0]);
+ return;
+ }
+ fwrite(region->base() + offset, 1, length, f);
+
fclose(f);
m_console.printf("Data saved successfully\n");
}
@@ -1907,25 +2138,24 @@ void debugger_commands::execute_save(int ref, const std::vector<std::string> &pa
execute_load - execute the load command
-------------------------------------------------*/
-void debugger_commands::execute_load(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_load(int spacenum, const std::vector<std::string_view> &params)
{
u64 offset, endoffset, length = 0;
address_space *space;
// validate parameters
- if (!validate_number_parameter(params[1], offset))
- return;
- if (params.size() > 2 && !validate_number_parameter(params[2], length))
+ if (!m_console.validate_target_address_parameter(params[1], spacenum, space, offset))
return;
- if (!validate_cpu_space_parameter((params.size() > 3) ? params[3].c_str() : nullptr, ref, space))
+ if (params.size() > 2 && !m_console.validate_number_parameter(params[2], length))
return;
// open the file
std::ifstream f;
- f.open(params[0], std::ifstream::in | std::ifstream::binary);
+ std::string const fname(params[0]);
+ f.open(fname, std::ifstream::in | std::ifstream::binary);
if (f.fail())
{
- m_console.printf("Error opening file '%s'\n", params[0].c_str());
+ m_console.printf("Error opening file '%s'\n", params[0]);
return;
}
@@ -1945,7 +2175,9 @@ void debugger_commands::execute_load(int ref, const std::vector<std::string> &pa
endoffset = (offset + length - 1) & space->addrmask();
offset = offset & space->addrmask();
u64 i = 0;
+
// now read the data in, ignore endoffset and load entire file if length has been set to zero (offset-1)
+ auto dis = space->device().machine().disable_side_effects();
switch (space->addr_shift())
{
case -3:
@@ -1954,8 +2186,9 @@ void debugger_commands::execute_load(int ref, const std::vector<std::string> &pa
offs_t curaddr = i;
u64 data;
f.read((char *)&data, 8);
- if (f && space->device().memory().translate(space->spacenum(), TRANSLATE_WRITE_DEBUG, curaddr))
- space->write_qword(curaddr, data);
+ address_space *tspace;
+ if (f && space->device().memory().translate(space->spacenum(), device_memory_interface::TR_WRITE, curaddr, tspace))
+ tspace->write_qword(curaddr, data);
}
break;
case -2:
@@ -1964,8 +2197,9 @@ void debugger_commands::execute_load(int ref, const std::vector<std::string> &pa
offs_t curaddr = i;
u32 data;
f.read((char *)&data, 4);
- if (f && space->device().memory().translate(space->spacenum(), TRANSLATE_WRITE_DEBUG, curaddr))
- space->write_dword(curaddr, data);
+ address_space *tspace;
+ if (f && space->device().memory().translate(space->spacenum(), device_memory_interface::TR_WRITE, curaddr, tspace))
+ tspace->write_dword(curaddr, data);
}
break;
case -1:
@@ -1974,8 +2208,9 @@ void debugger_commands::execute_load(int ref, const std::vector<std::string> &pa
offs_t curaddr = i;
u16 data;
f.read((char *)&data, 2);
- if (f && space->device().memory().translate(space->spacenum(), TRANSLATE_WRITE_DEBUG, curaddr))
- space->write_word(curaddr, data);
+ address_space *tspace;
+ if (f && space->device().memory().translate(space->spacenum(), device_memory_interface::TR_WRITE, curaddr, tspace))
+ tspace->write_word(curaddr, data);
}
break;
case 0:
@@ -1984,8 +2219,9 @@ void debugger_commands::execute_load(int ref, const std::vector<std::string> &pa
offs_t curaddr = i;
u8 data;
f.read((char *)&data, 1);
- if (f && space->device().memory().translate(space->spacenum(), TRANSLATE_WRITE_DEBUG, curaddr))
- space->write_byte(curaddr, data);
+ address_space *tspace;
+ if (f && space->device().memory().translate(space->spacenum(), device_memory_interface::TR_WRITE, curaddr, tspace))
+ tspace->write_byte(curaddr, data);
}
break;
case 3:
@@ -1996,8 +2232,9 @@ void debugger_commands::execute_load(int ref, const std::vector<std::string> &pa
offs_t curaddr = i;
u16 data;
f.read((char *)&data, 2);
- if (f && space->device().memory().translate(space->spacenum(), TRANSLATE_WRITE_DEBUG, curaddr))
- space->write_word(curaddr, data);
+ address_space *tspace;
+ if (f && space->device().memory().translate(space->spacenum(), device_memory_interface::TR_WRITE, curaddr, tspace))
+ tspace->write_word(curaddr, data);
}
break;
}
@@ -2012,40 +2249,86 @@ void debugger_commands::execute_load(int ref, const std::vector<std::string> &pa
/*-------------------------------------------------
+ execute_loadregion - execute the load command on region memory
+-------------------------------------------------*/
+
+void debugger_commands::execute_loadregion(const std::vector<std::string_view> &params)
+{
+ u64 offset, length;
+ memory_region *region;
+
+ // validate parameters
+ if (!m_console.validate_number_parameter(params[1], offset))
+ return;
+ if (!m_console.validate_number_parameter(params[2], length))
+ return;
+ if (!m_console.validate_memory_region_parameter(params[3], region))
+ return;
+
+ if (offset >= region->bytes())
+ {
+ m_console.printf("Invalid offset\n");
+ return;
+ }
+ if ((length <= 0) || ((length + offset) >= region->bytes()))
+ length = region->bytes() - offset;
+
+ // open the file
+ std::string filename(params[0]);
+ FILE *const f = fopen(filename.c_str(), "rb");
+ if (!f)
+ {
+ m_console.printf("Error opening file '%s'\n", params[0]);
+ return;
+ }
+
+ fseek(f, 0L, SEEK_END);
+ u64 size = ftell(f);
+ rewind(f);
+
+ // check file size
+ if (length >= size)
+ length = size;
+
+ fread(region->base() + offset, 1, length, f);
+
+ fclose(f);
+ m_console.printf("Data loaded successfully to memory : 0x%X to 0x%X\n", offset, offset + length - 1);
+}
+
+
+/*-------------------------------------------------
execute_dump - execute the dump command
-------------------------------------------------*/
-void debugger_commands::execute_dump(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_dump(int spacenum, const std::vector<std::string_view> &params)
{
- /* validate parameters */
+ // validate parameters
+ address_space *space;
u64 offset;
- if (!validate_number_parameter(params[1], offset))
+ if (!m_console.validate_target_address_parameter(params[1], spacenum, space, offset))
return;
u64 length;
- if (!validate_number_parameter(params[2], length))
+ if (!m_console.validate_number_parameter(params[2], length))
return;
u64 width = 0;
- if (params.size() > 3 && !validate_number_parameter(params[3], width))
- return;
-
- u64 ascii = 1;
- if (params.size() > 4 && !validate_number_parameter(params[4], ascii))
+ if (params.size() > 3 && !m_console.validate_number_parameter(params[3], width))
return;
- address_space *space;
- if (!validate_cpu_space_parameter((params.size() > 6) ? params[6].c_str() : nullptr, ref, space))
+ bool ascii = true;
+ if (params.size() > 4 && !m_console.validate_boolean_parameter(params[4], ascii))
return;
u64 rowsize = space->byte_to_address(16);
- if (params.size() > 5 && !validate_number_parameter(params[5], rowsize))
+ if (params.size() > 5 && !m_console.validate_number_parameter(params[5], rowsize))
return;
int shift = space->addr_shift();
- u64 granularity = shift > 0 ? 2 : 1 << -shift;
+ u64 granularity = shift >= 0 ? 1 : 1 << -shift;
- /* further validation */
+ // further validation
if (width == 0)
width = space->data_width() / 8;
if (width < space->address_to_byte(1))
@@ -2069,15 +2352,16 @@ void debugger_commands::execute_dump(int ref, const std::vector<std::string> &pa
u64 endoffset = (offset + length - 1) & space->addrmask();
offset = offset & space->addrmask();
- /* open the file */
- FILE* f = fopen(params[0].c_str(), "w");
+ // open the file
+ std::string filename(params[0]);
+ FILE *const f = fopen(filename.c_str(), "w");
if (!f)
{
- m_console.printf("Error opening file '%s'\n", params[0].c_str());
+ m_console.printf("Error opening file '%s'\n", params[0]);
return;
}
- /* now write the data out */
+ // now write the data out
util::ovectorstream output;
output.reserve(200);
@@ -2091,30 +2375,31 @@ void debugger_commands::execute_dump(int ref, const std::vector<std::string> &pa
output.clear();
output.rdbuf()->clear();
- /* print the address */
+ // print the address
util::stream_format(output, "%0*X: ", space->logaddrchars(), i);
- /* print the bytes */
+ // print the bytes
for (u64 j = 0; j < rowsize; j += delta)
{
if (i + j <= endoffset)
{
offs_t curaddr = i + j;
- if (space->device().memory().translate(space->spacenum(), TRANSLATE_READ_DEBUG, curaddr))
+ address_space *tspace;
+ if (space->device().memory().translate(space->spacenum(), device_memory_interface::TR_READ, curaddr, tspace))
{
switch (width)
{
case 8:
- util::stream_format(output, " %016X", space->read_qword_unaligned(i+j));
+ util::stream_format(output, " %016X", tspace->read_qword_unaligned(i+j));
break;
case 4:
- util::stream_format(output, " %08X", space->read_dword_unaligned(i+j));
+ util::stream_format(output, " %08X", tspace->read_dword_unaligned(i+j));
break;
case 2:
- util::stream_format(output, " %04X", space->read_word_unaligned(i+j));
+ util::stream_format(output, " %04X", tspace->read_word_unaligned(i+j));
break;
case 1:
- util::stream_format(output, " %02X", space->read_byte(i+j));
+ util::stream_format(output, " %02X", tspace->read_byte(i+j));
break;
}
}
@@ -2127,29 +2412,30 @@ void debugger_commands::execute_dump(int ref, const std::vector<std::string> &pa
util::stream_format(output, " %*s", width * 2, "");
}
- /* print the ASCII */
+ // print the ASCII
if (ascii)
{
util::stream_format(output, " ");
for (u64 j = 0; j < rowsize && (i + j) <= endoffset; j += delta)
{
offs_t curaddr = i + j;
- if (space->device().memory().translate(space->spacenum(), TRANSLATE_READ_DEBUG, curaddr))
+ address_space *tspace;
+ if (space->device().memory().translate(space->spacenum(), device_memory_interface::TR_READ, curaddr, tspace))
{
u64 data = 0;
switch (width)
{
case 8:
- data = space->read_qword_unaligned(i+j);
+ data = tspace->read_qword_unaligned(i+j);
break;
case 4:
- data = space->read_dword_unaligned(i+j);
+ data = tspace->read_dword_unaligned(i+j);
break;
case 2:
- data = space->read_word_unaligned(i+j);
+ data = tspace->read_word_unaligned(i+j);
break;
case 1:
- data = space->read_byte(i+j);
+ data = tspace->read_byte(i+j);
break;
}
for (unsigned int b = 0; b != width; b++) {
@@ -2164,167 +2450,361 @@ void debugger_commands::execute_dump(int ref, const std::vector<std::string> &pa
}
}
- /* output the result */
+ // output the result
auto const &text = output.vec();
fprintf(f, "%.*s\n", int(unsigned(text.size())), &text[0]);
}
- /* close the file */
+ // close the file
fclose(f);
m_console.printf("Data dumped successfully\n");
}
-/*-------------------------------------------------
- execute_cheatinit - initialize the cheat system
--------------------------------------------------*/
+//-------------------------------------------------
+// execute_strdump - execute the strdump command
+//-------------------------------------------------
-void debugger_commands::execute_cheatinit(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_strdump(int spacenum, const std::vector<std::string_view> &params)
{
- u64 offset, length = 0, real_length = 0;
- address_space *space;
- u32 active_cheat = 0;
- u64 curaddr;
- u8 i, region_count = 0;
+ // validate parameters
+ u64 offset;
+ if (!m_console.validate_number_parameter(params[1], offset))
+ return;
- cheat_region_map cheat_region[100];
+ u64 length;
+ if (!m_console.validate_number_parameter(params[2], length))
+ return;
- memset(cheat_region, 0, sizeof(cheat_region));
+ u64 term = 0;
+ if (params.size() > 3 && !m_console.validate_number_parameter(params[3], term))
+ return;
- /* validate parameters */
- if (!validate_cpu_space_parameter((params.size() > 3) ? params[3].c_str() : nullptr, AS_PROGRAM, space))
+ address_space *space;
+ if (!m_console.validate_device_space_parameter((params.size() > 4) ? params[4] : std::string_view(), spacenum, space))
return;
- if (ref == 0)
+ // further validation
+ if (term >= 0x100 && term != u64(-0x80))
{
- m_cheat.width = 1;
- m_cheat.signed_cheat = false;
- m_cheat.swapped_cheat = false;
- if (!params.empty())
+ m_console.printf("Invalid termination character\n");
+ return;
+ }
+
+ // open the file
+ std::string filename(params[0]);
+ FILE *f = fopen(filename.c_str(), "w");
+ if (!f)
+ {
+ m_console.printf("Error opening file '%s'\n", params[0]);
+ return;
+ }
+
+ const int shift = space->addr_shift();
+ const unsigned delta = (shift >= 0) ? (1 << shift) : 1;
+ const unsigned width = (shift >= 0) ? 1 : (1 << -shift);
+ const bool be = space->endianness() == ENDIANNESS_BIG;
+
+ offset = offset & space->addrmask();
+ if (shift > 0)
+ length >>= shift;
+
+ // now write the data out
+ util::ovectorstream output;
+ output.reserve(200);
+
+ auto dis = space->device().machine().disable_side_effects();
+
+ bool terminated = true;
+ while (length-- != 0)
+ {
+ if (terminated)
{
- char *srtpnt = (char*)params[0].c_str();
+ terminated = false;
+ output.clear();
+ output.rdbuf()->clear();
- if (*srtpnt == 's')
- m_cheat.signed_cheat = true;
- else if (*srtpnt == 'u')
- m_cheat.signed_cheat = false;
- else
+ // print the address
+ util::stream_format(output, "%0*X: \"", space->logaddrchars(), offset);
+ }
+
+ // get the character data
+ u64 data = 0;
+ offs_t curaddr = offset;
+ address_space *tspace;
+ if (space->device().memory().translate(space->spacenum(), device_memory_interface::TR_READ, curaddr, tspace))
+ {
+ switch (width)
{
- m_console.printf("Invalid sign: expected s or u\n");
- return;
+ case 1:
+ data = tspace->read_byte(curaddr);
+ break;
+
+ case 2:
+ data = tspace->read_word(curaddr);
+ if (be)
+ data = swapendian_int16(data);
+ break;
+
+ case 4:
+ data = tspace->read_dword(curaddr);
+ if (be)
+ data = swapendian_int32(data);
+ break;
+
+ case 8:
+ data = tspace->read_qword(curaddr);
+ if (be)
+ data = swapendian_int64(data);
+ break;
}
+ }
- if (*(++srtpnt) == 'b')
- m_cheat.width = 1;
- else if (*srtpnt == 'w')
- m_cheat.width = 2;
- else if (*srtpnt == 'd')
- m_cheat.width = 4;
- else if (*srtpnt == 'q')
- m_cheat.width = 8;
- else
+ // print the characters
+ for (int n = 0; n < width; n++)
+ {
+ // check for termination within word
+ if (terminated)
{
- m_console.printf("Invalid width: expected b, w, d or q\n");
- return;
+ terminated = false;
+
+ // output the result
+ auto const &text = output.vec();
+ fprintf(f, "%.*s\"\n", int(unsigned(text.size())), &text[0]);
+ output.clear();
+ output.rdbuf()->clear();
+
+ // print the address
+ util::stream_format(output, "%0*X.%d: \"", space->logaddrchars(), offset, n);
+ }
+
+ u8 ch = data & 0xff;
+ data >>= 8;
+
+ // check for termination
+ if (term == u64(-0x80))
+ {
+ if (BIT(ch, 7))
+ {
+ terminated = true;
+ ch &= 0x7f;
+ }
+ }
+ else if (ch == term)
+ {
+ terminated = true;
+ continue;
}
- if (*(++srtpnt) == 's')
- m_cheat.swapped_cheat = true;
+ // check for non-ASCII characters
+ if (ch < 0x20 || ch >= 0x7f)
+ {
+ // use special or octal escape
+ if (ch >= 0x07 && ch <= 0x0d)
+ util::stream_format(output, "\\%c", "abtnvfr"[ch - 0x07]);
+ else
+ util::stream_format(output, "\\%03o", ch);
+ }
else
- m_cheat.swapped_cheat = false;
+ {
+ if (ch == '"' || ch == '\\')
+ output << '\\';
+ output << char(ch);
+ }
}
+
+ if (terminated)
+ {
+ // output the result
+ auto const &text = output.vec();
+ fprintf(f, "%.*s\"\n", int(unsigned(text.size())), &text[0]);
+ output.clear();
+ output.rdbuf()->clear();
+ }
+
+ offset += delta;
}
- /* initialize entire memory by default */
- if (params.size() <= 1)
+ if (!terminated)
{
- for (address_map_entry &entry : space->map()->m_entrylist)
+ // output the result
+ auto const &text = output.vec();
+ fprintf(f, "%.*s\"\\\n", int(unsigned(text.size())), &text[0]);
+ }
+
+ // close the file
+ fclose(f);
+ m_console.printf("Data dumped successfully\n");
+}
+
+
+/*-------------------------------------------------
+ execute_cheatrange - add a range to search for
+ cheats
+-------------------------------------------------*/
+
+void debugger_commands::execute_cheatrange(bool init, const std::vector<std::string_view> &params)
+{
+ address_space *space = m_cheat.space;
+ if (!space && !init)
+ {
+ m_console.printf("Use cheatinit before cheatrange\n");
+ return;
+ }
+
+ u8 width = (space || !init) ? m_cheat.width : 1;
+ bool signed_cheat = (space || !init) ? m_cheat.signed_cheat : false;
+ bool swapped_cheat = (space || !init) ? m_cheat.swapped_cheat : false;
+ if (init)
+ {
+ // first argument is sign/size/swap flags
+ if (!params.empty())
{
- cheat_region[region_count].offset = entry.m_addrstart & space->addrmask();
- cheat_region[region_count].endoffset = entry.m_addrend & space->addrmask();
- cheat_region[region_count].share = entry.m_share;
- cheat_region[region_count].disabled = (entry.m_write.m_type == AMH_RAM) ? false : true;
+ std::string_view const &srtpnt = params[0];
+ if (!srtpnt.empty())
+ {
+ width = 1;
+ signed_cheat = false;
+ swapped_cheat = false;
+ }
+
+ if (srtpnt.length() >= 1)
+ {
+ char const sspec = std::tolower((unsigned char)srtpnt[0]);
+ if (sspec == 's')
+ signed_cheat = true;
+ else if (sspec == 'u')
+ signed_cheat = false;
+ else
+ {
+ m_console.printf("Invalid sign: expected s or u\n");
+ return;
+ }
+ }
- /* disable double share regions */
- if (entry.m_share != nullptr)
- for (i = 0; i < region_count; i++)
- if (cheat_region[i].share != nullptr)
- if (strcmp(cheat_region[i].share, entry.m_share) == 0)
- cheat_region[region_count].disabled = true;
+ if (srtpnt.length() >= 2)
+ {
+ char const wspec = std::tolower((unsigned char)srtpnt[1]);
+ if (wspec == 'b')
+ width = 1;
+ else if (wspec == 'w')
+ width = 2;
+ else if (wspec == 'd')
+ width = 4;
+ else if (wspec == 'q')
+ width = 8;
+ else
+ {
+ m_console.printf("Invalid width: expected b, w, d or q\n");
+ return;
+ }
+ }
- region_count++;
+ if (srtpnt.length() >= 3)
+ {
+ if (std::tolower((unsigned char)srtpnt[2]) == 's')
+ swapped_cheat = true;
+ else
+ {
+ m_console.printf("Invalid swap: expected s\n");
+ return;
+ }
+ }
}
+
+ // fourth argument is device/space
+ if (!m_console.validate_device_space_parameter((params.size() > 3) ? params[3] : std::string_view(), -1, space))
+ return;
}
- else
+
+ cheat_region_map cheat_region[100]; // FIXME: magic number
+ unsigned region_count = 0;
+ if (params.size() >= (init ? 3 : 2))
{
- /* validate parameters */
- if (!validate_number_parameter(params[(ref == 0) ? 1 : 0], offset))
+ // validate parameters
+ u64 offset, length;
+ if (!m_console.validate_number_parameter(params[init ? 1 : 0], offset))
return;
- if (!validate_number_parameter(params[(ref == 0) ? 2 : 1], length))
+ if (!m_console.validate_number_parameter(params[init ? 2 : 1], length))
return;
- /* force region to the specified range */
+ // force region to the specified range
cheat_region[region_count].offset = offset & space->addrmask();
cheat_region[region_count].endoffset = (offset + length - 1) & space->addrmask();
cheat_region[region_count].share = nullptr;
cheat_region[region_count].disabled = false;
region_count++;
}
+ else
+ {
+ // initialize to entire memory by default
+ for (address_map_entry &entry : space->map()->m_entrylist)
+ {
+ cheat_region[region_count].offset = entry.m_addrstart & space->addrmask();
+ cheat_region[region_count].endoffset = entry.m_addrend & space->addrmask();
+ cheat_region[region_count].share = entry.m_share;
+ cheat_region[region_count].disabled = entry.m_write.m_type != AMH_RAM;
+
+ // disable duplicate share regions
+ if (entry.m_share)
+ for (unsigned i = 0; i < region_count; i++)
+ if (cheat_region[i].share && !strcmp(cheat_region[i].share, entry.m_share))
+ cheat_region[region_count].disabled = true;
+
+ if (!cheat_region[region_count].disabled)
+ region_count++;
+ }
+ }
- /* determine the writable extent of each region in total */
- for (i = 0; i < region_count; i++)
- if (!cheat_region[i].disabled)
- for (curaddr = cheat_region[i].offset; curaddr <= cheat_region[i].endoffset; curaddr += m_cheat.width)
- if (cheat_address_is_valid(*space, curaddr))
- real_length++;
+ // determine the writable extent of each region in total
+ u64 real_length = 0;
+ for (unsigned i = 0; i < region_count; i++)
+ for (u64 curaddr = cheat_region[i].offset; curaddr <= cheat_region[i].endoffset; curaddr += width)
+ if (cheat_address_is_valid(*space, curaddr))
+ real_length++;
- if (real_length == 0)
+ if (!real_length)
{
m_console.printf("No writable bytes found in this area\n");
return;
}
- if (ref == 0)
+ size_t active_cheat = 0;
+ if (init)
{
- /* initialize new cheat system */
- m_cheat.cheatmap.resize(real_length);
+ // initialize new cheat system
+ m_cheat.space = space;
+ m_cheat.width = width;
m_cheat.undo = 0;
- m_cheat.cpu[0] = params.size() > 3 ? params[3][0] : '0';
+ m_cheat.signed_cheat = signed_cheat;
+ m_cheat.swapped_cheat = swapped_cheat;
}
else
{
- /* add range to cheat system */
- if (m_cheat.cpu[0] == 0)
- {
- m_console.printf("Use cheatinit before cheatrange\n");
- return;
- }
-
- if (!validate_cpu_space_parameter(m_cheat.cpu, AS_PROGRAM, space))
- return;
-
active_cheat = m_cheat.cheatmap.size();
- m_cheat.cheatmap.resize(m_cheat.cheatmap.size() + real_length);
}
+ m_cheat.cheatmap.resize(active_cheat + real_length);
- /* initialize cheatmap in the selected space */
- for (i = 0; i < region_count; i++)
- if (!cheat_region[i].disabled)
- for (curaddr = cheat_region[i].offset; curaddr <= cheat_region[i].endoffset; curaddr += m_cheat.width)
- if (cheat_address_is_valid(*space, curaddr))
- {
- m_cheat.cheatmap[active_cheat].previous_value = cheat_read_extended(&m_cheat, *space, curaddr);
- m_cheat.cheatmap[active_cheat].first_value = m_cheat.cheatmap[active_cheat].previous_value;
- m_cheat.cheatmap[active_cheat].offset = curaddr;
- m_cheat.cheatmap[active_cheat].state = 1;
- m_cheat.cheatmap[active_cheat].undo = 0;
- active_cheat++;
- }
+ // initialize cheatmap in the selected space
+ for (unsigned i = 0; i < region_count; i++)
+ for (u64 curaddr = cheat_region[i].offset; curaddr <= cheat_region[i].endoffset; curaddr += width)
+ if (cheat_address_is_valid(*space, curaddr))
+ {
+ m_cheat.cheatmap[active_cheat].previous_value = m_cheat.read_extended(curaddr);
+ m_cheat.cheatmap[active_cheat].first_value = m_cheat.cheatmap[active_cheat].previous_value;
+ m_cheat.cheatmap[active_cheat].offset = curaddr;
+ m_cheat.cheatmap[active_cheat].state = 1;
+ m_cheat.cheatmap[active_cheat].undo = 0;
+ active_cheat++;
+ }
- /* give a detailed init message to avoid searches being mistakingly carried out on the wrong CPU */
- device_t *cpu = nullptr;
- validate_cpu_parameter(m_cheat.cpu, cpu);
- m_console.printf("%u cheat initialized for CPU index %s ( aka %s )\n", active_cheat, m_cheat.cpu, cpu->tag());
+ // give a detailed init message to avoid searches being mistakenly carried out on the wrong CPU
+ m_console.printf(
+ "%u cheat locations initialized for %s '%s' %s space\n",
+ active_cheat,
+ space->device().type().fullname(),
+ space->device().tag(),
+ space->name());
}
@@ -2332,14 +2812,8 @@ void debugger_commands::execute_cheatinit(int ref, const std::vector<std::string
execute_cheatnext - execute the search
-------------------------------------------------*/
-void debugger_commands::execute_cheatnext(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_cheatnext(bool initial, const std::vector<std::string_view> &params)
{
- address_space *space;
- u64 cheatindex;
- u32 active_cheat = 0;
- u8 condition;
- u64 comp_value = 0;
-
enum
{
CHEAT_ALL = 0,
@@ -2358,54 +2832,61 @@ void debugger_commands::execute_cheatnext(int ref, const std::vector<std::string
CHEAT_CHANGEDBY
};
- if (m_cheat.cpu[0] == 0)
+ address_space *const space = m_cheat.space;
+ if (!space)
{
m_console.printf("Use cheatinit before cheatnext\n");
return;
}
- if (!validate_cpu_space_parameter(m_cheat.cpu, AS_PROGRAM, space))
- return;
-
- if (params.size() > 1 && !validate_number_parameter(params[1], comp_value))
+ u64 comp_value = 0;
+ if (params.size() > 1 && !m_console.validate_number_parameter(params[1], comp_value))
return;
- comp_value = cheat_sign_extend(&m_cheat, comp_value);
+ comp_value = m_cheat.sign_extend(comp_value);
- /* decode condition */
- if (params[0] == "all")
- condition = CHEAT_ALL;
- else if (params[0] == "equal" || params[0] == "eq")
- condition = (params.size() > 1) ? CHEAT_EQUALTO : CHEAT_EQUAL;
- else if (params[0] == "notequal" || params[0] == "ne")
- condition = (params.size() > 1) ? CHEAT_NOTEQUALTO : CHEAT_NOTEQUAL;
- else if (params[0] == "decrease" || params[0] == "de" || params[0] == "-")
- condition = (params.size() > 1) ? CHEAT_DECREASEOF : CHEAT_DECREASE;
- else if (params[0] == "increase" || params[0] == "in" || params[0] == "+")
- condition = (params.size() > 1) ? CHEAT_INCREASEOF : CHEAT_INCREASE;
- else if (params[0] == "decreaseorequal" || params[0] == "deeq")
- condition = CHEAT_DECREASE_OR_EQUAL;
- else if (params[0] == "increaseorequal" || params[0] == "ineq")
- condition = CHEAT_INCREASE_OR_EQUAL;
- else if (params[0] == "smallerof" || params[0] == "lt" || params[0] == "<")
- condition = CHEAT_SMALLEROF;
- else if (params[0] == "greaterof" || params[0] == "gt" || params[0] == ">")
- condition = CHEAT_GREATEROF;
- else if (params[0] == "changedby" || params[0] == "ch" || params[0] == "~")
- condition = CHEAT_CHANGEDBY;
- else
+ // decode condition
+ u8 condition;
{
- m_console.printf("Invalid condition type\n");
- return;
+ using util::streqlower;
+ using namespace std::literals;
+ if (streqlower(params[0], "all"sv))
+ condition = CHEAT_ALL;
+ else if (streqlower(params[0], "equal"sv) || streqlower(params[0], "eq"sv))
+ condition = (params.size() > 1) ? CHEAT_EQUALTO : CHEAT_EQUAL;
+ else if (streqlower(params[0], "notequal"sv) || streqlower(params[0], "ne"sv))
+ condition = (params.size() > 1) ? CHEAT_NOTEQUALTO : CHEAT_NOTEQUAL;
+ else if (streqlower(params[0], "decrease"sv) || streqlower(params[0], "de"sv) || params[0] == "-"sv)
+ condition = (params.size() > 1) ? CHEAT_DECREASEOF : CHEAT_DECREASE;
+ else if (streqlower(params[0], "increase"sv) || streqlower(params[0], "in"sv) || params[0] == "+"sv)
+ condition = (params.size() > 1) ? CHEAT_INCREASEOF : CHEAT_INCREASE;
+ else if (streqlower(params[0], "decreaseorequal"sv) || streqlower(params[0], "deeq"sv))
+ condition = CHEAT_DECREASE_OR_EQUAL;
+ else if (streqlower(params[0], "increaseorequal"sv) || streqlower(params[0], "ineq"sv))
+ condition = CHEAT_INCREASE_OR_EQUAL;
+ else if (streqlower(params[0], "smallerof"sv) || streqlower(params[0], "lt"sv) || params[0] == "<"sv)
+ condition = CHEAT_SMALLEROF;
+ else if (streqlower(params[0], "greaterof"sv) || streqlower(params[0], "gt"sv) || params[0] == ">"sv)
+ condition = CHEAT_GREATEROF;
+ else if (streqlower(params[0], "changedby"sv) || streqlower(params[0], "ch"sv) || params[0] == "~"sv)
+ condition = CHEAT_CHANGEDBY;
+ else
+ {
+ m_console.printf("Invalid condition type\n");
+ return;
+ }
}
m_cheat.undo++;
- /* execute the search */
- for (cheatindex = 0; cheatindex < m_cheat.cheatmap.size(); cheatindex += 1)
+ // execute the search
+ u32 active_cheat = 0;
+ for (u64 cheatindex = 0; cheatindex < m_cheat.cheatmap.size(); cheatindex += 1)
if (m_cheat.cheatmap[cheatindex].state == 1)
{
- u64 cheat_value = cheat_read_extended(&m_cheat, *space, m_cheat.cheatmap[cheatindex].offset);
- u64 comp_byte = (ref == 0) ? m_cheat.cheatmap[cheatindex].previous_value : m_cheat.cheatmap[cheatindex].first_value;
+ u64 cheat_value = m_cheat.read_extended(m_cheat.cheatmap[cheatindex].offset);
+ u64 comp_byte = initial
+ ? m_cheat.cheatmap[cheatindex].first_value
+ : m_cheat.cheatmap[cheatindex].previous_value;
u8 disable_byte = false;
switch (condition)
@@ -2494,12 +2975,12 @@ void debugger_commands::execute_cheatnext(int ref, const std::vector<std::string
else
active_cheat++;
- /* update previous value */
+ // update previous value
m_cheat.cheatmap[cheatindex].previous_value = cheat_value;
}
if (active_cheat <= 5)
- execute_cheatlist(0, std::vector<std::string>());
+ execute_cheatlist(std::vector<std::string_view>());
m_console.printf("%u cheats found\n", active_cheat);
}
@@ -2509,51 +2990,79 @@ void debugger_commands::execute_cheatnext(int ref, const std::vector<std::string
execute_cheatlist - show a list of active cheat
-------------------------------------------------*/
-void debugger_commands::execute_cheatlist(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_cheatlist(const std::vector<std::string_view> &params)
{
- char spaceletter, sizeletter;
- address_space *space;
- device_t *cpu;
- u32 active_cheat = 0;
- u64 cheatindex;
- u64 sizemask;
- FILE *f = nullptr;
-
- if (!validate_cpu_space_parameter(m_cheat.cpu, AS_PROGRAM, space))
- return;
-
- if (!validate_cpu_parameter(m_cheat.cpu, cpu))
+ address_space *const space = m_cheat.space;
+ if (!space)
+ {
+ m_console.printf("Use cheatinit before cheatlist\n");
return;
+ }
+ FILE *f = nullptr;
if (params.size() > 0)
- f = fopen(params[0].c_str(), "w");
+ {
+ std::string filename(params[0]);
+ f = fopen(filename.c_str(), "w");
+ if (!f)
+ {
+ m_console.printf("Error opening file '%s'\n", params[0]);
+ return;
+ }
+ }
+ // get device/space syntax for memory access
+ std::string tag(space->device().tag());
+ std::string spaceletter;
switch (space->spacenum())
{
- default:
- case AS_PROGRAM: spaceletter = 'p'; break;
- case AS_DATA: spaceletter = 'd'; break;
- case AS_IO: spaceletter = 'i'; break;
- case AS_OPCODES: spaceletter = 'o'; break;
+ default:
+ tag.append(1, ':');
+ tag.append(space->name());
+ break;
+ case AS_PROGRAM:
+ spaceletter = "p";
+ break;
+ case AS_DATA:
+ spaceletter = "d";
+ break;
+ case AS_IO:
+ spaceletter = "i";
+ break;
+ case AS_OPCODES:
+ spaceletter = "3";
+ break;
}
+ // get size syntax for memory access and formatting values
+ bool const octal = space->is_octal();
+ int const addrchars = octal
+ ? ((2 + space->logaddr_width()) / 3)
+ : ((3 + space->logaddr_width()) / 4);
+ int const datachars = octal
+ ? ((2 + (m_cheat.width * 8)) / 3)
+ : ((3 + (m_cheat.width * 8)) / 4);
+ u64 const sizemask = util::make_bitmask<u64>(m_cheat.width * 8);
+ char sizeletter;
switch (m_cheat.width)
{
- default:
- case 1: sizeletter = 'b'; sizemask = 0xffU; break;
- case 2: sizeletter = 'w'; sizemask = 0xffffU; break;
- case 4: sizeletter = 'd'; sizemask = 0xffffffffU; break;
- case 8: sizeletter = 'q'; sizemask = 0xffffffffffffffffU; break;
+ default:
+ case 1: sizeletter = 'b'; break;
+ case 2: sizeletter = 'w'; break;
+ case 4: sizeletter = 'd'; break;
+ case 8: sizeletter = 'q'; break;
}
- /* write the cheat list */
+ // write the cheat list
+ u32 active_cheat = 0;
util::ovectorstream output;
- for (cheatindex = 0; cheatindex < m_cheat.cheatmap.size(); cheatindex += 1)
+ for (u64 cheatindex = 0; cheatindex < m_cheat.cheatmap.size(); cheatindex += 1)
{
if (m_cheat.cheatmap[cheatindex].state == 1)
{
- u64 value = cheat_byte_swap(&m_cheat, cheat_read_extended(&m_cheat, *space, m_cheat.cheatmap[cheatindex].offset)) & sizemask;
- offs_t address = space->byte_to_address(m_cheat.cheatmap[cheatindex].offset);
+ u64 const value = m_cheat.byte_swap(m_cheat.read_extended(m_cheat.cheatmap[cheatindex].offset)) & sizemask;
+ u64 const first_value = m_cheat.byte_swap(m_cheat.cheatmap[cheatindex].first_value) & sizemask;
+ offs_t const address = space->byte_to_address(m_cheat.cheatmap[cheatindex].offset);
if (!params.empty())
{
@@ -2562,23 +3071,31 @@ void debugger_commands::execute_cheatlist(int ref, const std::vector<std::string
output.rdbuf()->clear();
stream_format(
output,
- " <cheat desc=\"Possibility %d : %0*X (%0*X)\">\n"
- " <script state=\"run\">\n"
- " <action>%s.p%c%c@%0*X=%0*X</action>\n"
- " </script>\n"
- " </cheat>\n\n",
- active_cheat, space->logaddrchars(), address, m_cheat.width * 2, value,
- cpu->tag(), spaceletter, sizeletter, space->logaddrchars(), address, m_cheat.width * 2, cheat_byte_swap(&m_cheat, m_cheat.cheatmap[cheatindex].first_value) & sizemask);
+ octal ?
+ " <cheat desc=\"Possibility %d: 0%0*o (0%0*o)\">\n"
+ " <script state=\"run\">\n"
+ " <action>%s.%s%c@0o%0*o=0o%0*o</action>\n"
+ " </script>\n"
+ " </cheat>\n\n" :
+ " <cheat desc=\"Possibility %d: %0*X (%0*X)\">\n"
+ " <script state=\"run\">\n"
+ " <action>%s.%s%c@0x%0*X=0x%0*X</action>\n"
+ " </script>\n"
+ " </cheat>\n\n",
+ active_cheat, addrchars, address, datachars, value,
+ tag, spaceletter, sizeletter, addrchars, address, datachars, first_value);
auto const &text(output.vec());
fprintf(f, "%.*s", int(unsigned(text.size())), &text[0]);
}
else
{
m_console.printf(
- "Address=%0*X Start=%0*X Current=%0*X\n",
- space->logaddrchars(), address,
- m_cheat.width * 2, cheat_byte_swap(&m_cheat, m_cheat.cheatmap[cheatindex].first_value) & sizemask,
- m_cheat.width * 2, value);
+ octal
+ ? "Address=0%0*o Start=0%0*o Current=0%0*o\n"
+ : "Address=%0*X Start=%0*X Current=%0*X\n",
+ addrchars, address,
+ datachars, first_value,
+ datachars, value);
}
}
}
@@ -2591,14 +3108,12 @@ void debugger_commands::execute_cheatlist(int ref, const std::vector<std::string
execute_cheatundo - undo the last search
-------------------------------------------------*/
-void debugger_commands::execute_cheatundo(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_cheatundo(const std::vector<std::string_view> &params)
{
- u64 cheatindex;
- u32 undo_count = 0;
-
if (m_cheat.undo > 0)
{
- for (cheatindex = 0; cheatindex < m_cheat.cheatmap.size(); cheatindex += 1)
+ u64 undo_count = 0;
+ for (u64 cheatindex = 0; cheatindex < m_cheat.cheatmap.size(); cheatindex += 1)
{
if (m_cheat.cheatmap[cheatindex].undo == m_cheat.undo)
{
@@ -2612,7 +3127,9 @@ void debugger_commands::execute_cheatundo(int ref, const std::vector<std::string
m_console.printf("%u cheat reactivated\n", undo_count);
}
else
+ {
m_console.printf("Maximum undo reached\n");
+ }
}
@@ -2620,89 +3137,119 @@ void debugger_commands::execute_cheatundo(int ref, const std::vector<std::string
execute_find - execute the find command
-------------------------------------------------*/
-void debugger_commands::execute_find(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_find(int spacenum, const std::vector<std::string_view> &params)
{
- u64 offset, endoffset, length;
+ u64 offset, length;
address_space *space;
- u64 data_to_find[256];
- u8 data_size[256];
- int cur_data_size;
- int data_count = 0;
- int found = 0;
- int j;
- /* validate parameters */
- if (!validate_number_parameter(params[0], offset))
- return;
- if (!validate_number_parameter(params[1], length))
+ // validate parameters
+ if (!m_console.validate_target_address_parameter(params[0], spacenum, space, offset))
return;
- if (!validate_cpu_space_parameter(nullptr, ref, space))
+ if (!m_console.validate_number_parameter(params[1], length))
return;
- /* further validation */
- endoffset = space->address_to_byte_end((offset + length - 1) & space->addrmask());
+ // further validation
+ u64 const endoffset = space->address_to_byte_end((offset + length - 1) & space->addrmask());
offset = space->address_to_byte(offset & space->addrmask());
- cur_data_size = space->addr_shift() > 0 ? 2 : 1 << -space->addr_shift();
+ int cur_data_size = (space->addr_shift() > 0) ? 2 : (1 << -space->addr_shift());
if (cur_data_size == 0)
cur_data_size = 1;
- /* parse the data parameters */
+ // parse the data parameters
+ u64 data_to_find[256];
+ u8 data_size[256];
+ int data_count = 0;
for (int i = 2; i < params.size(); i++)
{
- const char *pdata = params[i].c_str();
- size_t pdatalen = strlen(pdata) - 1;
+ std::string_view pdata = params[i];
- /* check for a string */
- if (pdata[0] == '"' && pdata[pdatalen] == '"')
+ if (!pdata.empty() && pdata.front() == '"' && pdata.back() == '"') // check for a string
{
- for (j = 1; j < pdatalen; j++)
+ auto const pdatalen = params[i].length() - 1;
+ for (int j = 1; j < pdatalen; j++)
{
data_to_find[data_count] = pdata[j];
data_size[data_count++] = 1;
}
}
-
- /* otherwise, validate as a number */
- else
+ else // otherwise, validate as a number
{
- /* check for a 'b','w','d',or 'q' prefix */
+ // check for a 'b','w','d',or 'q' prefix
data_size[data_count] = cur_data_size;
- if (tolower(u8(pdata[0])) == 'b' && pdata[1] == '.') { data_size[data_count] = cur_data_size = 1; pdata += 2; }
- if (tolower(u8(pdata[0])) == 'w' && pdata[1] == '.') { data_size[data_count] = cur_data_size = 2; pdata += 2; }
- if (tolower(u8(pdata[0])) == 'd' && pdata[1] == '.') { data_size[data_count] = cur_data_size = 4; pdata += 2; }
- if (tolower(u8(pdata[0])) == 'q' && pdata[1] == '.') { data_size[data_count] = cur_data_size = 8; pdata += 2; }
+ if (pdata.length() >= 2)
+ {
+ if (tolower(u8(pdata[0])) == 'b' && pdata[1] == '.') { data_size[data_count] = cur_data_size = 1; pdata.remove_prefix(2); }
+ if (tolower(u8(pdata[0])) == 'w' && pdata[1] == '.') { data_size[data_count] = cur_data_size = 2; pdata.remove_prefix(2); }
+ if (tolower(u8(pdata[0])) == 'd' && pdata[1] == '.') { data_size[data_count] = cur_data_size = 4; pdata.remove_prefix(2); }
+ if (tolower(u8(pdata[0])) == 'q' && pdata[1] == '.') { data_size[data_count] = cur_data_size = 8; pdata.remove_prefix(2); }
+ }
- /* look for a wildcard */
- if (!strcmp(pdata, "?"))
+ // look for a wildcard
+ if (pdata == "?")
data_size[data_count++] |= 0x10;
- /* otherwise, validate as a number */
- else if (!validate_number_parameter(pdata, data_to_find[data_count++]))
+ // otherwise, validate as a number
+ else if (!m_console.validate_number_parameter(pdata, data_to_find[data_count++]))
return;
}
}
- /* now search */
+ // now search
+ device_memory_interface &memory = space->device().memory();
+ auto dis = space->device().machine().disable_side_effects();
+ int found = 0;
for (u64 i = offset; i <= endoffset; i += data_size[0])
{
int suboffset = 0;
- int match = 1;
+ bool match = true;
- /* find the entire string */
- for (j = 0; j < data_count && match; j++)
+ // find the entire string
+ for (int j = 0; j < data_count && match; j++)
{
+ offs_t address = space->byte_to_address(i + suboffset);
+ address_space *tspace;
switch (data_size[j])
{
- case 1: match = (u8(m_cpu.read_byte(*space, space->byte_to_address(i + suboffset), true)) == u8(data_to_find[j])); break;
- case 2: match = (u16(m_cpu.read_word(*space, space->byte_to_address(i + suboffset), true)) == u16(data_to_find[j])); break;
- case 4: match = (u32(m_cpu.read_dword(*space, space->byte_to_address(i + suboffset), true)) == u32(data_to_find[j])); break;
- case 8: match = (u64(m_cpu.read_qword(*space, space->byte_to_address(i + suboffset), true)) == u64(data_to_find[j])); break;
- default: /* all other cases are wildcards */ break;
+ case 1:
+ address &= space->logaddrmask();
+ if (memory.translate(space->spacenum(), device_memory_interface::TR_READ, address, tspace))
+ match = tspace->read_byte(address) == u8(data_to_find[j]);
+ else
+ match = false;
+ break;
+
+ case 2:
+ address &= space->logaddrmask();
+ if (memory.translate(space->spacenum(), device_memory_interface::TR_READ, address, tspace))
+ match = tspace->read_word_unaligned(address) == u16(data_to_find[j]);
+ else
+ match = false;
+ break;
+
+ case 4:
+ address &= space->logaddrmask();
+ if (memory.translate(space->spacenum(), device_memory_interface::TR_READ, address, tspace))
+ match = tspace->read_dword_unaligned(address) == u32(data_to_find[j]);
+ else
+ match = false;
+ break;
+
+ case 8:
+ address &= space->logaddrmask();
+ if (memory.translate(space->spacenum(), device_memory_interface::TR_READ, address, tspace))
+ match = tspace->read_qword_unaligned(address) == u64(data_to_find[j]);
+ else
+ match = false;
+ break;
+
+ default:
+ // all other cases are wildcards
+ break;
}
suboffset += data_size[j] & 0x0f;
}
- /* did we find it? */
+ // did we find it?
if (match)
{
found++;
@@ -2710,32 +3257,142 @@ void debugger_commands::execute_find(int ref, const std::vector<std::string> &pa
}
}
- /* print something if not found */
+ // print something if not found
if (found == 0)
m_console.printf("Not found\n");
}
+//-------------------------------------------------
+// execute_fill - execute the fill command
+//-------------------------------------------------
+
+void debugger_commands::execute_fill(int spacenum, const std::vector<std::string_view> &params)
+{
+ u64 offset, length;
+ address_space *space;
+
+ // validate parameters
+ if (!m_console.validate_target_address_parameter(params[0], spacenum, space, offset))
+ return;
+ if (!m_console.validate_number_parameter(params[1], length))
+ return;
+
+ // further validation
+ offset = space->address_to_byte(offset & space->addrmask());
+ int cur_data_size = (space->addr_shift() > 0) ? 2 : (1 << -space->addr_shift());
+ if (cur_data_size == 0)
+ cur_data_size = 1;
+
+ // parse the data parameters
+ u64 fill_data[256];
+ u8 fill_data_size[256];
+ int data_count = 0;
+ for (int i = 2; i < params.size(); i++)
+ {
+ std::string_view pdata = params[i];
+
+ // check for a string
+ if (!pdata.empty() && pdata.front() == '"' && pdata.back() == '"')
+ {
+ auto const pdatalen = pdata.length() - 1;
+ for (int j = 1; j < pdatalen; j++)
+ {
+ fill_data[data_count] = pdata[j];
+ fill_data_size[data_count++] = 1;
+ }
+ }
+
+ // otherwise, validate as a number
+ else
+ {
+ // check for a 'b','w','d',or 'q' prefix
+ fill_data_size[data_count] = cur_data_size;
+ if (pdata.length() >= 2)
+ {
+ if (tolower(u8(pdata[0])) == 'b' && pdata[1] == '.') { fill_data_size[data_count] = cur_data_size = 1; pdata.remove_prefix(2); }
+ if (tolower(u8(pdata[0])) == 'w' && pdata[1] == '.') { fill_data_size[data_count] = cur_data_size = 2; pdata.remove_prefix(2); }
+ if (tolower(u8(pdata[0])) == 'd' && pdata[1] == '.') { fill_data_size[data_count] = cur_data_size = 4; pdata.remove_prefix(2); }
+ if (tolower(u8(pdata[0])) == 'q' && pdata[1] == '.') { fill_data_size[data_count] = cur_data_size = 8; pdata.remove_prefix(2); }
+ }
+
+ // validate as a number
+ if (!m_console.validate_number_parameter(pdata, fill_data[data_count++]))
+ return;
+ }
+ }
+ if (data_count == 0)
+ return;
+
+ // now fill memory
+ device_memory_interface &memory = space->device().memory();
+ auto dis = space->device().machine().disable_side_effects();
+ u64 count = space->address_to_byte(length);
+ while (count != 0)
+ {
+ // write the entire string
+ for (int j = 0; j < data_count; j++)
+ {
+ offs_t address = space->byte_to_address(offset) & space->logaddrmask();
+ address_space *tspace;
+ if (!memory.translate(space->spacenum(), device_memory_interface::TR_WRITE, address, tspace))
+ {
+ m_console.printf("Fill aborted due to page fault at %0*X\n", space->logaddrchars(), space->byte_to_address(offset) & space->logaddrmask());
+ length = 0;
+ break;
+ }
+ switch (fill_data_size[j])
+ {
+ case 1:
+ tspace->write_byte(address, fill_data[j]);
+ break;
+
+ case 2:
+ tspace->write_word_unaligned(address, fill_data[j]);
+ break;
+
+ case 4:
+ tspace->write_dword_unaligned(address, fill_data[j]);
+ break;
+
+ case 8:
+ tspace->read_qword_unaligned(address, fill_data[j]);
+ break;
+ }
+ offset += fill_data_size[j];
+ if (count <= fill_data_size[j])
+ {
+ count = 0;
+ break;
+ }
+ else
+ count -= fill_data_size[j];
+ }
+ }
+}
+
+
/*-------------------------------------------------
execute_dasm - execute the dasm command
-------------------------------------------------*/
-void debugger_commands::execute_dasm(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_dasm(const std::vector<std::string_view> &params)
{
- u64 offset, length, bytes = 1;
+ u64 offset, length;
+ bool bytes = true;
address_space *space;
- /* validate parameters */
- if (!validate_number_parameter(params[1], offset))
+ // validate parameters
+ if (!m_console.validate_number_parameter(params[1], offset))
return;
- if (!validate_number_parameter(params[2], length))
+ if (!m_console.validate_number_parameter(params[2], length))
return;
- if (params.size() > 3 && !validate_number_parameter(params[3], bytes))
+ if (params.size() > 3 && !m_console.validate_boolean_parameter(params[3], bytes))
return;
- if (!validate_cpu_space_parameter(params.size() > 4 ? params[4].c_str() : nullptr, AS_PROGRAM, space))
+ if (!m_console.validate_device_space_parameter(params.size() > 4 ? params[4] : std::string_view(), AS_PROGRAM, space))
return;
- /* determine the width of the bytes */
+ // determine the width of the bytes
device_disasm_interface *dasmintf;
if (!space->device().interface(dasmintf))
{
@@ -2743,7 +3400,7 @@ void debugger_commands::execute_dasm(int ref, const std::vector<std::string> &pa
return;
}
- /* build the data, check the maximum size of the opcodes and disasm */
+ // build the data, check the maximum size of the opcodes and disasm
std::vector<offs_t> pcs;
std::vector<std::string> instructions;
std::vector<std::string> tpc;
@@ -2766,11 +3423,11 @@ void debugger_commands::execute_dasm(int ref, const std::vector<std::string> &pa
topcodes.emplace_back(buffer.data_to_string(offset, size, true));
int osize = topcodes.back().size();
- if(osize > max_opcodes_size)
+ if (osize > max_opcodes_size)
max_opcodes_size = osize;
int dsize = instructions.back().size();
- if(dsize > max_disasm_size)
+ if (dsize > max_disasm_size)
max_disasm_size = dsize;
i += size;
@@ -2778,7 +3435,8 @@ void debugger_commands::execute_dasm(int ref, const std::vector<std::string> &pa
}
/* write the data */
- std::ofstream f(params[0]);
+ std::string fname(params[0]);
+ std::ofstream f(fname);
if (!f.good())
{
m_console.printf("Error opening file '%s'\n", params[0]);
@@ -2787,7 +3445,7 @@ void debugger_commands::execute_dasm(int ref, const std::vector<std::string> &pa
if (bytes)
{
- for(unsigned int i=0; i != pcs.size(); i++)
+ for (unsigned int i=0; i != pcs.size(); i++)
{
const char *comment = space->device().debug()->comment_text(pcs[i]);
if (comment)
@@ -2798,7 +3456,7 @@ void debugger_commands::execute_dasm(int ref, const std::vector<std::string> &pa
}
else
{
- for(unsigned int i=0; i != pcs.size(); i++)
+ for (unsigned int i=0; i != pcs.size(); i++)
{
const char *comment = space->device().debug()->comment_text(pcs[i]);
if (comment)
@@ -2813,104 +3471,88 @@ void debugger_commands::execute_dasm(int ref, const std::vector<std::string> &pa
/*-------------------------------------------------
- execute_trace_internal - functionality for
+ execute_trace - functionality for
trace over and trace info
-------------------------------------------------*/
-void debugger_commands::execute_trace_internal(int ref, const std::vector<std::string> &params, bool trace_over)
+void debugger_commands::execute_trace(const std::vector<std::string_view> &params, bool trace_over)
{
- const char *action = nullptr;
+ std::string_view action;
bool detect_loops = true;
bool logerror = false;
- device_t *cpu;
- FILE *f = nullptr;
- const char *mode;
- std::string filename = params[0];
+ std::string filename(params[0]);
- /* replace macros */
+ // replace macros
strreplace(filename, "{game}", m_machine.basename());
- /* validate parameters */
- if (!validate_cpu_parameter(params.size() > 1 ? params[1].c_str() : nullptr, cpu))
+ // validate parameters
+ device_t *cpu;
+ if (!m_console.validate_cpu_parameter(params.size() > 1 ? params[1] : std::string_view(), cpu))
return;
if (params.size() > 2)
{
std::stringstream stream;
- stream.str(params[2]);
+ stream.str(std::string(params[2]));
std::string flag;
while (std::getline(stream, flag, '|'))
{
- if (!core_stricmp(flag.c_str(), "noloop"))
+ using namespace std::literals;
+ if (util::streqlower(flag, "noloop"sv))
detect_loops = false;
- else if (!core_stricmp(flag.c_str(), "logerror"))
+ else if (util::streqlower(flag, "logerror"sv))
logerror = true;
else
{
- m_console.printf("Invalid flag '%s'\n", flag.c_str());
+ m_console.printf("Invalid flag '%s'\n", flag);
return;
}
}
}
- if (!debug_command_parameter_command(action = (params.size() > 3) ? params[3].c_str() : nullptr))
+ if (params.size() > 3 && !m_console.validate_command_parameter(action = params[3]))
return;
- /* open the file */
- if (core_stricmp(filename.c_str(), "off") != 0)
+ // open the file
+ std::unique_ptr<std::ofstream> f;
+ using namespace std::literals;
+ if (!util::streqlower(filename, "off"sv))
{
- mode = "w";
+ std::ios_base::openmode mode;
- /* opening for append? */
+ // opening for append?
if ((filename[0] == '>') && (filename[1] == '>'))
{
- mode = "a";
+ mode = std::ios_base::in | std::ios_base::out | std::ios_base::ate;
filename = filename.substr(2);
}
+ else
+ mode = std::ios_base::out | std::ios_base::trunc;
- f = fopen(filename.c_str(), mode);
- if (!f)
+ f = std::make_unique<std::ofstream>(filename.c_str(), mode);
+ if (f->fail())
{
- m_console.printf("Error opening file '%s'\n", params[0].c_str());
+ m_console.printf("Error opening file '%s'\n", params[0]);
return;
}
}
- /* do it */
- cpu->debug()->trace(f, trace_over, detect_loops, logerror, action);
- if (f)
- m_console.printf("Tracing CPU '%s' to file %s\n", cpu->tag(), filename.c_str());
+ // do it
+ bool const on(f);
+ cpu->debug()->trace(std::move(f), trace_over, detect_loops, logerror, action);
+ if (on)
+ m_console.printf("Tracing CPU '%s' to file %s\n", cpu->tag(), filename);
else
m_console.printf("Stopped tracing on CPU '%s'\n", cpu->tag());
}
/*-------------------------------------------------
- execute_trace - execute the trace command
--------------------------------------------------*/
-
-void debugger_commands::execute_trace(int ref, const std::vector<std::string> &params)
-{
- execute_trace_internal(ref, params, false);
-}
-
-
-/*-------------------------------------------------
- execute_traceover - execute the trace over command
--------------------------------------------------*/
-
-void debugger_commands::execute_traceover(int ref, const std::vector<std::string> &params)
-{
- execute_trace_internal(ref, params, true);
-}
-
-
-/*-------------------------------------------------
execute_traceflush - execute the trace flush command
-------------------------------------------------*/
-void debugger_commands::execute_traceflush(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_traceflush(const std::vector<std::string_view> &params)
{
- m_cpu.flush_traces();
+ m_machine.debugger().cpu().flush_traces();
}
@@ -2918,43 +3560,46 @@ void debugger_commands::execute_traceflush(int ref, const std::vector<std::strin
execute_history - execute the history command
-------------------------------------------------*/
-void debugger_commands::execute_history(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_history(const std::vector<std::string_view> &params)
{
- /* validate parameters */
- address_space *space;
- if (!validate_cpu_space_parameter(!params.empty() ? params[0].c_str() : nullptr, AS_PROGRAM, space))
+ // validate parameters
+ device_t *device;
+ if (!m_console.validate_cpu_parameter(!params.empty() ? params[0] : std::string_view(), device))
return;
u64 count = device_debug::HISTORY_SIZE;
- if (params.size() > 1 && !validate_number_parameter(params[1], count))
+ if (params.size() > 1 && !m_console.validate_number_parameter(params[1], count))
return;
- /* further validation */
+ // further validation
if (count > device_debug::HISTORY_SIZE)
count = device_debug::HISTORY_SIZE;
- device_debug *debug = space->device().debug();
+ device_debug *const debug = device->debug();
- /* loop over lines */
device_disasm_interface *dasmintf;
- if (!space->device().interface(dasmintf))
+ if (!device->interface(dasmintf))
{
- m_console.printf("No disassembler available for %s\n", space->device().name());
+ m_console.printf("No disassembler available for device %s\n", device->name());
return;
}
- debug_disasm_buffer buffer(space->device());
-
- for (int index = 0; index < (int) count; index++)
+ // loop over lines
+ std::string instruction;
+ for (int index = int(unsigned(count)); index > 0; index--)
{
- offs_t pc = debug->history_pc(-index);
- std::string instruction;
- offs_t next_offset;
- offs_t size;
- u32 info;
- buffer.disassemble(pc, instruction, next_offset, size, info);
-
- m_console.printf("%s: %s\n", buffer.pc_to_string(pc), instruction);
+ auto const pc = debug->history_pc(1 - index);
+ if (pc.second)
+ {
+ debug_disasm_buffer buffer(*device);
+ offs_t next_offset;
+ offs_t size;
+ u32 info;
+ instruction.clear();
+ buffer.disassemble(pc.first, instruction, next_offset, size, info);
+
+ m_console.printf("%s: %s\n", buffer.pc_to_string(pc.first), instruction);
+ }
}
}
@@ -2963,30 +3608,37 @@ void debugger_commands::execute_history(int ref, const std::vector<std::string>
execute_trackpc - execute the trackpc command
-------------------------------------------------*/
-void debugger_commands::execute_trackpc(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_trackpc(const std::vector<std::string_view> &params)
{
// Gather the on/off switch (if present)
bool turnOn = true;
- if (params.size() > 0 && !validate_boolean_parameter(params[0], turnOn))
+ if (params.size() > 0 && !m_console.validate_boolean_parameter(params[0], turnOn))
return;
// Gather the cpu id (if present)
device_t *cpu = nullptr;
- if (!validate_cpu_parameter((params.size() > 1) ? params[1].c_str() : nullptr, cpu))
+ if (!m_console.validate_cpu_parameter((params.size() > 1) ? params[1] : std::string_view(), cpu))
return;
+ const device_state_interface *state;
+ if (!cpu->interface(state))
+ {
+ m_console.printf("Device has no PC to be tracked\n");
+ return;
+ }
+
// Should we clear the existing data?
bool clear = false;
- if (params.size() > 2 && !validate_boolean_parameter(params[2], clear))
+ if (params.size() > 2 && !m_console.validate_boolean_parameter(params[2], clear))
return;
cpu->debug()->set_track_pc((bool)turnOn);
if (turnOn)
{
// Insert current pc
- if (m_cpu.get_visible_cpu() == cpu)
+ if (m_console.get_visible_cpu() == cpu)
{
- const offs_t pc = cpu->state().pcbase();
+ const offs_t pc = state->pcbase();
cpu->debug()->set_track_pc_visited(pc);
}
m_console.printf("PC tracking enabled\n");
@@ -3005,26 +3657,29 @@ void debugger_commands::execute_trackpc(int ref, const std::vector<std::string>
execute_trackmem - execute the trackmem command
-------------------------------------------------*/
-void debugger_commands::execute_trackmem(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_trackmem(const std::vector<std::string_view> &params)
{
// Gather the on/off switch (if present)
bool turnOn = true;
- if (params.size() > 0 && !validate_boolean_parameter(params[0], turnOn))
+ if (params.size() > 0 && !m_console.validate_boolean_parameter(params[0], turnOn))
return;
// Gather the cpu id (if present)
+ std::string_view cpuparam;
+ if (params.size() > 1)
+ cpuparam = params[1];
device_t *cpu = nullptr;
- if (!validate_cpu_parameter((params.size() > 1) ? params[1].c_str() : nullptr, cpu))
+ if (!m_console.validate_cpu_parameter(cpuparam, cpu))
return;
// Should we clear the existing data?
bool clear = false;
- if (params.size() > 2 && !validate_boolean_parameter(params[2], clear))
+ if (params.size() > 2 && !m_console.validate_boolean_parameter(params[2], clear))
return;
// Get the address space for the given cpu
address_space *space;
- if (!validate_cpu_space_parameter((params.size() > 1) ? params[1].c_str() : nullptr, AS_PROGRAM, space))
+ if (!m_console.validate_device_space_parameter(cpuparam, AS_PROGRAM, space))
return;
// Inform the CPU it's time to start tracking memory writes
@@ -3040,30 +3695,47 @@ void debugger_commands::execute_trackmem(int ref, const std::vector<std::string>
execute_pcatmem - execute the pcatmem command
-------------------------------------------------*/
-void debugger_commands::execute_pcatmem(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_pcatmem(int spacenum, const std::vector<std::string_view> &params)
{
- // Gather the required address parameter
+ // Gather the required target address/space parameter
u64 address;
- if (!validate_number_parameter(params[0], address))
- return;
-
- // Gather the cpu id (if present)
- device_t *cpu = nullptr;
- if (!validate_cpu_parameter((params.size() > 1) ? params[1].c_str() : nullptr, cpu))
+ address_space *space;
+ if (!m_console.validate_target_address_parameter(params[0], spacenum, space, address))
return;
- // Get the address space for the given cpu
- address_space *space;
- if (!validate_cpu_space_parameter((params.size() > 1) ? params[1].c_str() : nullptr, ref, space))
+ // Translate the address
+ offs_t a = address & space->logaddrmask();
+ address_space *tspace;
+ if (!space->device().memory().translate(space->spacenum(), device_memory_interface::TR_READ, a, tspace))
+ {
+ m_console.printf("Address translation failed\n");
return;
+ }
// Get the value of memory at the address
- const int native_data_width = space->data_width() / 8;
- const u64 data = m_cpu.read_memory(*space, space->address_to_byte(address), native_data_width, true);
+ u64 data = space->unmap();
+ auto dis = space->device().machine().disable_side_effects();
+ switch (space->data_width())
+ {
+ case 8:
+ data = tspace->read_byte(a);
+ break;
+
+ case 16:
+ data = tspace->read_word_unaligned(a);
+ break;
+
+ case 32:
+ data = tspace->read_dword_unaligned(a);
+ break;
+
+ case 64:
+ data = tspace->read_qword_unaligned(a);
+ break;
+ }
// Recover the pc & print
- const int space_num = (int)ref;
- const offs_t result = space->device().debug()->track_mem_pc_from_space_address_data(space_num, address, data);
+ const offs_t result = space->device().debug()->track_mem_pc_from_space_address_data(space->spacenum(), address, data);
if (result != (offs_t)(-1))
m_console.printf("%02x\n", result);
else
@@ -3075,7 +3747,7 @@ void debugger_commands::execute_pcatmem(int ref, const std::vector<std::string>
execute_snap - execute the snapshot command
-------------------------------------------------*/
-void debugger_commands::execute_snap(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_snap(const std::vector<std::string_view> &params)
{
/* if no params, use the default behavior */
if (params.empty())
@@ -3087,10 +3759,11 @@ void debugger_commands::execute_snap(int ref, const std::vector<std::string> &pa
/* otherwise, we have to open the file ourselves */
else
{
- const char *filename = params[0].c_str();
- int scrnum = (params.size() > 1) ? atoi(params[1].c_str()) : 0;
+ u64 scrnum = 0;
+ if (params.size() > 1 && !m_console.validate_number_parameter(params[1], scrnum))
+ return;
- screen_device_iterator iter(m_machine.root_device());
+ screen_device_enumerator iter(m_machine.root_device());
screen_device *screen = iter.byindex(scrnum);
if ((screen == nullptr) || !m_machine.render().is_live(*screen))
@@ -3099,20 +3772,20 @@ void debugger_commands::execute_snap(int ref, const std::vector<std::string> &pa
return;
}
- std::string fname(filename);
+ std::string fname(params[0]);
if (fname.find(".png") == -1)
fname.append(".png");
emu_file file(m_machine.options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
- osd_file::error filerr = file.open(fname.c_str());
+ std::error_condition filerr = file.open(std::move(fname));
- if (filerr != osd_file::error::NONE)
+ if (filerr)
{
- m_console.printf("Error creating file '%s'\n", filename);
+ m_console.printf("Error creating file '%s' (%s:%d %s)\n", params[0], filerr.category().name(), filerr.value(), filerr.message());
return;
}
screen->machine().video().save_snapshot(screen, file);
- m_console.printf("Saved screen #%d snapshot as '%s'\n", scrnum, filename);
+ m_console.printf("Saved screen #%d snapshot as '%s'\n", scrnum, params[0]);
}
}
@@ -3121,9 +3794,10 @@ void debugger_commands::execute_snap(int ref, const std::vector<std::string> &pa
execute_source - execute the source command
-------------------------------------------------*/
-void debugger_commands::execute_source(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_source(const std::vector<std::string_view> &params)
{
- m_console.source_script(params[0].c_str());
+ std::string filename(params[0]);
+ m_console.source_script(filename.c_str());
}
@@ -3131,34 +3805,29 @@ void debugger_commands::execute_source(int ref, const std::vector<std::string> &
execute_map - execute the map command
-------------------------------------------------*/
-void debugger_commands::execute_map(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_map(int spacenum, const std::vector<std::string_view> &params)
{
- address_space *space;
- offs_t taddress;
+ // validate parameters
u64 address;
- int intention;
-
- /* validate parameters */
- if (!validate_number_parameter(params[0], address))
- return;
-
- /* CPU is implicit */
- if (!validate_cpu_space_parameter(nullptr, ref, space))
+ address_space *space;
+ if (!m_console.validate_target_address_parameter(params[0], spacenum, space, address))
return;
+ address &= space->logaddrmask();
- /* do the translation first */
- for (intention = TRANSLATE_READ_DEBUG; intention <= TRANSLATE_FETCH_DEBUG; intention++)
+ // do the translation first
+ for (int intention = device_memory_interface::TR_READ; intention <= device_memory_interface::TR_FETCH; intention++)
{
static const char *const intnames[] = { "Read", "Write", "Fetch" };
- taddress = address & space->addrmask();
- if (space->device().memory().translate(space->spacenum(), intention, taddress))
+ offs_t taddress = address;
+ address_space *tspace;
+ if (space->device().memory().translate(space->spacenum(), intention, taddress, tspace))
{
- std::string mapname = space->get_handler_string((intention == TRANSLATE_WRITE_DEBUG) ? read_or_write::WRITE : read_or_write::READ, taddress);
+ std::string mapname = tspace->get_handler_string((intention == device_memory_interface::TR_WRITE) ? read_or_write::WRITE : read_or_write::READ, taddress);
m_console.printf(
- "%7s: %0*X logical == %0*X physical -> %s\n",
+ "%7s: %0*X logical %s == %0*X physical %s -> %s\n",
intnames[intention & 3],
- space->logaddrchars(), address,
- space->addrchars(), taddress,
+ space->logaddrchars(), address, space->name(),
+ tspace->addrchars(), taddress, tspace->name(),
mapname);
}
else
@@ -3171,20 +3840,29 @@ void debugger_commands::execute_map(int ref, const std::vector<std::string> &par
execute_memdump - execute the memdump command
-------------------------------------------------*/
-void debugger_commands::execute_memdump(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_memdump(const std::vector<std::string_view> &params)
{
- FILE *file;
- const char *filename;
+ device_t *root = &m_machine.root_device();
+ if ((params.size() >= 2) && !m_console.validate_device_parameter(params[1], root))
+ return;
- filename = params.empty() ? "memdump.log" : params[0].c_str();
+ using namespace std::literals;
+ std::string filename = params.empty() ? "memdump.log"s : std::string(params[0]);
+ FILE *const file = fopen(filename.c_str(), "w");
+ if (!file)
+ {
+ m_console.printf("Error opening file %s\n", filename);
+ return;
+ }
- m_console.printf("Dumping memory to %s\n", filename);
+ m_console.printf("Dumping memory maps to %s\n", filename);
- file = fopen(filename, "w");
- if (file)
+ try
{
- memory_interface_iterator iter(m_machine.root_device());
- for (device_memory_interface &memory : iter) {
+ memory_interface_enumerator iter(*root);
+ std::vector<memory_entry> entries[2];
+ for (device_memory_interface &memory : iter)
+ {
for (int space = 0; space != memory.max_space_count(); space++)
if (memory.has_space(space))
{
@@ -3192,25 +3870,36 @@ void debugger_commands::execute_memdump(int ref, const std::vector<std::string>
bool octal = sp.is_octal();
int nc = octal ? (sp.addr_width() + 2) / 3 : (sp.addr_width() + 3) / 4;
- std::vector<memory_entry> entries[2];
sp.dump_maps(entries[0], entries[1]);
for (int mode = 0; mode < 2; mode ++)
{
- fprintf(file, " device %s space %s %s:\n", memory.device().tag(), sp.name(), mode ? "write" : "read");
+ fprintf(file, " %s '%s' space %s %s:\n", memory.device().type().fullname(), memory.device().tag(), sp.name(), mode ? "write" : "read");
for (memory_entry &entry : entries[mode])
{
if (octal)
- fprintf(file, "%0*o - %0*o", nc, entry.start, nc, entry.end);
+ fprintf(file, "%0*o - %0*o:", nc, entry.start, nc, entry.end);
else
- fprintf(file, "%0*x - %0*x", nc, entry.start, nc, entry.end);
- fprintf(file, ": %s\n", entry.entry->name().c_str());
+ fprintf(file, "%0*x - %0*x:", nc, entry.start, nc, entry.end);
+ for (const auto &c : entry.context)
+ if (c.disabled)
+ fprintf(file, " %s[off]", c.view->name().c_str());
+ else
+ fprintf(file, " %s[%d]", c.view->name().c_str(), c.slot);
+ fprintf(file, " %s\n", entry.entry->name().c_str());
}
fprintf(file, "\n");
}
+ entries[0].clear();
+ entries[1].clear();
}
}
fclose(file);
}
+ catch (...)
+ {
+ fclose(file);
+ throw;
+ }
}
@@ -3218,53 +3907,56 @@ void debugger_commands::execute_memdump(int ref, const std::vector<std::string>
execute_symlist - execute the symlist command
-------------------------------------------------*/
-void debugger_commands::execute_symlist(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_symlist(const std::vector<std::string_view> &params)
{
- device_t *cpu = nullptr;
const char *namelist[1000];
symbol_table *symtable;
- int symnum, count = 0;
+ int count = 0;
if (!params.empty())
{
- /* validate parameters */
- if (!validate_cpu_parameter(params[0].c_str(), cpu))
+ // validate parameters
+ device_t *cpu;
+ if (!m_console.validate_cpu_parameter(params[0], cpu))
return;
symtable = &cpu->debug()->symtable();
m_console.printf("CPU '%s' symbols:\n", cpu->tag());
}
else
{
- symtable = m_cpu.get_global_symtable();
+ symtable = &m_machine.debugger().cpu().global_symtable();
m_console.printf("Global symbols:\n");
}
- /* gather names for all symbols */
+ // gather names for all symbols
for (auto &entry : symtable->entries())
{
- /* only display "register" type symbols */
+ // only display "register" type symbols
if (!entry.second->is_function())
{
namelist[count++] = entry.second->name();
- if (count >= ARRAY_LENGTH(namelist))
+ if (count >= std::size(namelist))
break;
}
}
- /* sort the symbols */
+ // sort the symbols
if (count > 1)
- std::sort(&namelist[0], &namelist[count], [](const char *item1, const char *item2) {
- return strcmp(item1, item2) < 0;
- });
+ {
+ std::sort(
+ &namelist[0],
+ &namelist[count],
+ [] (const char *item1, const char *item2) { return strcmp(item1, item2) < 0; });
+ }
- /* iterate over symbols and print out relevant ones */
- for (symnum = 0; symnum < count; symnum++)
+ // iterate over symbols and print out relevant ones
+ for (int symnum = 0; symnum < count; symnum++)
{
- const symbol_entry *entry = symtable->find(namelist[symnum]);
+ symbol_entry const *const entry = symtable->find(namelist[symnum]);
assert(entry != nullptr);
u64 value = entry->value();
- /* only display "register" type symbols */
+ // only display "register" type symbols
m_console.printf("%s = %X", namelist[symnum], value);
if (!entry->is_lval())
m_console.printf(" (read-only)");
@@ -3277,7 +3969,7 @@ void debugger_commands::execute_symlist(int ref, const std::vector<std::string>
execute_softreset - execute the softreset command
-------------------------------------------------*/
-void debugger_commands::execute_softreset(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_softreset(const std::vector<std::string_view> &params)
{
m_machine.schedule_soft_reset();
}
@@ -3287,7 +3979,7 @@ void debugger_commands::execute_softreset(int ref, const std::vector<std::string
execute_hardreset - execute the hardreset command
-------------------------------------------------*/
-void debugger_commands::execute_hardreset(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_hardreset(const std::vector<std::string_view> &params)
{
m_machine.schedule_hard_reset();
}
@@ -3297,57 +3989,84 @@ void debugger_commands::execute_hardreset(int ref, const std::vector<std::string
mounted files
-------------------------------------------------*/
-void debugger_commands::execute_images(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_images(const std::vector<std::string_view> &params)
{
- image_interface_iterator iter(m_machine.root_device());
+ image_interface_enumerator iter(m_machine.root_device());
for (device_image_interface &img : iter)
- m_console.printf("%s: %s\n", img.brief_instance_name(), img.exists() ? img.filename() : "[empty slot]");
- if (iter.first() == nullptr)
- m_console.printf("No image devices in this driver\n");
+ {
+ if (!img.exists())
+ {
+ m_console.printf("%s: [no media]\n", img.brief_instance_name());
+ }
+ else if (img.loaded_through_softlist())
+ {
+ m_console.printf("%s: %s:%s:%s\n",
+ img.brief_instance_name(),
+ img.software_list_name(),
+ img.software_entry()->shortname(),
+ img.part_entry()->name());
+ }
+ else
+ {
+ m_console.printf("%s: %s\n", img.brief_instance_name(), img.filename());
+ }
+ }
+ if (!iter.first())
+ m_console.printf("No image devices present\n");
}
/*-------------------------------------------------
execute_mount - execute the image mount command
-------------------------------------------------*/
-void debugger_commands::execute_mount(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_mount(const std::vector<std::string_view> &params)
{
- bool done = false;
- for (device_image_interface &img : image_interface_iterator(m_machine.root_device()))
+ for (device_image_interface &img : image_interface_enumerator(m_machine.root_device()))
{
- if (img.brief_instance_name() == params[0])
+ if ((img.instance_name() == params[0]) || (img.brief_instance_name() == params[0]))
{
- if (img.load(params[1]) != image_init_result::PASS)
- m_console.printf("Unable to mount file %s on %s\n", params[1], params[0]);
- else
+ auto [err, msg] = img.load(params[1]);
+ if (!err)
+ {
m_console.printf("File %s mounted on %s\n", params[1], params[0]);
- done = true;
- break;
+ }
+ else
+ {
+ m_console.printf(
+ "Unable to mount file %s on %s: %s\n",
+ params[1],
+ params[0],
+ !msg.empty() ? msg : err.message());
+ }
+ return;
}
}
- if (!done)
- m_console.printf("There is no image device :%s\n", params[0].c_str());
+ m_console.printf("No image instance %s\n", params[0]);
}
/*-------------------------------------------------
execute_unmount - execute the image unmount command
-------------------------------------------------*/
-void debugger_commands::execute_unmount(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_unmount(const std::vector<std::string_view> &params)
{
- bool done = false;
- for (device_image_interface &img : image_interface_iterator(m_machine.root_device()))
+ for (device_image_interface &img : image_interface_enumerator(m_machine.root_device()))
{
- if (img.brief_instance_name() == params[0])
+ if ((img.instance_name() == params[0]) || (img.brief_instance_name() == params[0]))
{
- img.unload();
- m_console.printf("Unmounted file from : %s\n", params[0]);
- done = true;
- break;
+ if (img.exists())
+ {
+ img.unload();
+ m_console.printf("Unmounted media from %s\n", params[0]);
+ }
+ else
+ {
+ m_console.printf("No media mounted on %s\n", params[0]);
+ }
+ return;
}
}
- if (!done)
- m_console.printf("There is no image device :%s\n", params[0]);
+ m_console.printf("No image instance %s\n", params[0]);
}
@@ -3356,9 +4075,9 @@ void debugger_commands::execute_unmount(int ref, const std::vector<std::string>
natural keyboard input
-------------------------------------------------*/
-void debugger_commands::execute_input(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_input(const std::vector<std::string_view> &params)
{
- m_machine.ioport().natkeyboard().post_coded(params[0].c_str());
+ m_machine.natkeyboard().post_coded(params[0]);
}
@@ -3367,15 +4086,15 @@ void debugger_commands::execute_input(int ref, const std::vector<std::string> &p
keyboard codes
-------------------------------------------------*/
-void debugger_commands::execute_dumpkbd(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_dumpkbd(const std::vector<std::string_view> &params)
{
// was there a file specified?
- const char *filename = !params.empty() ? params[0].c_str() : nullptr;
+ std::string filename = !params.empty() ? std::string(params[0]) : std::string();
FILE *file = nullptr;
- if (filename != nullptr)
+ if (!filename.empty())
{
// if so, open it
- file = fopen(filename, "w");
+ file = fopen(filename.c_str(), "w");
if (file == nullptr)
{
m_console.printf("Cannot open \"%s\"\n", filename);
@@ -3384,13 +4103,13 @@ void debugger_commands::execute_dumpkbd(int ref, const std::vector<std::string>
}
// loop through all codes
- std::string buffer = m_machine.ioport().natkeyboard().dump();
+ std::string buffer = m_machine.natkeyboard().dump();
// and output it as appropriate
if (file != nullptr)
fprintf(file, "%s\n", buffer.c_str());
else
- m_console.printf("%s\n", buffer.c_str());
+ m_console.printf("%s\n", buffer);
// cleanup
if (file != nullptr)
diff --git a/src/emu/debug/debugcmd.h b/src/emu/debug/debugcmd.h
index 862ad93a45a..cf38c6d903a 100644
--- a/src/emu/debug/debugcmd.h
+++ b/src/emu/debug/debugcmd.h
@@ -13,26 +13,13 @@
#pragma once
-#include "debugcpu.h"
-#include "debugcon.h"
+#include <string_view>
class debugger_commands
{
public:
- debugger_commands(running_machine& machine, debugger_cpu& cpu, debugger_console& console);
-
- /* validates a parameter as a boolean value */
- bool validate_boolean_parameter(const std::string &param, bool &result);
-
- /* validates a parameter as a numeric value */
- bool validate_number_parameter(const std::string &param, u64 &result);
-
- /* validates a parameter as a cpu */
- bool validate_cpu_parameter(const char *param, device_t *&result);
-
- /* validates a parameter as a cpu and retrieves the given address space */
- bool validate_cpu_space_parameter(const char *param, int spacenum, address_space *&result);
+ debugger_commands(running_machine &machine, debugger_cpu &cpu, debugger_console &console);
private:
struct global_entry
@@ -56,117 +43,122 @@ private:
// TODO [RH 31 May 2016]: Move this cheat stuff into its own class
struct cheat_system
{
- char cpu[2];
+ address_space *space;
u8 width;
- std::vector<cheat_map> cheatmap;
- u8 undo;
u8 signed_cheat;
u8 swapped_cheat;
- };
+ std::vector<cheat_map> cheatmap;
+ u8 undo;
+ u64 sign_extend(u64 value) const;
+ u64 byte_swap(u64 value) const;
+ u64 read_extended(offs_t address) const;
+ };
struct cheat_region_map
{
- u64 offset;
- u64 endoffset;
- const char *share;
- u8 disabled;
+ u64 offset = 0U;
+ u64 endoffset = 0U;
+ const char *share = nullptr;
+ u8 disabled = 0U;
};
- bool debug_command_parameter_expression(const std::string &param, parsed_expression &result);
- bool debug_command_parameter_command(const char *param);
-
bool cheat_address_is_valid(address_space &space, offs_t address);
- u64 cheat_sign_extend(const cheat_system *cheatsys, u64 value);
- u64 cheat_byte_swap(const cheat_system *cheatsys, u64 value);
- u64 cheat_read_extended(const cheat_system *cheatsys, address_space &space, offs_t address);
-
- u64 execute_min(symbol_table &table, int params, const u64 *param);
- u64 execute_max(symbol_table &table, int params, const u64 *param);
- u64 execute_if(symbol_table &table, int params, const u64 *param);
-
- u64 global_get(symbol_table &table, global_entry *global);
- void global_set(symbol_table &table, global_entry *global, u64 value);
-
- int mini_printf(char *buffer, const char *format, int params, u64 *param);
-
- void execute_trace_internal(int ref, const std::vector<std::string> &params, bool trace_over);
-
- void execute_help(int ref, const std::vector<std::string> &params);
- void execute_print(int ref, const std::vector<std::string> &params);
- void execute_printf(int ref, const std::vector<std::string> &params);
- void execute_logerror(int ref, const std::vector<std::string> &params);
- void execute_tracelog(int ref, const std::vector<std::string> &params);
- void execute_tracesym(int ref, const std::vector<std::string> &params);
- void execute_quit(int ref, const std::vector<std::string> &params);
- void execute_do(int ref, const std::vector<std::string> &params);
- void execute_step(int ref, const std::vector<std::string> &params);
- void execute_over(int ref, const std::vector<std::string> &params);
- void execute_out(int ref, const std::vector<std::string> &params);
- void execute_go(int ref, const std::vector<std::string> &params);
- void execute_go_vblank(int ref, const std::vector<std::string> &params);
- void execute_go_interrupt(int ref, const std::vector<std::string> &params);
- void execute_go_exception(int ref, const std::vector<std::string> &params);
- void execute_go_time(int ref, const std::vector<std::string> &params);
- void execute_go_privilege(int ref, const std::vector<std::string> &params);
- void execute_focus(int ref, const std::vector<std::string> &params);
- void execute_ignore(int ref, const std::vector<std::string> &params);
- void execute_observe(int ref, const std::vector<std::string> &params);
- void execute_suspend(int ref, const std::vector<std::string> &params);
- void execute_resume(int ref, const std::vector<std::string> &params);
- void execute_next(int ref, const std::vector<std::string> &params);
- void execute_comment_add(int ref, const std::vector<std::string> &params);
- void execute_comment_del(int ref, const std::vector<std::string> &params);
- void execute_comment_save(int ref, const std::vector<std::string> &params);
- void execute_comment_list(int ref, const std::vector<std::string> &params);
- void execute_comment_commit(int ref, const std::vector<std::string> &params);
- void execute_bpset(int ref, const std::vector<std::string> &params);
- void execute_bpclear(int ref, const std::vector<std::string> &params);
- void execute_bpdisenable(int ref, const std::vector<std::string> &params);
- void execute_bplist(int ref, const std::vector<std::string> &params);
- void execute_wpset(int ref, const std::vector<std::string> &params);
- void execute_wpclear(int ref, const std::vector<std::string> &params);
- void execute_wpdisenable(int ref, const std::vector<std::string> &params);
- void execute_wplist(int ref, const std::vector<std::string> &params);
- void execute_rpset(int ref, const std::vector<std::string> &params);
- void execute_rpclear(int ref, const std::vector<std::string> &params);
- void execute_rpdisenable(int ref, const std::vector<std::string> &params);
- void execute_rplist(int ref, const std::vector<std::string> &params);
- void execute_hotspot(int ref, const std::vector<std::string> &params);
- void execute_statesave(int ref, const std::vector<std::string> &params);
- void execute_stateload(int ref, const std::vector<std::string> &params);
- void execute_rewind(int ref, const std::vector<std::string> &params);
- void execute_save(int ref, const std::vector<std::string> &params);
- void execute_load(int ref, const std::vector<std::string> &params);
- void execute_dump(int ref, const std::vector<std::string> &params);
- void execute_cheatinit(int ref, const std::vector<std::string> &params);
- void execute_cheatnext(int ref, const std::vector<std::string> &params);
- void execute_cheatlist(int ref, const std::vector<std::string> &params);
- void execute_cheatundo(int ref, const std::vector<std::string> &params);
- void execute_dasm(int ref, const std::vector<std::string> &params);
- void execute_find(int ref, const std::vector<std::string> &params);
- void execute_trace(int ref, const std::vector<std::string> &params);
- void execute_traceover(int ref, const std::vector<std::string> &params);
- void execute_traceflush(int ref, const std::vector<std::string> &params);
- void execute_history(int ref, const std::vector<std::string> &params);
- void execute_trackpc(int ref, const std::vector<std::string> &params);
- void execute_trackmem(int ref, const std::vector<std::string> &params);
- void execute_pcatmem(int ref, const std::vector<std::string> &params);
- void execute_snap(int ref, const std::vector<std::string> &params);
- void execute_source(int ref, const std::vector<std::string> &params);
- void execute_map(int ref, const std::vector<std::string> &params);
- void execute_memdump(int ref, const std::vector<std::string> &params);
- void execute_symlist(int ref, const std::vector<std::string> &params);
- void execute_softreset(int ref, const std::vector<std::string> &params);
- void execute_hardreset(int ref, const std::vector<std::string> &params);
- void execute_images(int ref, const std::vector<std::string> &params);
- void execute_mount(int ref, const std::vector<std::string> &params);
- void execute_unmount(int ref, const std::vector<std::string> &params);
- void execute_input(int ref, const std::vector<std::string> &params);
- void execute_dumpkbd(int ref, const std::vector<std::string> &params);
+
+ u64 get_cpunum();
+
+ u64 global_get(global_entry *global);
+ void global_set(global_entry *global, u64 value);
+
+ bool mini_printf(std::ostream &stream, const std::vector<std::string_view> &params);
+ template <typename T>
+ void execute_index_command(std::vector<std::string_view> const &params, T &&apply, char const *unused_message);
+
+ void execute_help(const std::vector<std::string_view> &params);
+ void execute_print(const std::vector<std::string_view> &params);
+ void execute_printf(const std::vector<std::string_view> &params);
+ void execute_logerror(const std::vector<std::string_view> &params);
+ void execute_tracelog(const std::vector<std::string_view> &params);
+ void execute_tracesym(const std::vector<std::string_view> &params);
+ void execute_cls(const std::vector<std::string_view> &params);
+ void execute_quit(const std::vector<std::string_view> &params);
+ void execute_do(const std::vector<std::string_view> &params);
+ void execute_step(const std::vector<std::string_view> &params);
+ void execute_over(const std::vector<std::string_view> &params);
+ void execute_out(const std::vector<std::string_view> &params);
+ void execute_go(const std::vector<std::string_view> &params);
+ void execute_go_vblank(const std::vector<std::string_view> &params);
+ void execute_go_interrupt(const std::vector<std::string_view> &params);
+ void execute_go_exception(const std::vector<std::string_view> &params);
+ void execute_go_time(const std::vector<std::string_view> &params);
+ void execute_go_privilege(const std::vector<std::string_view> &params);
+ void execute_go_branch(bool sense, const std::vector<std::string_view> &params);
+ void execute_go_next_instruction(const std::vector<std::string_view> &params);
+ void execute_focus(const std::vector<std::string_view> &params);
+ void execute_ignore(const std::vector<std::string_view> &params);
+ void execute_observe(const std::vector<std::string_view> &params);
+ void execute_suspend(const std::vector<std::string_view> &params);
+ void execute_resume(const std::vector<std::string_view> &params);
+ void execute_next(const std::vector<std::string_view> &params);
+ void execute_cpulist(const std::vector<std::string_view> &params);
+ void execute_time(const std::vector<std::string_view> &params);
+ void execute_comment_add(const std::vector<std::string_view> &params);
+ void execute_comment_del(const std::vector<std::string_view> &params);
+ void execute_comment_save(const std::vector<std::string_view> &params);
+ void execute_comment_list(const std::vector<std::string_view> &params);
+ void execute_comment_commit(const std::vector<std::string_view> &params);
+ void execute_bpset(const std::vector<std::string_view> &params);
+ void execute_bpclear(const std::vector<std::string_view> &params);
+ void execute_bpdisenable(bool enable, const std::vector<std::string_view> &params);
+ void execute_bplist(const std::vector<std::string_view> &params);
+ void execute_wpset(int spacenum, const std::vector<std::string_view> &params);
+ void execute_wpclear(const std::vector<std::string_view> &params);
+ void execute_wpdisenable(bool enable, const std::vector<std::string_view> &params);
+ void execute_wplist(const std::vector<std::string_view> &params);
+ void execute_rpset(const std::vector<std::string_view> &params);
+ void execute_rpclear(const std::vector<std::string_view> &params);
+ void execute_rpdisenable(bool enable, const std::vector<std::string_view> &params);
+ void execute_rplist(const std::vector<std::string_view> &params);
+ void execute_epset(const std::vector<std::string_view> &params);
+ void execute_epclear(const std::vector<std::string_view> &params);
+ void execute_epdisenable(bool enable, const std::vector<std::string_view> &params);
+ void execute_eplist(const std::vector<std::string_view> &params);
+ void execute_statesave(const std::vector<std::string_view> &params);
+ void execute_stateload(const std::vector<std::string_view> &params);
+ void execute_rewind(const std::vector<std::string_view> &params);
+ void execute_save(int spacenum, const std::vector<std::string_view> &params);
+ void execute_saveregion(const std::vector<std::string_view> &params);
+ void execute_load(int spacenum, const std::vector<std::string_view> &params);
+ void execute_loadregion(const std::vector<std::string_view> &params);
+ void execute_dump(int spacenum, const std::vector<std::string_view> &params);
+ void execute_strdump(int spacenum, const std::vector<std::string_view> &params);
+ void execute_cheatrange(bool init, const std::vector<std::string_view> &params);
+ void execute_cheatnext(bool initial, const std::vector<std::string_view> &params);
+ void execute_cheatlist(const std::vector<std::string_view> &params);
+ void execute_cheatundo(const std::vector<std::string_view> &params);
+ void execute_dasm(const std::vector<std::string_view> &params);
+ void execute_find(int spacenum, const std::vector<std::string_view> &params);
+ void execute_fill(int spacenum, const std::vector<std::string_view> &params);
+ void execute_trace(const std::vector<std::string_view> &params, bool trace_over);
+ void execute_traceflush(const std::vector<std::string_view> &params);
+ void execute_history(const std::vector<std::string_view> &params);
+ void execute_trackpc(const std::vector<std::string_view> &params);
+ void execute_trackmem(const std::vector<std::string_view> &params);
+ void execute_pcatmem(int spacenum, const std::vector<std::string_view> &params);
+ void execute_snap(const std::vector<std::string_view> &params);
+ void execute_source(const std::vector<std::string_view> &params);
+ void execute_map(int spacenum, const std::vector<std::string_view> &params);
+ void execute_memdump(const std::vector<std::string_view> &params);
+ void execute_symlist(const std::vector<std::string_view> &params);
+ void execute_softreset(const std::vector<std::string_view> &params);
+ void execute_hardreset(const std::vector<std::string_view> &params);
+ void execute_images(const std::vector<std::string_view> &params);
+ void execute_mount(const std::vector<std::string_view> &params);
+ void execute_unmount(const std::vector<std::string_view> &params);
+ void execute_input(const std::vector<std::string_view> &params);
+ void execute_dumpkbd(const std::vector<std::string_view> &params);
running_machine& m_machine;
- debugger_cpu& m_cpu;
debugger_console& m_console;
std::unique_ptr<global_entry []> m_global_array;
diff --git a/src/emu/debug/debugcon.cpp b/src/emu/debug/debugcon.cpp
index 8faf4f0de02..ec5bc154751 100644
--- a/src/emu/debug/debugcon.cpp
+++ b/src/emu/debug/debugcon.cpp
@@ -1,8 +1,8 @@
// license:BSD-3-Clause
-// copyright-holders:Aaron Giles
+// copyright-holders:Aaron Giles, Vas Crabb
/*********************************************************************
- debugcon.c
+ debugcon.cpp
Debugger console engine.
@@ -10,12 +10,21 @@
#include "emu.h"
#include "debugcon.h"
+
#include "debugcpu.h"
#include "debugvw.h"
#include "textbuf.h"
+
#include "debugger.h"
-#include <ctype.h>
+#include "fileio.h"
+#include "main.h"
+
+#include "corestr.h"
+
+#include <cctype>
#include <fstream>
+#include <iterator>
+
/***************************************************************************
CONSTANTS
@@ -35,9 +44,10 @@
debugger_console::debugger_console(running_machine &machine)
: m_machine(machine)
+ , m_visiblecpu(nullptr)
, m_console_textbuf(nullptr)
, m_errorlog_textbuf(nullptr)
- , m_commandlist(nullptr)
+ , m_logfile(nullptr)
{
/* allocate text buffers */
m_console_textbuf = text_buffer_alloc(CONSOLE_BUF_SIZE, CONSOLE_MAX_LINES);
@@ -48,6 +58,9 @@ debugger_console::debugger_console(running_machine &machine)
if (!m_errorlog_textbuf)
return;
+ /* due to initialization order, @machine is holding our debug.log handle */
+ m_logfile = machine.steal_debuglogfile();
+
/* print the opening lines */
printf("%s debugger version %s\n", emulator_info::get_appname(), emulator_info::get_build_version());
printf("Currently targeting %s (%s)\n", m_machine.system().name, m_machine.system().type.fullname());
@@ -60,31 +73,41 @@ debugger_console::debugger_console(running_machine &machine)
m_machine.add_logerror_callback(std::bind(&debugger_console::errorlog_write_line, this, _1));
/* register our own custom-command help */
- register_command("helpcustom", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_console::execute_help_custom, this, _1, _2));
+ register_command("helpcustom", CMDFLAG_NONE, 0, 0, std::bind(&debugger_console::execute_help_custom, this, _1));
+ register_command("condump", CMDFLAG_NONE, 1, 1, std::bind(&debugger_console::execute_condump, this, _1));
+
+ /* first CPU is visible by default */
+ for (device_t &device : device_enumerator(m_machine.root_device()))
+ {
+ auto *cpu = dynamic_cast<cpu_device *>(&device);
+ if (cpu)
+ {
+ m_visiblecpu = cpu;
+ break;
+ }
+ }
}
+debugger_console::~debugger_console()
+{
+}
-/*-------------------------------------------------
- exit - frees the console system
--------------------------------------------------*/
+
+//-------------------------------------------------
+// exit - frees the console system
+//-------------------------------------------------
void debugger_console::exit()
{
- /* free allocated memory */
- if (m_console_textbuf)
- {
- text_buffer_free(m_console_textbuf);
- }
- m_console_textbuf = nullptr;
+ // free allocated memory
+ m_console_textbuf.reset();
+ m_errorlog_textbuf.reset();
- if (m_errorlog_textbuf)
- {
- text_buffer_free(m_errorlog_textbuf);
- }
- m_errorlog_textbuf = nullptr;
+ // free the command list
+ m_commandlist.clear();
- /* free the command list */
- m_commandlist = nullptr;
+ // close the logfile, if any
+ m_logfile.reset();
}
@@ -95,192 +118,230 @@ void debugger_console::exit()
***************************************************************************/
-/*------------------------------------------------------------
- execute_help_custom - execute the helpcustom command
-------------------------------------------------------------*/
+inline bool debugger_console::debug_command::compare::operator()(const debug_command &a, const debug_command &b) const
+{
+ return a.command < b.command;
+}
+
+inline bool debugger_console::debug_command::compare::operator()(const char *a, const debug_command &b) const
+{
+ return strcmp(a, b.command.c_str()) < 0;
+}
-void debugger_console::execute_help_custom(int ref, const std::vector<std::string> &params)
+inline bool debugger_console::debug_command::compare::operator()(const debug_command &a, const char *b) const
{
- debug_command *cmd = m_commandlist;
- char buf[64];
- while (cmd)
+ return strcmp(a.command.c_str(), b) < 0;
+}
+
+
+debugger_console::debug_command::debug_command(std::string_view _command, u32 _flags, int _minparams, int _maxparams, std::function<void (const std::vector<std::string_view> &)> &&_handler)
+ : command(_command), params(nullptr), help(nullptr), handler(std::move(_handler)), flags(_flags), minparams(_minparams), maxparams(_maxparams)
+{
+}
+
+
+//------------------------------------------------------------
+// execute_help_custom - execute the helpcustom command
+//------------------------------------------------------------
+
+void debugger_console::execute_help_custom(const std::vector<std::string_view> &params)
+{
+ for (const debug_command &cmd : m_commandlist)
{
- if (cmd->flags & CMDFLAG_CUSTOM_HELP)
+ if (cmd.flags & CMDFLAG_CUSTOM_HELP)
{
- snprintf(buf, 63, "%s help", cmd->command);
- buf[63] = 0;
- char *temp_params[1] = { buf };
- internal_execute_command(true, 1, &temp_params[0]);
+ std::string buf = cmd.command + " help";
+ std::vector<std::string_view> temp_params = { buf };
+ internal_execute_command(true, temp_params);
}
- cmd = cmd->next;
}
}
+/*------------------------------------------------------------
+ execute_condump - execute the condump command
+------------------------------------------------------------*/
-/*-------------------------------------------------
- trim_parameter - executes a
- command
--------------------------------------------------*/
+void debugger_console::execute_condump(const std::vector<std::string_view>& params)
+{
+ std::string filename(params[0]);
+ const char* mode;
+
+ /* replace macros */
+ strreplace(filename, "{game}", m_machine.basename());
+
+ mode = "w";
+ /* opening for append? */
+ if (filename.length() >= 2 && filename[0] == '>' && filename[1] == '>')
+ {
+ mode = "a";
+ filename = filename.substr(2);
+ }
+
+ FILE* f = fopen(filename.c_str(), mode);
+ if (!f)
+ {
+ printf("Error opening file '%s'\n", filename);
+ return;
+ }
+
+ for (std::string_view line_info : text_buffer_lines(*m_console_textbuf))
+ {
+ fwrite(line_info.data(), sizeof(char), line_info.length(), f);
+ fputc('\n', f);
+ }
-void debugger_console::trim_parameter(char **paramptr, bool keep_quotes)
+ fclose(f);
+ printf("Wrote console contents to '%s'\n", filename);
+}
+
+//-------------------------------------------------
+// visible_symtable - return the locally-visible
+// symbol table
+//-------------------------------------------------
+
+symbol_table &debugger_console::visible_symtable() const
+{
+ return m_visiblecpu->debug()->symtable();
+}
+
+
+
+//-------------------------------------------------
+// trim_parameter - trim spaces and quotes around
+// a command parameter
+//-------------------------------------------------
+
+std::string_view debugger_console::trim_parameter(std::string_view param, bool keep_quotes)
{
- char *param = *paramptr;
- size_t len = strlen(param);
+ std::string_view::size_type len = param.length();
bool repeat;
- /* loop until all adornments are gone */
+ // loop until all adornments are gone
do
{
repeat = false;
- /* check for begin/end quotes */
+ // check for begin/end quotes
if (len >= 2 && param[0] == '"' && param[len - 1] == '"')
{
if (!keep_quotes)
{
- param[len - 1] = 0;
- param++;
+ param = param.substr(1, len - 2);
len -= 2;
}
}
- /* check for start/end braces */
+ // check for start/end braces
else if (len >= 2 && param[0] == '{' && param[len - 1] == '}')
{
- param[len - 1] = 0;
- param++;
+ param = param.substr(1, len - 2);
len -= 2;
repeat = true;
}
- /* check for leading spaces */
+ // check for leading spaces
else if (len >= 1 && param[0] == ' ')
{
- param++;
+ param.remove_prefix(1);
len--;
repeat = true;
}
- /* check for trailing spaces */
+ // check for trailing spaces
else if (len >= 1 && param[len - 1] == ' ')
{
- param[len - 1] = 0;
+ param.remove_suffix(1);
len--;
repeat = true;
}
} while (repeat);
- *paramptr = param;
+ return param;
}
-/*-------------------------------------------------
- internal_execute_command - executes a
- command
--------------------------------------------------*/
+//-------------------------------------------------
+// internal_execute_command - executes a
+// command
+//-------------------------------------------------
-CMDERR debugger_console::internal_execute_command(bool execute, int params, char **param)
+CMDERR debugger_console::internal_execute_command(bool execute, std::vector<std::string_view> &params)
{
- debug_command *cmd, *found = nullptr;
- int i, foundcount = 0;
- char *p, *command;
- size_t len;
+ // no params is an error
+ if (params.empty())
+ return CMDERR::none();
+
+ // the first parameter has the command and the real first parameter; separate them
+ std::string_view command_param = params[0];
+ std::string_view::size_type pos = 0;
+ while (pos < command_param.length() && !isspace(u8(command_param[pos])))
+ pos++;
+ const std::string command(strmakelower(command_param.substr(0, pos)));
+ while (pos < command_param.length() && isspace(u8(command_param[pos])))
+ pos++;
+ if (pos == command_param.length() && params.size() == 1)
+ params.clear();
+ else
+ params[0].remove_prefix(pos);
- /* no params is an error */
- if (params == 0)
- return CMDERR_NONE;
+ // search the command list
+ auto const found = m_commandlist.lower_bound(command.c_str());
- /* the first parameter has the command and the real first parameter; separate them */
- for (p = param[0]; *p && isspace(u8(*p)); p++) { }
- for (command = p; *p && !isspace(u8(*p)); p++) { }
- if (*p != 0)
+ // error if not found
+ if (m_commandlist.end() == found || std::string_view(command) != std::string_view(found->command).substr(0, command.length()))
+ return CMDERR::unknown_command(0);
+ if (found->command.length() > command.length())
{
- *p++ = 0;
- for ( ; *p && isspace(u8(*p)); p++) { }
- if (*p != 0)
- param[0] = p;
- else
- params = 0;
+ auto const next = std::next(found);
+ if (m_commandlist.end() != next && std::string_view(command) == std::string_view(next->command).substr(0, command.length()))
+ return CMDERR::ambiguous_command(0);
}
- else
- {
- params = 0;
- param[0] = nullptr;
- }
-
- /* search the command list */
- len = strlen(command);
- for (cmd = m_commandlist; cmd != nullptr; cmd = cmd->next)
- if (!strncmp(command, cmd->command, len))
- {
- foundcount++;
- found = cmd;
- if (strlen(cmd->command) == len)
- {
- foundcount = 1;
- break;
- }
- }
- /* error if not found */
- if (!found)
- return MAKE_CMDERR_UNKNOWN_COMMAND(0);
- if (foundcount > 1)
- return MAKE_CMDERR_AMBIGUOUS_COMMAND(0);
+ // now go back and trim quotes and braces and any spaces they reveal
+ for (std::string_view &param : params)
+ param = trim_parameter(param, found->flags & CMDFLAG_KEEP_QUOTES);
- /* NULL-terminate and trim space around all the parameters */
- for (i = 1; i < params; i++)
- *param[i]++ = 0;
+ // see if we have the right number of parameters
+ if (params.size() < found->minparams)
+ return CMDERR::not_enough_params(0);
+ if (params.size() > found->maxparams)
+ return CMDERR::too_many_params(0);
- /* now go back and trim quotes and braces and any spaces they reveal*/
- for (i = 0; i < params; i++)
- trim_parameter(&param[i], found->flags & CMDFLAG_KEEP_QUOTES);
-
- /* see if we have the right number of parameters */
- if (params < found->minparams)
- return MAKE_CMDERR_NOT_ENOUGH_PARAMS(0);
- if (params > found->maxparams)
- return MAKE_CMDERR_TOO_MANY_PARAMS(0);
-
- /* execute the handler */
+ // execute the handler
if (execute)
- {
- std::vector<std::string> params_vec(param, param + params);
- found->handler(found->ref, params_vec);
- }
- return CMDERR_NONE;
+ found->handler(params);
+ return CMDERR::none();
}
-/*-------------------------------------------------
- internal_parse_command - parses a command
- and either executes or just validates it
--------------------------------------------------*/
+//-------------------------------------------------
+// internal_parse_command - parses a command
+// and either executes or just validates it
+//-------------------------------------------------
-CMDERR debugger_console::internal_parse_command(const std::string &original_command, bool execute)
+CMDERR debugger_console::internal_parse_command(std::string_view command, bool execute)
{
- char command[MAX_COMMAND_LENGTH], parens[MAX_COMMAND_LENGTH];
- char *params[MAX_COMMAND_PARAMS] = { nullptr };
- CMDERR result;
- char *command_start;
- char *p, c = 0;
-
- /* make a copy of the command */
- strcpy(command, original_command.c_str());
+ std::string_view::size_type pos = 0;
+ std::string_view::size_type len = command.length();
- /* loop over all semicolon-separated stuff */
- for (p = command; *p != 0; )
+ while (pos < len)
{
- int paramcount = 0, parendex = 0;
+ std::string parens;
+ std::vector<std::string_view> params;
bool foundend = false, instring = false, isexpr = false;
- /* find a semicolon or the end */
- for (params[paramcount++] = p; !foundend; p++)
+ // skip leading spaces
+ while (pos < len && isspace(u8(command[pos])))
+ pos++;
+ std::string_view::size_type startpos = pos;
+
+ // find a semicolon or the end
+ for (params.push_back(command.substr(pos)); !foundend && pos < len; pos++)
{
- c = *p;
+ char c = command[pos];
if (instring)
{
- if (c == '"' && p[-1] != '\\')
+ if (c == '"' && command[pos - 1] != '\\')
instring = false;
}
else
@@ -290,90 +351,84 @@ CMDERR debugger_console::internal_parse_command(const std::string &original_comm
case '"': instring = true; break;
case '(':
case '[':
- case '{': parens[parendex++] = c; break;
- case ')': if (parendex == 0 || parens[--parendex] != '(') return MAKE_CMDERR_UNBALANCED_PARENS(p - command); break;
- case ']': if (parendex == 0 || parens[--parendex] != '[') return MAKE_CMDERR_UNBALANCED_PARENS(p - command); break;
- case '}': if (parendex == 0 || parens[--parendex] != '{') return MAKE_CMDERR_UNBALANCED_PARENS(p - command); break;
- case ',': if (parendex == 0) params[paramcount++] = p; break;
- case ';': if (parendex == 0) foundend = true; break;
- case '-': if (parendex == 0 && paramcount == 1 && p[1] == '-') isexpr = true; *p = c; break;
- case '+': if (parendex == 0 && paramcount == 1 && p[1] == '+') isexpr = true; *p = c; break;
- case '=': if (parendex == 0 && paramcount == 1) isexpr = true; *p = c; break;
- case 0: foundend = true; break;
- default: *p = tolower(u8(c)); break;
+ case '{': parens.push_back(c); break;
+ case ')': if (parens.empty() || parens.back() != '(') return CMDERR::unbalanced_parens(pos); parens.pop_back(); break;
+ case ']': if (parens.empty() || parens.back() != '[') return CMDERR::unbalanced_parens(pos); parens.pop_back(); break;
+ case '}': if (parens.empty() || parens.back() != '{') return CMDERR::unbalanced_parens(pos); parens.pop_back(); break;
+ case ',': if (parens.empty()) { params.back().remove_suffix(len - pos); params.push_back(command.substr(pos + 1)); } break;
+ case ';': if (parens.empty()) { params.back().remove_suffix(len - pos); foundend = true; } break;
+ case '-': if (parens.empty() && params.size() == 1 && pos > 0 && command[pos - 1] == '-') isexpr = true; break;
+ case '+': if (parens.empty() && params.size() == 1 && pos > 0 && command[pos - 1] == '+') isexpr = true; break;
+ case '=': if (parens.empty() && params.size() == 1) isexpr = true; break;
+ default: break;
}
}
}
- /* check for unbalanced parentheses or quotes */
+ // check for unbalanced parentheses or quotes
if (instring)
- return MAKE_CMDERR_UNBALANCED_QUOTES(p - command);
- if (parendex != 0)
- return MAKE_CMDERR_UNBALANCED_PARENS(p - command);
-
- /* NULL-terminate if we ended in a semicolon */
- p--;
- if (c == ';') *p++ = 0;
+ return CMDERR::unbalanced_quotes(pos);
+ if (!parens.empty())
+ return CMDERR::unbalanced_parens(pos);
- /* process the command */
- command_start = params[0];
+ // process the command
+ std::string_view command_or_expr = params[0];
- /* allow for "do" commands */
- if (tolower(u8(command_start[0])) == 'd' && tolower(u8(command_start[1])) == 'o' && isspace(u8(command_start[2])))
+ // allow for "do" commands
+ if (command_or_expr.length() > 3 && tolower(u8(command_or_expr[0])) == 'd' && tolower(u8(command_or_expr[1])) == 'o' && isspace(u8(command_or_expr[2])))
{
isexpr = true;
- command_start += 3;
+ command_or_expr.remove_prefix(3);
}
- /* if it smells like an assignment expression, treat it as such */
- if (isexpr && paramcount == 1)
+ // if it smells like an assignment expression, treat it as such
+ if (isexpr && params.size() == 1)
{
try
{
- u64 expresult;
- parsed_expression expression(m_machine.debugger().cpu().get_visible_symtable(), command_start, &expresult);
+ parsed_expression expr(visible_symtable(), command_or_expr);
+ if (execute)
+ expr.execute();
}
catch (expression_error &err)
{
- return MAKE_CMDERR_EXPRESSION_ERROR(err);
+ return CMDERR::expression_error(err);
}
}
else
{
- result = internal_execute_command(execute, paramcount, &params[0]);
- if (result != CMDERR_NONE)
- return MAKE_CMDERR(CMDERR_ERROR_CLASS(result), command_start - command);
+ const CMDERR result = internal_execute_command(execute, params);
+ if (result.error_class() != CMDERR::NONE)
+ return CMDERR(result.error_class(), startpos);
}
}
- return CMDERR_NONE;
+ return CMDERR::none();
}
-/*-------------------------------------------------
- execute_command - execute a command string
--------------------------------------------------*/
+//-------------------------------------------------
+// execute_command - execute a command string
+//-------------------------------------------------
-CMDERR debugger_console::execute_command(const std::string &command, bool echo)
+CMDERR debugger_console::execute_command(std::string_view command, bool echo)
{
- CMDERR result;
-
- /* echo if requested */
+ // echo if requested
if (echo)
- printf(">%s\n", command.c_str());
+ printf(">%s\n", command);
- /* parse and execute */
- result = internal_parse_command(command, true);
+ // parse and execute
+ const CMDERR result = internal_parse_command(command, true);
- /* display errors */
- if (result != CMDERR_NONE)
+ // display errors
+ if (result.error_class() != CMDERR::NONE)
{
if (!echo)
- printf(">%s\n", command.c_str());
- printf(" %*s^\n", CMDERR_ERROR_OFFSET(result), "");
- printf("%s\n", cmderr_to_string(result).c_str());
+ printf(">%s\n", command);
+ printf(" %*s^\n", result.error_offset(), "");
+ printf("%s\n", cmderr_to_string(result));
}
- /* update all views */
+ // update all views
if (echo)
{
m_machine.debug_view().update_all();
@@ -383,11 +438,11 @@ CMDERR debugger_console::execute_command(const std::string &command, bool echo)
}
-/*-------------------------------------------------
- validate_command - validate a command string
--------------------------------------------------*/
+//-------------------------------------------------
+// validate_command - validate a command string
+//-------------------------------------------------
-CMDERR debugger_console::validate_command(const char *command)
+CMDERR debugger_console::validate_command(std::string_view command)
{
return internal_parse_command(command, false);
}
@@ -397,26 +452,16 @@ CMDERR debugger_console::validate_command(const char *command)
register_command - register a command handler
-------------------------------------------------*/
-void debugger_console::register_command(const char *command, u32 flags, int ref, int minparams, int maxparams, std::function<void(int, const std::vector<std::string> &)> handler)
+void debugger_console::register_command(std::string_view command, u32 flags, int minparams, int maxparams, std::function<void (const std::vector<std::string_view> &)> &&handler)
{
if (m_machine.phase() != machine_phase::INIT)
throw emu_fatalerror("Can only call debugger_console::register_command() at init time!");
if (!(m_machine.debug_flags & DEBUG_FLAG_ENABLED))
throw emu_fatalerror("Cannot call debugger_console::register_command() when debugger is not running");
- debug_command *cmd = auto_alloc_clear(m_machine, <debug_command>());
-
- /* fill in the command */
- strcpy(cmd->command, command);
- cmd->flags = flags;
- cmd->ref = ref;
- cmd->minparams = minparams;
- cmd->maxparams = maxparams;
- cmd->handler = handler;
-
- /* link it */
- cmd->next = m_commandlist;
- m_commandlist = cmd;
+ auto const ins = m_commandlist.emplace(command, flags, minparams, maxparams, std::move(handler));
+ if (!ins.second)
+ osd_printf_error("error: Duplicate debugger command %s registered\n", command);
}
@@ -469,7 +514,7 @@ void debugger_console::process_source_file()
buf.resize(pos);
// strip whitespace
- strtrimrightspace(buf);
+ buf = strtrimrightspace(buf);
// execute the command
if (!buf.empty())
@@ -499,22 +544,510 @@ void debugger_console::process_source_file()
std::string debugger_console::cmderr_to_string(CMDERR error)
{
- int offset = CMDERR_ERROR_OFFSET(error);
- switch (CMDERR_ERROR_CLASS(error))
+ const int offset = error.error_offset();
+ switch (error.error_class())
{
- case CMDERR_UNKNOWN_COMMAND: return "unknown command";
- case CMDERR_AMBIGUOUS_COMMAND: return "ambiguous command";
- case CMDERR_UNBALANCED_PARENS: return "unbalanced parentheses";
- case CMDERR_UNBALANCED_QUOTES: return "unbalanced quotes";
- case CMDERR_NOT_ENOUGH_PARAMS: return "not enough parameters for command";
- case CMDERR_TOO_MANY_PARAMS: return "too many parameters for command";
- case CMDERR_EXPRESSION_ERROR: return string_format("error in assignment expression: %s",
+ case CMDERR::UNKNOWN_COMMAND: return "unknown command";
+ case CMDERR::AMBIGUOUS_COMMAND: return "ambiguous command";
+ case CMDERR::UNBALANCED_PARENS: return "unbalanced parentheses";
+ case CMDERR::UNBALANCED_QUOTES: return "unbalanced quotes";
+ case CMDERR::NOT_ENOUGH_PARAMS: return "not enough parameters for command";
+ case CMDERR::TOO_MANY_PARAMS: return "too many parameters for command";
+ case CMDERR::EXPRESSION_ERROR: return string_format("error in assignment expression: %s",
expression_error(static_cast<expression_error::error_code>(offset)).code_string());
default: return "unknown error";
}
}
+//**************************************************************************
+// PARAMETER VALIDATION HELPERS
+//**************************************************************************
+
+namespace {
+
+template <typename T>
+inline std::string_view::size_type find_delimiter(std::string_view str, T &&is_delim)
+{
+ unsigned parens = 0;
+ for (std::string_view::size_type i = 0; str.length() > i; ++i)
+ {
+ if (str[i] == '(')
+ {
+ ++parens;
+ }
+ else if (parens)
+ {
+ if (str[i] == ')')
+ --parens;
+ }
+ else if (is_delim(str[i]))
+ {
+ return i;
+ }
+ }
+ return std::string_view::npos;
+}
+
+} // anonymous namespace
+
+
+/// \brief Validate parameter as a Boolean value
+///
+/// Validates a parameter as a Boolean value. Fixed strings and
+/// expressions evaluating to numeric values are recognised. The result
+/// is unchanged for an empty string.
+/// \param [in] param The parameter string.
+/// \param [in,out] result The default value on entry, and the value of
+/// the parameter interpreted as a Boolean on success. Unchanged if
+/// the parameter is an empty string.
+/// \return true if the parameter is a valid Boolean value or an empty
+/// string, or false otherwise.
+bool debugger_console::validate_boolean_parameter(std::string_view param, bool &result)
+{
+ // nullptr parameter does nothing and returns no error
+ if (param.empty())
+ return true;
+
+ // evaluate the expression; success if no error
+ using namespace std::literals;
+ bool const is_true = util::streqlower(param, "true"sv);
+ bool const is_false = util::streqlower(param, "false"sv);
+
+ if (is_true || is_false)
+ {
+ result = is_true;
+ return true;
+ }
+
+ // try to evaluate as a number
+ u64 val;
+ if (!validate_number_parameter(param, val))
+ return false;
+
+ result = val != 0;
+ return true;
+}
+
+
+/// \brief Validate parameter as a numeric value
+///
+/// Parses the parameter as an expression and evaluates it as a number.
+/// \param [in] param The parameter string.
+/// \param [out] result The numeric value of the expression on success.
+/// Unchanged on failure.
+/// \return true if the parameter is a valid expression that evaluates
+/// to a numeric value, or false otherwise.
+bool debugger_console::validate_number_parameter(std::string_view param, u64 &result)
+{
+ // evaluate the expression; success if no error
+ try
+ {
+ result = parsed_expression(visible_symtable(), param).execute();
+ return true;
+ }
+ catch (expression_error const &error)
+ {
+ // print an error pointing to the character that caused it
+ printf("Error in expression: %s\n", param);
+ printf(" %*s^", error.offset(), "");
+ printf("%s\n", error.code_string());
+ return false;
+ }
+}
+
+
+/// \brief Validate parameter as a device
+///
+/// Validates a parameter as a device identifier and retrieves the
+/// device on success. A string corresponding to the tag of a device
+/// refers to that device; an empty string refers to the current CPU
+/// with debugger focus; any other string is parsed as an expression
+/// and treated as an index of a device implementing
+/// #device_execute_interface and #device_state_interface, and exposing
+/// a generic PC base value.
+/// \param [in] param The parameter string.
+/// \param [out] result A pointer to the device on success, or unchanged
+/// on failure.
+/// \return true if the parameter refers to a device in the current
+/// system, or false otherwise.
+bool debugger_console::validate_device_parameter(std::string_view param, device_t *&result)
+{
+ // if no parameter, use the visible CPU
+ if (param.empty())
+ {
+ device_t *const current = m_visiblecpu;
+ if (current)
+ {
+ result = current;
+ return true;
+ }
+ else
+ {
+ printf("No valid CPU is currently selected\n");
+ return false;
+ }
+ }
+
+ // next look for a tag match
+ std::string_view relative = param;
+ device_t &base = get_device_search_base(relative);
+ device_t *device = base.subdevice(strmakelower(relative));
+ if (device)
+ {
+ result = device;
+ return true;
+ }
+
+ // then evaluate as an expression; on an error assume it was a tag
+ u64 cpunum;
+ try
+ {
+ cpunum = parsed_expression(visible_symtable(), param).execute();
+ }
+ catch (expression_error &)
+ {
+ printf("Unable to find device '%s'\n", param);
+ return false;
+ }
+
+ // attempt to find by numerical index
+ device = get_cpu_by_index(cpunum);
+ if (device)
+ {
+ result = device;
+ return true;
+ }
+ else
+ {
+ // if out of range, complain
+ printf("Invalid CPU index %u\n", cpunum);
+ return false;
+ }
+}
+
+
+/// \brief Validate a parameter as a CPU
+///
+/// Validates a parameter as a CPU identifier. Uses the same rules as
+/// #validate_device_parameter to identify devices, but additionally
+/// checks that the device is a "CPU" for the debugger's purposes.
+/// \param [in] The parameter string.
+/// \param [out] result The device on success, or unchanged on failure.
+/// \return true if the parameter refers to a CPU-like device in the
+/// current system, or false otherwise.
+bool debugger_console::validate_cpu_parameter(std::string_view param, device_t *&result)
+{
+ // first do the standard device thing
+ device_t *device;
+ if (!validate_device_parameter(param, device))
+ return false;
+
+ // check that it's a "CPU" for the debugger's purposes
+ device_execute_interface const *execute;
+ if (device->interface(execute))
+ {
+ result = device;
+ return true;
+ }
+
+ printf("Device %s is not a CPU\n", device->name());
+ return false;
+}
+
+
+/// \brief Validate a parameter as an address space identifier
+///
+/// Validates a parameter as an address space identifier. Uses the same
+/// rules as #validate_device_parameter to identify devices. If the
+/// default address space number is negative, the first address space
+/// exposed by the device will be used as the default.
+/// \param [in] The parameter string.
+/// \param [in] spacenum The default address space index. If negative,
+/// the first address space exposed by the device (i.e. the address
+/// space with the lowest index) will be used as the default.
+/// \param [out] result The addresfs space on success, or unchanged on
+/// failure.
+/// \return true if the parameter refers to an address space in the
+/// current system, or false otherwise.
+bool debugger_console::validate_device_space_parameter(std::string_view param, int spacenum, address_space *&result)
+{
+ device_t *device;
+ std::string spacename;
+ if (param.empty())
+ {
+ // if no parameter, use the visible CPU
+ device = m_visiblecpu;
+ if (!device)
+ {
+ printf("No valid CPU is currently selected\n");
+ return false;
+ }
+ }
+ else
+ {
+ // look for a tag match on the whole parameter value
+ std::string_view relative = param;
+ device_t &base = get_device_search_base(relative);
+ device = base.subdevice(strmakelower(relative));
+
+ // if that failed, treat the last component as an address space
+ if (!device)
+ {
+ auto const delimiter = relative.find_last_of(":^");
+ bool const found = std::string_view::npos != delimiter;
+ if (!found || (':' == relative[delimiter]))
+ {
+ spacename = strmakelower(relative.substr(found ? (delimiter + 1) : 0));
+ relative = relative.substr(0, !found ? 0 : !delimiter ? 1 : delimiter);
+ if (!relative.empty())
+ device = base.subdevice(strmakelower(relative));
+ else if (m_visiblecpu)
+ device = m_visiblecpu;
+ else
+ device = &m_machine.root_device();
+ }
+ }
+ }
+
+ // if still no device found, evaluate as an expression
+ if (!device)
+ {
+ u64 cpunum;
+ try
+ {
+ cpunum = parsed_expression(visible_symtable(), param).execute();
+ }
+ catch (expression_error const &)
+ {
+ // parsing failed - assume it was a tag
+ printf("Unable to find device '%s'\n", param);
+ return false;
+ }
+
+ // attempt to find by numerical index
+ device = get_cpu_by_index(cpunum);
+ if (!device)
+ {
+ // if out of range, complain
+ printf("Invalid CPU index %u\n", cpunum);
+ return false;
+ }
+ }
+
+ // ensure the device implements the memory interface
+ device_memory_interface *memory;
+ if (!device->interface(memory))
+ {
+ printf("No memory interface found for device %s\n", device->name());
+ return false;
+ }
+
+ // fall back to supplied default space if appropriate
+ if (spacename.empty() && (0 <= spacenum))
+ {
+ if (memory->has_space(spacenum))
+ {
+ result = &memory->space(spacenum);
+ return true;
+ }
+ else
+ {
+ printf("No matching memory space found for device '%s'\n", device->tag());
+ return false;
+ }
+ }
+
+ // otherwise find the specified space or fall back to the first populated space
+ for (int i = 0; memory->max_space_count() > i; ++i)
+ {
+ if (memory->has_space(i) && (spacename.empty() || (memory->space(i).name() == spacename)))
+ {
+ result = &memory->space(i);
+ return true;
+ }
+ }
+
+ // report appropriate error message
+ if (spacename.empty())
+ printf("No memory spaces found for device '%s'\n", device->tag());
+ else
+ printf("Memory space '%s' not found found for device '%s'\n", spacename, device->tag());
+ return false;
+}
+
+
+/// \brief Validate a parameter as a target address
+///
+/// Validates a parameter as an numeric expression to use as an address
+/// optionally followed by a colon and a device identifier. If the
+/// device identifier is not presnt, the current CPU with debugger focus
+/// is assumed. See #validate_device_parameter for information on how
+/// device parametersare interpreted.
+/// \param [in] The parameter string.
+/// \param [in] spacenum The default address space index. If negative,
+/// the first address space exposed by the device (i.e. the address
+/// space with the lowest index) will be used as the default.
+/// \param [out] space The address space on success, or unchanged on
+/// failure.
+/// \param [out] addr The address on success, or unchanged on failure.
+/// \return true if the address is a valid expression evaluating to a
+/// number and the address space is found, or false otherwise.
+bool debugger_console::validate_target_address_parameter(std::string_view param, int spacenum, address_space *&space, u64 &addr)
+{
+ // check for the device delimiter
+ std::string_view::size_type const devdelim = find_delimiter(param, [] (char ch) { return ':' == ch; });
+ std::string_view device;
+ if (devdelim != std::string::npos)
+ device = param.substr(devdelim + 1);
+
+ // parse the address first
+ u64 addrval;
+ if (!validate_number_parameter(param.substr(0, devdelim), addrval))
+ return false;
+
+ // find the address space
+ if (!validate_device_space_parameter(device, spacenum, space))
+ return false;
+
+ // set the address now that we have the space
+ addr = addrval;
+ return true;
+}
+
+
+/// \brief Validate a parameter as a memory region
+///
+/// Validates a parameter as a memory region tag and retrieves the
+/// specified memory region.
+/// \param [in] The parameter string.
+/// \param [out] result The memory region on success, or unchanged on
+/// failure.
+/// \return true if the parameter refers to a memory region in the
+/// current system, or false otherwise.
+bool debugger_console::validate_memory_region_parameter(std::string_view param, memory_region *&result)
+{
+ auto const &regions = m_machine.memory().regions();
+ std::string_view relative = param;
+ device_t &base = get_device_search_base(relative);
+ auto const iter = regions.find(base.subtag(strmakelower(relative)));
+ if (regions.end() != iter)
+ {
+ result = iter->second.get();
+ return true;
+ }
+ else
+ {
+ printf("No matching memory region found for '%s'\n", param);
+ return false;
+ }
+}
+
+
+/// \brief Get search base for device or address space parameter
+///
+/// Handles prefix prefixes used to indicate that a device tag should be
+/// interpreted relative to the selected CPU. Removes the recognised
+/// prefixes from the parameter value.
+/// \param [in,out] param The parameter string. Recognised prefixes
+/// affecting the search base are removed, leaving a tag relative to
+/// the base device.
+/// \return A reference to the base device that the tag should be
+/// interpreted relative to.
+device_t &debugger_console::get_device_search_base(std::string_view &param) const
+{
+ if (!param.empty())
+ {
+ // handle ".:" or ".^" prefix for tag relative to current CPU if any
+ if (('.' == param[0]) && ((param.size() == 1) || (':' == param[1]) || ('^' == param[1])))
+ {
+ param.remove_prefix(((param.size() > 1) && (':' == param[1])) ? 2 : 1);
+ device_t *const current = m_visiblecpu;
+ return current ? *current : m_machine.root_device();
+ }
+
+ // a sibling path makes most sense relative to current CPU
+ if ('^' == param[0])
+ {
+ device_t *const current = m_visiblecpu;
+ return current ? *current : m_machine.root_device();
+ }
+ }
+
+ // default to root device
+ return m_machine.root_device();
+}
+
+
+/// \brief Get CPU by index
+///
+/// Looks up a CPU by the number the debugger assigns it based on its
+/// position in the device tree relative to other CPUs.
+/// \param [in] cpunum Zero-based index of the CPU to find.
+/// \return A pointer to the CPU if found, or \c nullptr if no CPU has
+/// the specified index.
+device_t *debugger_console::get_cpu_by_index(u64 cpunum) const
+{
+ unsigned index = 0;
+ for (device_execute_interface &exec : execute_interface_enumerator(m_machine.root_device()))
+ {
+ // real CPUs should have pcbase
+ device_state_interface const *state;
+ if (exec.device().interface(state) && state->state_find_entry(STATE_GENPCBASE))
+ {
+ if (index++ == cpunum)
+ {
+ return &exec.device();
+ }
+ }
+ }
+ return nullptr;
+}
+
+
+//-------------------------------------------------
+// validate_expression_parameter - validates
+// an expression parameter
+//-----------------------------------------------*/
+
+bool debugger_console::validate_expression_parameter(std::string_view param, parsed_expression &result)
+{
+ try
+ {
+ // parse the expression; success if no error
+ result.parse(param);
+ return true;
+ }
+ catch (expression_error const &err)
+ {
+ // output an error
+ printf("Error in expression: %s\n", param);
+ printf(" %*s^", err.offset(), "");
+ printf("%s\n", err.code_string());
+ return false;
+ }
+}
+
+
+//-------------------------------------------------
+// validate_command_parameter - validates a
+// command parameter
+//-------------------------------------------------
+
+bool debugger_console::validate_command_parameter(std::string_view param)
+{
+ // validate the comment; success if no error
+ CMDERR err = validate_command(param);
+ if (err.error_class() == CMDERR::NONE)
+ return true;
+
+ // output an error
+ printf("Error in command: %s\n", param);
+ printf(" %*s^", err.error_offset(), "");
+ printf("%s\n", cmderr_to_string(err));
+ return false;
+}
+
/***************************************************************************
@@ -522,62 +1055,86 @@ std::string debugger_console::cmderr_to_string(CMDERR error)
***************************************************************************/
-/*-------------------------------------------------
- vprintf - vprintfs the given arguments using
- the format to the debug console
--------------------------------------------------*/
-void debugger_console::vprintf(util::format_argument_pack<std::ostream> const &args)
+//-------------------------------------------------
+// print_core - write preformatted text
+// to the debug console
+//-------------------------------------------------
+
+void debugger_console::print_core(std::string_view text)
+{
+ text_buffer_print(*m_console_textbuf, text);
+ if (m_logfile)
+ m_logfile->write(text.data(), text.length());
+}
+
+//-------------------------------------------------
+// print_core_wrap - write preformatted text
+// to the debug console, with wrapping
+//-------------------------------------------------
+
+void debugger_console::print_core_wrap(std::string_view text, int wrapcol)
{
- text_buffer_print(m_console_textbuf, util::string_format(args).c_str());
+ // FIXME: look into honoring wrapcol for the logfile
+ text_buffer_print_wrap(*m_console_textbuf, text, wrapcol);
+ if (m_logfile)
+ m_logfile->write(text.data(), text.length());
+}
- /* force an update of any console views */
+//-------------------------------------------------
+// vprintf - vprintfs the given arguments using
+// the format to the debug console
+//-------------------------------------------------
+
+void debugger_console::vprintf(util::format_argument_pack<char> const &args)
+{
+ print_core(util::string_format(args));
+
+ // force an update of any console views
m_machine.debug_view().update_all(DVT_CONSOLE);
}
-void debugger_console::vprintf(util::format_argument_pack<std::ostream> &&args)
+void debugger_console::vprintf(util::format_argument_pack<char> &&args)
{
- text_buffer_print(m_console_textbuf, util::string_format(std::move(args)).c_str());
+ print_core(util::string_format(std::move(args)));
- /* force an update of any console views */
+ // force an update of any console views
m_machine.debug_view().update_all(DVT_CONSOLE);
}
-/*-------------------------------------------------
- vprintf_wrap - vprintfs the given arguments
- using the format to the debug console
--------------------------------------------------*/
+//-------------------------------------------------
+// vprintf_wrap - vprintfs the given arguments
+// using the format to the debug console
+//-------------------------------------------------
-void debugger_console::vprintf_wrap(int wrapcol, util::format_argument_pack<std::ostream> const &args)
+void debugger_console::vprintf_wrap(int wrapcol, util::format_argument_pack<char> const &args)
{
- text_buffer_print_wrap(m_console_textbuf, util::string_format(args).c_str(), wrapcol);
+ print_core_wrap(util::string_format(args), wrapcol);
- /* force an update of any console views */
+ // force an update of any console views
m_machine.debug_view().update_all(DVT_CONSOLE);
}
-void debugger_console::vprintf_wrap(int wrapcol, util::format_argument_pack<std::ostream> &&args)
+void debugger_console::vprintf_wrap(int wrapcol, util::format_argument_pack<char> &&args)
{
- text_buffer_print_wrap(m_console_textbuf, util::string_format(std::move(args)).c_str(), wrapcol);
+ print_core_wrap(util::string_format(std::move(args)), wrapcol);
- /* force an update of any console views */
+ // force an update of any console views
m_machine.debug_view().update_all(DVT_CONSOLE);
}
-/*-------------------------------------------------
- errorlog_write_line - writes a line to the
- errorlog ring buffer
--------------------------------------------------*/
+//-------------------------------------------------
+// errorlog_write_line - writes a line to the
+// errorlog ring buffer
+//-------------------------------------------------
void debugger_console::errorlog_write_line(const char *line)
{
if (m_errorlog_textbuf)
- {
- text_buffer_print(m_errorlog_textbuf, line);
- }
+ text_buffer_print(*m_errorlog_textbuf, line);
- /* force an update of any log views */
+ // force an update of any log views
m_machine.debug_view().update_all(DVT_LOG);
}
diff --git a/src/emu/debug/debugcon.h b/src/emu/debug/debugcon.h
index 289123591f0..37b2e235348 100644
--- a/src/emu/debug/debugcon.h
+++ b/src/emu/debug/debugcon.h
@@ -16,86 +16,85 @@
#include "textbuf.h"
#include <functional>
+#include <set>
/***************************************************************************
CONSTANTS
***************************************************************************/
-#define MAX_COMMAND_LENGTH 4096
-#define MAX_COMMAND_PARAMS 128
+constexpr int MAX_COMMAND_PARAMS = 128;
-/* flags for command parsing */
-#define CMDFLAG_NONE (0x0000)
-#define CMDFLAG_KEEP_QUOTES (0x0001)
-#define CMDFLAG_CUSTOM_HELP (0x0002)
-
-/* values for the error code in a command error */
-#define CMDERR_NONE (0)
-#define CMDERR_UNKNOWN_COMMAND (1)
-#define CMDERR_AMBIGUOUS_COMMAND (2)
-#define CMDERR_UNBALANCED_PARENS (3)
-#define CMDERR_UNBALANCED_QUOTES (4)
-#define CMDERR_NOT_ENOUGH_PARAMS (5)
-#define CMDERR_TOO_MANY_PARAMS (6)
-#define CMDERR_EXPRESSION_ERROR (7)
-
-/* parameter separator macros */
-#define CMDPARAM_SEPARATOR "\0"
-#define CMDPARAM_TERMINATOR "\0\0"
+// flags for command parsing
+constexpr u32 CMDFLAG_NONE = 0x0000;
+constexpr u32 CMDFLAG_KEEP_QUOTES = 0x0001;
+constexpr u32 CMDFLAG_CUSTOM_HELP = 0x0002;
/***************************************************************************
- MACROS
+ TYPE DEFINITIONS
***************************************************************************/
-/* command error assembly/disassembly macros */
-#define CMDERR_ERROR_CLASS(x) ((x) >> 16)
-#define CMDERR_ERROR_OFFSET(x) ((x) & 0xffff)
-#define MAKE_CMDERR(a,b) (((a) << 16) | ((b) & 0xffff))
-
-/* macros to assemble specific error conditions */
-#define MAKE_CMDERR_UNKNOWN_COMMAND(x) MAKE_CMDERR(CMDERR_UNKNOWN_COMMAND, (x))
-#define MAKE_CMDERR_AMBIGUOUS_COMMAND(x) MAKE_CMDERR(CMDERR_AMBIGUOUS_COMMAND, (x))
-#define MAKE_CMDERR_UNBALANCED_PARENS(x) MAKE_CMDERR(CMDERR_UNBALANCED_PARENS, (x))
-#define MAKE_CMDERR_UNBALANCED_QUOTES(x) MAKE_CMDERR(CMDERR_UNBALANCED_QUOTES, (x))
-#define MAKE_CMDERR_NOT_ENOUGH_PARAMS(x) MAKE_CMDERR(CMDERR_NOT_ENOUGH_PARAMS, (x))
-#define MAKE_CMDERR_TOO_MANY_PARAMS(x) MAKE_CMDERR(CMDERR_TOO_MANY_PARAMS, (x))
-#define MAKE_CMDERR_EXPRESSION_ERROR(x) MAKE_CMDERR(CMDERR_EXPRESSION_ERROR, (x))
-
+// CMDERR is an error code for command evaluation
+class CMDERR
+{
+public:
+ // values for the error code in a command error
+ static constexpr u16 NONE = 0;
+ static constexpr u16 UNKNOWN_COMMAND = 1;
+ static constexpr u16 AMBIGUOUS_COMMAND = 2;
+ static constexpr u16 UNBALANCED_PARENS = 3;
+ static constexpr u16 UNBALANCED_QUOTES = 4;
+ static constexpr u16 NOT_ENOUGH_PARAMS = 5;
+ static constexpr u16 TOO_MANY_PARAMS = 6;
+ static constexpr u16 EXPRESSION_ERROR = 7;
+
+ // command error assembly/disassembly
+ constexpr CMDERR(u16 c, u16 o) : m_error_class(c), m_error_offset(o) { }
+ constexpr u16 error_class() const { return m_error_class; }
+ constexpr u16 error_offset() const { return m_error_offset; }
+
+ // assemble specific error conditions
+ static constexpr CMDERR none() { return CMDERR(NONE, 0); }
+ static constexpr CMDERR unknown_command(u16 x) { return CMDERR(UNKNOWN_COMMAND, x); }
+ static constexpr CMDERR ambiguous_command(u16 x) { return CMDERR(AMBIGUOUS_COMMAND, x); }
+ static constexpr CMDERR unbalanced_parens(u16 x) { return CMDERR(UNBALANCED_PARENS, x); }
+ static constexpr CMDERR unbalanced_quotes(u16 x) { return CMDERR(UNBALANCED_QUOTES, x); }
+ static constexpr CMDERR not_enough_params(u16 x) { return CMDERR(NOT_ENOUGH_PARAMS, x); }
+ static constexpr CMDERR too_many_params(u16 x) { return CMDERR(TOO_MANY_PARAMS, x); }
+ static constexpr CMDERR expression_error(u16 x) { return CMDERR(EXPRESSION_ERROR, x); }
-/***************************************************************************
- TYPE DEFINITIONS
-***************************************************************************/
+private:
+ u16 m_error_class, m_error_offset;
+};
-/* CMDERR is an error code for command evaluation */
-typedef u32 CMDERR;
class debugger_console
{
public:
debugger_console(running_machine &machine);
+ ~debugger_console();
- /* command handling */
- CMDERR execute_command(const std::string &command, bool echo);
- CMDERR validate_command(const char *command);
- void register_command(const char *command, u32 flags, int ref, int minparams, int maxparams, std::function<void(int, const std::vector<std::string> &)> handler);
+ // command handling
+ CMDERR execute_command(std::string_view command, bool echo);
+ CMDERR validate_command(std::string_view command);
+ void register_command(std::string_view command, u32 flags, int minparams, int maxparams, std::function<void (const std::vector<std::string_view> &)> &&handler);
void source_script(const char *file);
void process_source_file();
- /* console management */
- void vprintf(util::format_argument_pack<std::ostream> const &args);
- void vprintf(util::format_argument_pack<std::ostream> &&args);
- void vprintf_wrap(int wrapcol, util::format_argument_pack<std::ostream> const &args);
- void vprintf_wrap(int wrapcol, util::format_argument_pack<std::ostream> &&args);
- text_buffer * get_console_textbuf() const { return m_console_textbuf; }
+ // console management
+ void vprintf(util::format_argument_pack<char> const &args);
+ void vprintf(util::format_argument_pack<char> &&args);
+ void vprintf_wrap(int wrapcol, util::format_argument_pack<char> const &args);
+ void vprintf_wrap(int wrapcol, util::format_argument_pack<char> &&args);
+ text_buffer &get_console_textbuf() const { return *m_console_textbuf; }
- /* errorlog management */
- void errorlog_write_line(const char *line);
- text_buffer * get_errorlog_textbuf() const { return m_errorlog_textbuf; }
+ // errorlog management
+ void errorlog_write_line(const char *line);
+ text_buffer &get_errorlog_textbuf() const { return *m_errorlog_textbuf; }
- /* convenience templates */
+ // convenience templates
template <typename Format, typename... Params>
inline void printf(Format &&fmt, Params &&...args)
{
@@ -107,38 +106,88 @@ public:
vprintf_wrap(wrapcol, util::make_format_argument_pack(std::forward<Format>(fmt), std::forward<Params>(args)...));
}
+ device_t *get_visible_cpu() const { return m_visiblecpu; }
+ void set_visible_cpu(device_t *visiblecpu) { m_visiblecpu = visiblecpu; }
+ symbol_table &visible_symtable() const;
+
static std::string cmderr_to_string(CMDERR error);
+ // validates a parameter as a boolean value
+ bool validate_boolean_parameter(std::string_view param, bool &result);
+
+ // validates a parameter as a numeric value
+ bool validate_number_parameter(std::string_view param, u64 &result);
+
+ // validates a parameter as a device
+ bool validate_device_parameter(std::string_view param, device_t *&result);
+
+ // validates a parameter as a CPU
+ bool validate_cpu_parameter(std::string_view param, device_t *&result);
+
+ // validates a parameter as an address space identifier
+ bool validate_device_space_parameter(std::string_view param, int spacenum, address_space *&result);
+
+ // validates a parameter as a target address and retrieves the given address space and address
+ bool validate_target_address_parameter(std::string_view param, int spacenum, address_space *&space, u64 &addr);
+
+ // validates a parameter as a memory region name and retrieves the given region
+ bool validate_memory_region_parameter(std::string_view param, memory_region *&result);
+
+ // validates a parameter as a debugger expression
+ bool validate_expression_parameter(std::string_view param, parsed_expression &result);
+
+ // validates a parameter as a debugger command
+ bool validate_command_parameter(std::string_view param);
+
private:
void exit();
- void execute_help_custom(int ref, const std::vector<std::string> &params);
+ void execute_help_custom(const std::vector<std::string_view> &params);
+ void execute_condump(const std::vector<std::string_view>& params);
+
+ [[nodiscard]] static std::string_view trim_parameter(std::string_view param, bool keep_quotes);
+ CMDERR internal_execute_command(bool execute, std::vector<std::string_view> &params);
+ CMDERR internal_parse_command(std::string_view command, bool execute);
+
+ void print_core(std::string_view text); // core text output
+ void print_core_wrap(std::string_view text, int wrapcol); // core text output
- void trim_parameter(char **paramptr, bool keep_quotes);
- CMDERR internal_execute_command(bool execute, int params, char **param);
- CMDERR internal_parse_command(const std::string &original_command, bool execute);
+ device_t &get_device_search_base(std::string_view &param) const;
+ device_t *get_cpu_by_index(u64 cpunum) const;
struct debug_command
{
- debug_command * next;
- char command[32];
+ debug_command(std::string_view _command, u32 _flags, int _minparams, int _maxparams, std::function<void (const std::vector<std::string_view> &)> &&_handler);
+
+ struct compare
+ {
+ using is_transparent = void;
+ bool operator()(const debug_command &a, const debug_command &b) const;
+ bool operator()(const char *a, const debug_command &b) const;
+ bool operator()(const debug_command &a, const char *b) const;
+ };
+
+ std::string command;
const char * params;
const char * help;
- std::function<void(int, const std::vector<std::string> &)> handler;
+ std::function<void (const std::vector<std::string_view> &)> handler;
u32 flags;
- int ref;
int minparams;
int maxparams;
};
running_machine &m_machine;
- text_buffer *m_console_textbuf;
- text_buffer *m_errorlog_textbuf;
+ // visible CPU device (the one that commands should apply to)
+ device_t *m_visiblecpu;
+
+ text_buffer_ptr m_console_textbuf;
+ text_buffer_ptr m_errorlog_textbuf;
- debug_command *m_commandlist;
+ std::set<debug_command, debug_command::compare> m_commandlist;
std::unique_ptr<std::istream> m_source_file; // script source file
+ std::unique_ptr<emu_file> m_logfile; // logfile for debug console output
};
#endif // MAME_EMU_DEBUG_DEBUGCON_H
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();
}
diff --git a/src/emu/debug/debugcpu.h b/src/emu/debug/debugcpu.h
index 26653eb2baa..631d4c788d9 100644
--- a/src/emu/debug/debugcpu.h
+++ b/src/emu/debug/debugcpu.h
@@ -13,9 +13,8 @@
#pragma once
-#include "express.h"
-
#include <set>
+#include <utility>
//**************************************************************************
@@ -30,9 +29,6 @@ constexpr int COMMENT_VERSION = 1;
// TYPE DEFINITIONS
//**************************************************************************
-typedef int (*debug_instruction_hook_func)(device_t &device, offs_t curpc);
-
-
// ======================> device_debug
// [TODO] This whole thing is terrible.
@@ -40,133 +36,12 @@ typedef int (*debug_instruction_hook_func)(device_t &device, offs_t curpc);
class device_debug
{
public:
- // breakpoint class
- class breakpoint
- {
- friend class device_debug;
-
- public:
- // construction/destruction
- breakpoint(
- device_debug* debugInterface,
- symbol_table &symbols,
- int index,
- offs_t address,
- const char *condition = nullptr,
- const char *action = nullptr);
-
- // getters
- const device_debug *debugInterface() const { return m_debugInterface; }
- int index() const { return m_index; }
- bool enabled() const { return m_enabled; }
- offs_t address() const { return m_address; }
- const char *condition() const { return m_condition.original_string(); }
- const char *action() const { return m_action.c_str(); }
-
- // setters
- void setEnabled(bool value) { m_enabled = value; }
-
- private:
- // internals
- bool hit(offs_t pc);
- const device_debug * m_debugInterface; // the interface we were created from
- int m_index; // user reported index
- bool m_enabled; // enabled?
- offs_t m_address; // execution address
- parsed_expression m_condition; // condition
- std::string m_action; // action
- };
-
- // watchpoint class
- class watchpoint
- {
- friend class device_debug;
-
- public:
- // construction/destruction
- 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 = nullptr,
- const char *action = nullptr);
- ~watchpoint();
-
- // getters
- const device_debug *debugInterface() const { return m_debugInterface; }
- address_space &space() const { return m_space; }
- int index() const { return m_index; }
- read_or_write type() const { return m_type; }
- bool enabled() const { return m_enabled; }
- offs_t address() const { return m_address; }
- offs_t length() const { return m_length; }
- const char *condition() const { return m_condition.original_string(); }
- const std::string &action() const { return m_action; }
-
- // setters
- void setEnabled(bool value);
-
- // internals
- bool hit(int type, offs_t address, int size);
-
- private:
- device_debug * m_debugInterface; // the interface we were created from
- memory_passthrough_handler *m_phr; // passthrough handler reference, read access
- memory_passthrough_handler *m_phw; // passthrough handler reference, write access
- address_space & m_space; // address space
- int m_index; // user reported index
- bool m_enabled; // enabled?
- read_or_write m_type; // type (read/write)
- offs_t m_address; // start address
- offs_t m_length; // length of watch area
- parsed_expression m_condition; // condition
- std::string m_action; // action
- int m_notifier; // address map change notifier id
-
- offs_t m_start_address[3]; // the start addresses of the checks to install
- offs_t m_end_address[3]; // the end addresses
- u64 m_masks[3]; // the access masks
- bool m_installing; // prevent recursive multiple installs
- void install(read_or_write mode);
- void triggered(read_or_write type, offs_t address, u64 data, u64 mem_mask);
- };
-
- // registerpoint class
- class registerpoint
- {
- friend class device_debug;
-
- public:
- // construction/destruction
- registerpoint(symbol_table &symbols, int index, const char *condition, const char *action = nullptr);
-
- // getters
- int index() const { return m_index; }
- bool enabled() const { return m_enabled; }
- const char *condition() const { return m_condition.original_string(); }
- const char *action() const { return m_action.c_str(); }
-
- private:
- // internals
- bool hit();
-
- int m_index; // user reported index
- bool m_enabled; // enabled?
- parsed_expression m_condition; // condition
- std::string m_action; // action
- };
-
-public:
// construction/destruction
device_debug(device_t &device);
~device_debug();
// getters
- symbol_table &symtable() { return m_symtable; }
+ symbol_table &symtable() { return *m_symtable; }
// commonly-used pass-throughs
int logaddrchars() const { return (m_memory != nullptr && m_memory->has_space(AS_PROGRAM)) ? m_memory->space(AS_PROGRAM).logaddrchars() : 8; }
@@ -176,13 +51,11 @@ public:
// hooks used by the rest of the system
void start_hook(const attotime &endtime);
void stop_hook();
- void interrupt_hook(int irqline);
+ void interrupt_hook(int irqline, offs_t pc);
void exception_hook(int exception);
void privilege_hook();
void instruction_hook(offs_t curpc);
-
- // hooks into our operations
- void set_instruction_hook(debug_instruction_hook_func hook);
+ void wait_hook();
// debugger focus
void ignore(bool ignore = true);
@@ -201,9 +74,10 @@ public:
void go(offs_t targetpc = ~0);
void go_vblank();
void go_interrupt(int irqline = -1);
- void go_exception(int exception);
+ void go_exception(int exception, const char *condition);
void go_milliseconds(u64 milliseconds);
void go_privilege(const char *condition);
+ void go_branch(bool sense, const char *condition);
void go_next_device();
template <typename Format, typename... Params>
@@ -213,37 +87,41 @@ public:
}
// breakpoints
- const std::forward_list<breakpoint> &breakpoint_list() const { return m_bplist; }
- const breakpoint *breakpoint_find(offs_t address) const;
- int breakpoint_set(offs_t address, const char *condition = nullptr, const char *action = nullptr);
+ const auto &breakpoint_list() const { return m_bplist; }
+ const debug_breakpoint *breakpoint_find(offs_t address) const;
+ int breakpoint_set(offs_t address, const char *condition = nullptr, std::string_view action = {});
bool breakpoint_clear(int index);
void breakpoint_clear_all();
bool breakpoint_enable(int index, bool enable = true);
void breakpoint_enable_all(bool enable = true);
- breakpoint *triggered_breakpoint(void) { breakpoint *ret = m_triggered_breakpoint; m_triggered_breakpoint = nullptr; return ret; }
+ debug_breakpoint *triggered_breakpoint() { debug_breakpoint *ret = m_triggered_breakpoint; m_triggered_breakpoint = nullptr; return ret; }
// watchpoints
int watchpoint_space_count() const { return m_wplist.size(); }
- const std::vector<std::unique_ptr<watchpoint>> &watchpoint_vector(int spacenum) const { return m_wplist[spacenum]; }
- int watchpoint_set(address_space &space, read_or_write type, offs_t address, offs_t length, const char *condition, const char *action);
+ const std::vector<std::unique_ptr<debug_watchpoint>> &watchpoint_vector(int spacenum) const { return m_wplist[spacenum]; }
+ int watchpoint_set(address_space &space, read_or_write type, offs_t address, offs_t length, const char *condition = nullptr, std::string_view action = {});
bool watchpoint_clear(int wpnum);
void watchpoint_clear_all();
bool watchpoint_enable(int index, bool enable = true);
void watchpoint_enable_all(bool enable = true);
- void set_triggered_watchpoint(watchpoint *wp) { m_triggered_watchpoint = wp; }
- watchpoint *triggered_watchpoint(void) { watchpoint *ret = m_triggered_watchpoint; m_triggered_watchpoint = nullptr; return ret; }
+ void set_triggered_watchpoint(debug_watchpoint *wp) { m_triggered_watchpoint = wp; }
+ debug_watchpoint *triggered_watchpoint() { debug_watchpoint *ret = m_triggered_watchpoint; m_triggered_watchpoint = nullptr; return ret; }
// registerpoints
- const std::forward_list<registerpoint> &registerpoint_list() const { return m_rplist; }
- int registerpoint_set(const char *condition, const char *action = nullptr);
+ const std::forward_list<debug_registerpoint> &registerpoint_list() const { return m_rplist; }
+ int registerpoint_set(const char *condition, std::string_view action = {});
bool registerpoint_clear(int index);
void registerpoint_clear_all();
bool registerpoint_enable(int index, bool enable = true);
- void registerpoint_enable_all(bool enable = true );
+ void registerpoint_enable_all(bool enable = true);
- // hotspots
- bool hotspot_tracking_enabled() const { return !m_hotspots.empty(); }
- void hotspot_track(int numspots, int threshhold);
+ // exception points
+ const auto &exceptionpoint_list() const { return m_eplist; }
+ int exceptionpoint_set(int exception, const char *condition = nullptr, std::string_view action = {});
+ bool exceptionpoint_clear(int index);
+ void exceptionpoint_clear_all();
+ bool exceptionpoint_enable(int index, bool enable = true);
+ void exceptionpoint_enable_all(bool enable = true);
// comments
void comment_add(offs_t address, const char *comment, rgb_t color);
@@ -256,24 +134,28 @@ public:
u32 compute_opcode_crc32(offs_t pc) const;
// history
- offs_t history_pc(int index) const;
+ std::pair<offs_t, bool> history_pc(int index) const;
// pc tracking
- void set_track_pc(bool value) { m_track_pc = value; }
- bool track_pc_visited(const offs_t& pc) const;
- void set_track_pc_visited(const offs_t& pc);
+ void set_track_pc(bool value);
+ bool track_pc_visited(offs_t pc) const;
+ void set_track_pc_visited(offs_t pc);
void track_pc_data_clear() { m_track_pc_set.clear(); }
// memory tracking
- void set_track_mem(bool value) { m_track_mem = value; }
+ void set_track_mem(bool value);
offs_t track_mem_pc_from_space_address_data(const int& space,
const offs_t& address,
const u64& data) const;
void track_mem_data_clear() { m_track_mem_set.clear(); }
// tracing
- void trace(FILE *file, bool trace_over, bool detect_loops, bool logerror, const char *action);
- void trace_printf(const char *fmt, ...) ATTR_PRINTF(2,3);
+ void trace(std::unique_ptr<std::ostream> &&file, bool trace_over, bool detect_loops, bool logerror, std::string_view action);
+ template <typename Format, typename... Params> void trace_printf(Format &&fmt, Params &&...args)
+ {
+ if (m_trace != nullptr)
+ m_trace->vprintf(util::make_format_argument_pack(std::forward<Format>(fmt), std::forward<Params>(args)...));
+ }
void trace_flush() { if (m_trace != nullptr) m_trace->flush(); }
void reset_transient_flag() { m_flags &= ~DEBUG_FLAG_TRANSIENT; }
@@ -284,7 +166,7 @@ public:
void compute_debug_flags();
private:
- void halt_on_next_instruction_impl(util::format_argument_pack<std::ostream> &&args);
+ void halt_on_next_instruction_impl(util::format_argument_pack<char> &&args);
// internal helpers
void prepare_for_step_overout(offs_t pc);
@@ -293,19 +175,11 @@ private:
// breakpoint and watchpoint helpers
void breakpoint_update_flags();
void breakpoint_check(offs_t pc);
- void hotspot_check(address_space &space, offs_t address);
+ void registerpoint_check();
void reinstall_all(read_or_write mode);
void reinstall(address_space &space, read_or_write mode);
void write_tracking(address_space &space, offs_t address, u64 data);
- // symbol get/set callbacks
- static u64 get_current_pc(symbol_table &table);
- static u64 get_cycles(symbol_table &table);
- static u64 get_totalcycles(symbol_table &table);
- static u64 get_lastinstructioncycles(symbol_table &table);
- static u64 get_state(symbol_table &table, int index);
- static void set_state(symbol_table &table, int index, u64 value);
-
// basic device information
device_t & m_device; // device we are attached to
device_execute_interface * m_exec; // execute interface, if present
@@ -315,44 +189,49 @@ private:
// global state
u32 m_flags; // debugging flags for this CPU
- symbol_table m_symtable; // symbol table for expression evaluation
- debug_instruction_hook_func m_instrhook; // per-instruction callback hook
+ std::unique_ptr<symbol_table> m_symtable; // symbol table for expression evaluation
// stepping information
- offs_t m_stepaddr; // step target address for DEBUG_FLAG_STEPPING_OVER
+ offs_t m_stepaddr; // step target address for DEBUG_FLAG_STEPPING_OVER or DEBUG_FLAG_STEPPING_BRANCH
int m_stepsleft; // number of steps left until done
+ int m_delay_steps; // number of steps until target address check
// execution information
offs_t m_stopaddr; // stop address for DEBUG_FLAG_STOP_PC
attotime m_stoptime; // stop time for DEBUG_FLAG_STOP_TIME
int m_stopirq; // stop IRQ number for DEBUG_FLAG_STOP_INTERRUPT
int m_stopexception; // stop exception number for DEBUG_FLAG_STOP_EXCEPTION
- std::unique_ptr<parsed_expression> m_privilege_condition; // expression to evaluate on privilege change
+ std::unique_ptr<parsed_expression> m_stop_condition; // expression to evaluate on privilege change
+ std::unique_ptr<parsed_expression> m_exception_condition; // expression to evaluate on exception hit
attotime m_endexectime; // ending time of the current execution
u64 m_total_cycles; // current total cycles
u64 m_last_total_cycles; // last total cycles
+ bool m_was_waiting; // true if no instruction executed since last wait
// history
offs_t m_pc_history[HISTORY_SIZE]; // history of recent PCs
u32 m_pc_history_index; // current history index
+ u32 m_pc_history_valid; // number of valid PC history entries
// breakpoints and watchpoints
- std::forward_list<breakpoint> m_bplist; // list of breakpoints
- std::vector<std::vector<std::unique_ptr<watchpoint>>> m_wplist; // watchpoint lists for each address space
- std::forward_list<registerpoint> m_rplist; // list of registerpoints
+ std::multimap<offs_t, std::unique_ptr<debug_breakpoint>> m_bplist; // list of breakpoints
+ std::vector<std::vector<std::unique_ptr<debug_watchpoint>>> m_wplist; // watchpoint lists for each address space
+ std::forward_list<debug_registerpoint> m_rplist; // list of registerpoints
+ std::multimap<offs_t, std::unique_ptr<debug_exceptionpoint>> m_eplist; // list of exception points
- breakpoint * m_triggered_breakpoint; // latest breakpoint that was triggered
- watchpoint * m_triggered_watchpoint; // latest watchpoint that was triggered
+ debug_breakpoint * m_triggered_breakpoint; // latest breakpoint that was triggered
+ debug_watchpoint * m_triggered_watchpoint; // latest watchpoint that was triggered
// tracing
class tracer
{
public:
- tracer(device_debug &debug, FILE &file, bool trace_over, bool detect_loops, bool logerror, const char *action);
+ tracer(device_debug &debug, std::unique_ptr<std::ostream> &&file, bool trace_over, bool detect_loops, bool logerror, std::string_view action);
~tracer();
void update(offs_t pc);
- void vprintf(const char *format, va_list va);
+ void interrupt_update(int irqline, offs_t pc);
+ void vprintf(util::format_argument_pack<char> const &args);
void flush();
bool logerror() const { return m_logerror; }
@@ -360,7 +239,7 @@ private:
static const int TRACE_LOOPS = 64;
device_debug & m_debug; // reference to our owner
- FILE & m_file; // tracing file for this CPU
+ std::unique_ptr<std::ostream> m_file; // tracing file for this CPU
std::string m_action; // action to perform during a trace
offs_t m_history[TRACE_LOOPS]; // history of recent PCs
bool m_detect_loops; // whether or not we should detect loops
@@ -372,22 +251,10 @@ private:
// (0 = not tracing over,
// ~0 = not currently tracing over)
};
- std::unique_ptr<tracer> m_trace; // tracer state
+ std::unique_ptr<tracer> m_trace; // tracer state
- // hotspots
- struct hotspot_entry
- {
- offs_t m_access; // access address
- offs_t m_pc; // PC of the access
- address_space * m_space; // space where the access occurred
- u32 m_count; // number of hits
- };
- std::vector<hotspot_entry> m_hotspots; // hotspot list
- int m_hotspot_threshhold; // threshhold for the number of hits to print
-
- std::vector<memory_passthrough_handler *> m_phr; // passthrough handler reference for each space, read mode
- std::vector<memory_passthrough_handler *> m_phw; // passthrough handler reference for each space, write mode
- std::vector<int> m_notifiers; // notifiers for each space
+ std::vector<memory_passthrough_handler> m_phw; // passthrough handler reference for each space, write mode
+ std::vector<util::notifier_subscription> m_notifiers; // notifiers for each space
// pc tracking
class dasm_pc_tag
@@ -455,7 +322,6 @@ private:
static constexpr u32 DEBUG_FLAG_HISTORY = 0x00000002; // tracking this CPU's history
static constexpr u32 DEBUG_FLAG_TRACING = 0x00000004; // tracing this CPU
static constexpr u32 DEBUG_FLAG_TRACING_OVER = 0x00000008; // tracing this CPU with step over behavior
- static constexpr u32 DEBUG_FLAG_HOOKED = 0x00000010; // per-instruction callback hook
static constexpr u32 DEBUG_FLAG_STEPPING = 0x00000020; // CPU is single stepping
static constexpr u32 DEBUG_FLAG_STEPPING_OVER = 0x00000040; // CPU is stepping over a function
static constexpr u32 DEBUG_FLAG_STEPPING_OUT = 0x00000080; // CPU is stepping out of a function
@@ -465,14 +331,20 @@ private:
static constexpr u32 DEBUG_FLAG_STOP_VBLANK = 0x00001000; // there is a pending stop on the next VBLANK
static constexpr u32 DEBUG_FLAG_STOP_TIME = 0x00002000; // there is a pending stop at cpu->stoptime
static constexpr u32 DEBUG_FLAG_SUSPENDED = 0x00004000; // CPU currently suspended
- static constexpr u32 DEBUG_FLAG_LIVE_BP = 0x00010000; // there are live breakpoints for this CPU
+ static constexpr u32 DEBUG_FLAG_LIVE_BP = 0x00008000; // there are live breakpoints for this CPU
+ static constexpr u32 DEBUG_FLAG_LIVE_RP = 0x00010000; // there are live registerpoints for this CPU
static constexpr u32 DEBUG_FLAG_STOP_PRIVILEGE = 0x00020000; // run until execution level changes
+ static constexpr u32 DEBUG_FLAG_STEPPING_BRANCH_TRUE = 0x0040000; // run until true branch
+ static constexpr u32 DEBUG_FLAG_STEPPING_BRANCH_FALSE = 0x0080000; // run until false branch
+ static constexpr u32 DEBUG_FLAG_CALL_IN_PROGRESS = 0x01000000; // CPU is in the middle of a subroutine call
+ static constexpr u32 DEBUG_FLAG_TEST_IN_PROGRESS = 0x02000000; // CPU is performing a conditional test and branch
- static constexpr u32 DEBUG_FLAG_STEPPING_ANY = DEBUG_FLAG_STEPPING | DEBUG_FLAG_STEPPING_OVER | DEBUG_FLAG_STEPPING_OUT;
+ static constexpr u32 DEBUG_FLAG_STEPPING_BRANCH = DEBUG_FLAG_STEPPING_BRANCH_TRUE | DEBUG_FLAG_STEPPING_BRANCH_FALSE;
+ static constexpr u32 DEBUG_FLAG_STEPPING_ANY = DEBUG_FLAG_STEPPING | DEBUG_FLAG_STEPPING_OVER | DEBUG_FLAG_STEPPING_OUT | DEBUG_FLAG_STEPPING_BRANCH;
static constexpr u32 DEBUG_FLAG_TRACING_ANY = DEBUG_FLAG_TRACING | DEBUG_FLAG_TRACING_OVER;
static constexpr u32 DEBUG_FLAG_TRANSIENT = DEBUG_FLAG_STEPPING_ANY | DEBUG_FLAG_STOP_PC |
DEBUG_FLAG_STOP_INTERRUPT | DEBUG_FLAG_STOP_EXCEPTION | DEBUG_FLAG_STOP_VBLANK |
- DEBUG_FLAG_STOP_TIME | DEBUG_FLAG_STOP_PRIVILEGE;
+ DEBUG_FLAG_STOP_TIME | DEBUG_FLAG_STOP_PRIVILEGE | DEBUG_FLAG_CALL_IN_PROGRESS | DEBUG_FLAG_TEST_IN_PROGRESS;
};
//**************************************************************************
@@ -491,14 +363,9 @@ public:
/* flushes all traces; this is useful if a trace is going on when we fatalerror */
void flush_traces();
- void configure_memory(symbol_table &table);
-
/* ----- debugging status & information ----- */
- /* return the visible CPU device (the one that commands should apply to) */
- device_t *get_visible_cpu() { return m_visiblecpu; }
-
/* return true if the current execution state is stopped */
bool is_stopped() const { return m_execution_state == exec_state::STOPPED; }
bool is_running() const { return m_execution_state == exec_state::RUNNING; }
@@ -507,10 +374,7 @@ public:
/* ----- symbol table interfaces ----- */
/* return the global symbol table */
- symbol_table *get_global_symtable() { return m_symtable.get(); }
-
- /* return the locally-visible symbol table */
- symbol_table *get_visible_symtable();
+ symbol_table &global_symtable() { return *m_symtable; }
/* ----- debugger comment helpers ----- */
@@ -522,41 +386,6 @@ public:
bool comment_load(bool is_inline);
- /* ----- debugger memory accessors ----- */
-
- /* return a byte from the specified memory space */
- u8 read_byte(address_space &space, offs_t address, bool apply_translation);
-
- /* return a word from the specified memory space */
- u16 read_word(address_space &space, offs_t address, bool apply_translation);
-
- /* return a dword from the specified memory space */
- u32 read_dword(address_space &space, offs_t address, bool apply_translation);
-
- /* return a qword from the specified memory space */
- u64 read_qword(address_space &space, offs_t address, bool apply_translation);
-
- /* return 1,2,4 or 8 bytes from the specified memory space */
- u64 read_memory(address_space &space, offs_t address, int size, bool apply_translation);
-
- /* write a byte to the specified memory space */
- void write_byte(address_space &space, offs_t address, u8 data, bool apply_translation);
-
- /* write a word to the specified memory space */
- void write_word(address_space &space, offs_t address, u16 data, bool apply_translation);
-
- /* write a dword to the specified memory space */
- void write_dword(address_space &space, offs_t address, u32 data, bool apply_translation);
-
- /* write a qword to the specified memory space */
- void write_qword(address_space &space, offs_t address, u64 data, bool apply_translation);
-
- /* write 1,2,4 or 8 bytes to the specified memory space */
- void write_memory(address_space &space, offs_t address, u64 data, int size, bool apply_translation);
-
- /* read 1,2,4 or 8 bytes at the given offset from opcode space */
- u64 read_opcode(address_space &space, offs_t offset, int size);
-
// getters
bool within_instruction_hook() const { return m_within_instruction_hook; }
bool memory_modified() const { return m_memory_modified; }
@@ -565,15 +394,15 @@ public:
u32 get_breakpoint_index() { return m_bpindex++; }
u32 get_watchpoint_index() { return m_wpindex++; }
u32 get_registerpoint_index() { return m_rpindex++; }
+ u32 get_exceptionpoint_index() { return m_epindex++; }
// setters
- void set_visible_cpu(device_t * visiblecpu) { m_visiblecpu = visiblecpu; }
void set_break_cpu(device_t * breakcpu) { m_breakcpu = breakcpu; }
void set_within_instruction(bool within_instruction) { m_within_instruction_hook = within_instruction; }
void set_memory_modified(bool memory_modified) { m_memory_modified = memory_modified; }
void set_execution_stopped() { m_execution_state = exec_state::STOPPED; }
void set_execution_running() { m_execution_state = exec_state::RUNNING; }
- void set_wpinfo(offs_t address, u64 data) { m_wpaddr = address; m_wpdata = data; }
+ void set_wpinfo(offs_t address, u64 data, offs_t size) { m_wpaddr = address; m_wpdata = data; m_wpsize = size; }
// device_debug helpers
// [TODO] [RH]: Look into this more later, can possibly merge these two classes
@@ -581,36 +410,20 @@ public:
void stop_hook(device_t *device);
void go_next_device(device_t *device);
void go_vblank();
- void halt_on_next_instruction(device_t *device, util::format_argument_pack<std::ostream> &&args);
+ void halt_on_next_instruction(device_t *device, util::format_argument_pack<char> &&args);
void ensure_comments_loaded();
void reset_transient_flags();
+ void wait_for_debugger(device_t &device);
private:
static const size_t NUM_TEMP_VARIABLES;
- /* expression handlers */
- u64 expression_read_memory(void *param, const char *name, expression_space space, u32 address, int size, bool disable_se);
- u64 expression_read_program_direct(address_space &space, int opcode, offs_t address, int size);
- u64 expression_read_memory_region(const char *rgntag, offs_t address, int size);
- void expression_write_memory(void *param, const char *name, expression_space space, u32 address, int size, u64 data, bool disable_se);
- void expression_write_program_direct(address_space &space, int opcode, offs_t address, int size, u64 data);
- void expression_write_memory_region(const char *rgntag, offs_t address, int size, u64 data);
- expression_error::error_code expression_validate(void *param, const char *name, expression_space space);
- device_t* expression_get_device(const char *tag);
-
- /* variable getters/setters */
- u64 get_cpunum(symbol_table &table);
- u64 get_beamx(symbol_table &table, screen_device *screen);
- u64 get_beamy(symbol_table &table, screen_device *screen);
- u64 get_frame(symbol_table &table, screen_device *screen);
-
- /* internal helpers */
+ // internal helpers
void on_vblank(screen_device &device, bool vblank_state);
running_machine& m_machine;
device_t * m_livecpu;
- device_t * m_visiblecpu;
device_t * m_breakcpu;
std::unique_ptr<symbol_table> m_symtable; // global symbol table
@@ -625,9 +438,11 @@ private:
u32 m_bpindex;
u32 m_wpindex;
u32 m_rpindex;
+ u32 m_epindex;
u64 m_wpdata;
u64 m_wpaddr;
+ u64 m_wpsize;
std::unique_ptr<u64[]> m_tempvar;
osd_ticks_t m_last_periodic_update_time;
diff --git a/src/emu/debug/debughlp.cpp b/src/emu/debug/debughlp.cpp
index d737c0376aa..8e5fa1b9c64 100644
--- a/src/emu/debug/debughlp.cpp
+++ b/src/emu/debug/debughlp.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Aaron Giles
/*********************************************************************
- debughlp.c
+ debughlp.cpp
Debugger help engine.
@@ -10,48 +10,28 @@
#include "emu.h"
#include "debughlp.h"
-#include <ctype.h>
+#include "corestr.h"
+#include <cstdio>
+#include <iterator>
+#include <map>
-/***************************************************************************
- CONSTANTS
-***************************************************************************/
-
-#define CONSOLE_HISTORY (10000)
-#define CONSOLE_LINE_CHARS (100)
+namespace {
/***************************************************************************
- TYPE DEFINITIONS
+ TABLE OF HELP
***************************************************************************/
struct help_item
{
- const char * tag;
- const char * help;
+ char const *tag;
+ char const *help;
};
-
-
-/***************************************************************************
- LOCAL VARIABLES
-***************************************************************************/
-
-
-
-/***************************************************************************
- FUNCTION PROTOTYPES
-***************************************************************************/
-
-
-
-/***************************************************************************
- TABLE OF HELP
-***************************************************************************/
-
-static const help_item static_help_list[] =
+const help_item f_static_help_list[] =
{
{
"",
@@ -66,6 +46,7 @@ static const help_item static_help_list[] =
" Breakpoints\n"
" Watchpoints\n"
" Registerpoints\n"
+ " Exceptionpoints\n"
" Expressions\n"
" Comments\n"
" Cheats\n"
@@ -88,18 +69,21 @@ static const help_item static_help_list[] =
" logerror <format>[,<item>[,...]] -- outputs one or more <item>s to the error.log\n"
" tracelog <format>[,<item>[,...]] -- outputs one or more <item>s to the trace file using <format>\n"
" tracesym <item>[,...]] -- outputs one or more <item>s to the trace file\n"
- " history [<CPU>,<length>] -- outputs a brief history of visited opcodes\n"
- " trackpc [<bool>,<CPU>,<bool>] -- visually track visited opcodes [boolean to turn on and off, for the given CPU, clear]\n"
- " trackmem [<bool>,<bool>] -- record which PC writes to each memory address [boolean to turn on and off, clear]\n"
- " pcatmemp <address>[,<CPU>] -- query which PC wrote to a given program memory address for the current CPU\n"
- " pcatmemd <address>[,<CPU>] -- query which PC wrote to a given data memory address for the current CPU\n"
- " pcatmemi <address>[,<CPU>] -- query which PC wrote to a given I/O memory address for the current CPU\n"
- " (Note: you can also query this info by right clicking in a memory window\n"
+ " history [<CPU>,[<length>]] -- outputs a brief history of visited opcodes\n"
+ " trackpc [<bool>,[<CPU>,[<bool>]]] -- visually track visited opcodes [boolean to turn on and off, for CPU, clear]\n"
+ " trackmem [<bool>,[<CPU>,[<bool>]]] -- record which PC writes to each memory address [boolean to turn on and off, for CPU, clear]\n"
+ " pcatmem <address>[:<space>] -- query which PC wrote to a given memory address\n"
+ " pcatmemd <address>[:<space>] -- query which PC wrote to a given data memory address\n"
+ " pcatmemi <address>[:<space>] -- query which PC wrote to a given I/O memory address\n"
+ " pcatmemo <address>[:<space>] -- query which PC wrote to a given opcode memory address\n"
+ " (Note: you can also query this info by right-clicking in a memory window)\n"
" rewind[rw] -- go back in time by loading the most recent rewind state"
" statesave[ss] <filename> -- save a state file for the current driver\n"
" stateload[sl] <filename> -- load a state file for the current driver\n"
" snap [<filename>] -- save a screen snapshot.\n"
" source <filename> -- reads commands from <filename> and executes them one by one\n"
+ " time -- prints current machine time to the console\n"
+ " cls -- clears the console text buffer\n"
" quit -- exits MAME and the debugger\n"
},
{
@@ -109,23 +93,36 @@ static const help_item static_help_list[] =
"Type help <command> for further details on each command\n"
"\n"
" dasm <filename>,<address>,<length>[,<opcodes>[,<CPU>]] -- disassemble to the given file\n"
- " f[ind] <address>,<length>[,<data>[,...]] -- search program memory for data\n"
+ " f[ind] <address>,<length>[,<data>[,...]] -- search memory for data\n"
" f[ind]d <address>,<length>[,<data>[,...]] -- search data memory for data\n"
" f[ind]i <address>,<length>[,<data>[,...]] -- search I/O memory for data\n"
- " dump <filename>,<address>,<length>[,<size>[,<ascii>[,<CPU>]]] -- dump program memory as text\n"
- " dumpd <filename>,<address>,<length>[,<size>[,<ascii>[,<CPU>]]] -- dump data memory as text\n"
- " dumpi <filename>,<address>,<length>[,<size>[,<ascii>[,<CPU>]]] -- dump I/O memory as text\n"
- " dumpo <filename>,<address>,<length>[,<size>[,<ascii>[,<CPU>]]] -- dump opcodes memory as text\n"
- " save <filename>,<address>,<length>[,<CPU>] -- save binary program memory to the given file\n"
- " saved <filename>,<address>,<length>[,<CPU>] -- save binary data memory to the given file\n"
- " savei <filename>,<address>,<length>[,<CPU>] -- save binary I/O memory to the given file\n"
- " load <filename>,<address>[,<length>,<CPU>] -- load binary program memory from the given file\n"
- " loadd <filename>,<address>[,<length>,<CPU>] -- load binary data memory from the given file\n"
- " loadi <filename>,<address>[,<length>,<CPU>] -- load binary I/O memory from the given file\n"
- " map <address> -- map logical program address to physical address and bank\n"
- " mapd <address> -- map logical data address to physical address and bank\n"
- " mapi <address> -- map logical I/O address to physical address and bank\n"
- " memdump [<filename>] -- dump the current memory map to <filename>\n"
+ " fill <address>,<length>[,<data>[,...]] -- fill memory with data\n"
+ " filld <address>[:<space>],<length>[,<data>[,...]] -- fill data memory with data\n"
+ " filli <address>[:<space>],<length>[,<data>[,...][ -- fill I/O memory with data\n"
+ " fillo <address>[:<space>],<length>[,<data>[,...][ -- fill opcode memory with data\n"
+ " dump <filename>,<address>[:<space>],<length>[,<group>[,<ascii>[,<rowsize>]]] -- dump memory as text\n"
+ " dumpd <filename>,<address>[:<space>],<length>[,<group>[,<ascii>[,<rowsize>]]] -- dump data memory as text\n"
+ " dumpi <filename>,<address>[:<space>],<length>[,<group>[,<ascii>[,<rowsize>]]] -- dump I/O memory as text\n"
+ " dumpo <filename>,<address>[:<space>],<length>[,<group>[,<ascii>[,<rowsize>]]] -- dump opcodes memory as text\n"
+ " strdump <filename>,<address>[:<space>],<length>[,<term>] -- dump ASCII strings from memory\n"
+ " strdumpd <filename>,<address>[:<space>],<length>[,<term>] -- dump ASCII strings from data memory\n"
+ " strdumpi <filename>,<address>[:<space>],<length>[,<term>] -- dump ASCII strings from I/O memory\n"
+ " strdumpo <filename>,<address>[:<space>],<length>[,<term>] -- dump ASCII strings from opcodes memory\n"
+ " save <filename>,<address>[:<space>],<length> -- save binary memory to the given file\n"
+ " saved <filename>,<address>[:<space>],<length> -- save binary data memory to the given file\n"
+ " savei <filename>,<address>[:<space>],<length> -- save binary I/O memory to the given file\n"
+ " saveo <filename>,<address>[:<space>],<length> -- save binary opcode memory to the given file\n"
+ " saver <filename>,<address>[:<space>],<length>,<region> -- save binary memory region to the given file\n"
+ " load <filename>,<address>[:<space>][,<length>] -- load binary memory from the given file\n"
+ " loadd <filename>,<address>[:<space>][,<length>] -- load binary data memory from the given file\n"
+ " loadi <filename>,<address>[:<space>][,<length>] -- load binary I/O memory from the given file\n"
+ " loado <filename>,<address>[:<space>][,<length>] -- load binary opcode memory from the given file\n"
+ " loadr <filename>,<address>[:<space>],<length>,<region> -- load binary memory region from the given file\n"
+ " map <address>[:<space>] -- map logical address to physical address and bank\n"
+ " mapd <address>[:<space>] -- map logical data address to physical address and bank\n"
+ " mapi <address>[:<space>] -- map logical I/O address to physical address and bank\n"
+ " mapo <address>[:<space>] -- map logical opcode address to physical address and bank\n"
+ " memdump [<filename>,[<root>]] -- dump current memory maps to <filename>\n"
},
{
"execution",
@@ -137,8 +134,12 @@ static const help_item static_help_list[] =
" o[ver] [<count>=1] -- single steps over <count> instructions (F10)\n"
" out -- single steps until the current subroutine/exception handler is exited (Shift-F11)\n"
" g[o] [<address>] -- resumes execution, sets temp breakpoint at <address> (F5)\n"
- " ge[x] [<exception>] -- resumes execution, setting temp breakpoint if <exception> is raised\n"
+ " gbf [<condition>] -- resumes execution until next false branch\n"
+ " gbt [<condition>] -- resumes execution until next true branch\n"
+ " gn[i] [<count>] -- resumes execution, sets temp breakpoint <count> instructions ahead\n"
+ " ge[x] [<exception>[,<condition>]] -- resumes execution, setting temp breakpoint if <exception> is raised\n"
" gi[nt] [<irqline>] -- resumes execution, setting temp breakpoint if <irqline> is taken (F7)\n"
+ " gp [<condition>] -- resumes execution, setting temp breakpoint if privilege level changes\n"
" gt[ime] <milliseconds> -- resumes execution until the given delay has elapsed\n"
" gv[blank] -- resumes execution, setting temp breakpoint on the next VBLANK (F8)\n"
" n[ext] -- executes until the next CPU switch (F6)\n"
@@ -147,6 +148,7 @@ static const help_item static_help_list[] =
" observe [<CPU>[,<CPU>[,...]]] -- resumes debugging on <CPU>\n"
" suspend [<CPU>[,<CPU>[,...]]] -- suspends execution on <CPU>\n"
" resume [<CPU>[,<CPU>[,...]]] -- resumes execution on <CPU>\n"
+ " cpulist -- list all CPUs\n"
" trace {<filename>|OFF}[,<CPU>[,<detectloops>[,<action>]]] -- trace the given CPU to a file (defaults to active CPU)\n"
" traceover {<filename>|OFF}[,<CPU>[,<detectloops>[,<action>]]] -- trace the given CPU to a file, but skip subroutines (defaults to active CPU)\n"
" traceflush -- flushes all open trace files\n"
@@ -157,11 +159,11 @@ static const help_item static_help_list[] =
"Breakpoint Commands\n"
"Type help <command> for further details on each command\n"
"\n"
- " bp[set] <address>[,<condition>[,<action>]] -- sets breakpoint at <address>\n"
- " bpclear [<bpnum>] -- clears a given breakpoint or all if no <bpnum> specified\n"
- " bpdisable [<bpnum>] -- disables a given breakpoint or all if no <bpnum> specified\n"
- " bpenable [<bpnum>] -- enables a given breakpoint or all if no <bpnum> specified\n"
- " bplist -- lists all the breakpoints\n"
+ " bp[set] <address>[:<CPU>][,<condition>[,<action>]] -- sets breakpoint at <address>\n"
+ " bpclear [<bpnum>[,...]] -- clears given breakpoints or all if no <bpnum> specified\n"
+ " bpdisable [<bpnum>[,...]] -- disables given breakpoints or all if no <bpnum> specified\n"
+ " bpenable [<bpnum>[,...]] -- enables given breakpoints or all if no <bpnum> specified\n"
+ " bplist [<CPU>] -- lists all the breakpoints\n"
},
{
"watchpoints",
@@ -169,14 +171,14 @@ static const help_item static_help_list[] =
"Watchpoint Commands\n"
"Type help <command> for further details on each command\n"
"\n"
- " wp[set] <address>,<length>,<type>[,<condition>[,<action>]] -- sets program space watchpoint\n"
- " wpd[set] <address>,<length>,<type>[,<condition>[,<action>]] -- sets data space watchpoint\n"
- " wpi[set] <address>,<length>,<type>[,<condition>[,<action>]] -- sets I/O space watchpoint\n"
- " wpclear [<wpnum>] -- clears a given watchpoint or all if no <wpnum> specified\n"
- " wpdisable [<wpnum>] -- disables a given watchpoint or all if no <wpnum> specified\n"
- " wpenable [<wpnum>] -- enables a given watchpoint or all if no <wpnum> specified\n"
- " wplist -- lists all the watchpoints\n"
- " hotspot [<CPU>,[<depth>[,<hits>]]] -- attempt to find hotspots\n"
+ " wp[set] <address>[:<space>],<length>,<type>[,<condition>[,<action>]] -- sets watchpoint\n"
+ " wpd[set] <address>[:<space>],<length>,<type>[,<condition>[,<action>]] -- sets data space watchpoint\n"
+ " wpi[set] <address>[:<space>],<length>,<type>[,<condition>[,<action>]] -- sets I/O space watchpoint\n"
+ " wpo[set] <address>[:<space>],<length>,<type>[,<condition>[,<action>]] -- sets opcode space watchpoint\n"
+ " wpclear [<wpnum>[,...]] -- clears given watchpoints or all if no <wpnum> specified\n"
+ " wpdisable [<wpnum>[,...]] -- disables given watchpoints or all if no <wpnum> specified\n"
+ " wpenable [<wpnum>[,...]] -- enables given watchpoints or all if no <wpnum> specified\n"
+ " wplist [<CPU>] -- lists all the watchpoints\n"
},
{
"registerpoints",
@@ -184,11 +186,23 @@ static const help_item static_help_list[] =
"Registerpoint Commands\n"
"Type help <command> for further details on each command\n"
"\n"
- " rp[set] {<condition>}[,<action>] -- sets a registerpoint to trigger on <condition>\n"
- " rpclear [<rpnum>] -- clears a given registerpoint or all if no <rpnum> specified\n"
- " rpdisable [<rpnum>] -- disabled a given registerpoint or all if no <rpnum> specified\n"
- " rpenable [<rpnum>] -- enables a given registerpoint or all if no <rpnum> specified\n"
- " rplist -- lists all the registerpoints\n"
+ " rp[set] <condition>[,<action>] -- sets a registerpoint to trigger on <condition>\n"
+ " rpclear [<rpnum>[,...]] -- clears given registerpoints or all if no <rpnum> specified\n"
+ " rpdisable [<rpnum>[,...]] -- disabled given registerpoints or all if no <rpnum> specified\n"
+ " rpenable [<rpnum>[,...]] -- enables given registerpoints or all if no <rpnum> specified\n"
+ " rplist [<CPU>] -- lists all the registerpoints\n"
+ },
+ {
+ "exceptionpoints",
+ "\n"
+ "Exceptionpoint Commands\n"
+ "Type help <command> for further details on each command\n"
+ "\n"
+ " ep[set] <type>[,<condition>[,<action>]] -- sets exceptionpoint on <type>\n"
+ " epclear [<epnum>] -- clears a given exceptionpoint or all if no <epnum> specified\n"
+ " epdisable [<epnum>] -- disabled a given exceptionpoint or all if no <epnum> specified\n"
+ " epenable [<epnum>] -- enables a given exceptionpoint or all if no <epnum> specified\n"
+ " eplist -- lists all the exceptionpoints\n"
},
{
"expressions",
@@ -250,9 +264,9 @@ static const help_item static_help_list[] =
"Cheat Commands\n"
"Type help <command> for further details on each command\n"
"\n"
- " cheatinit [<address>,<length>[,<CPU>]] -- initialize the cheat search to the selected memory area\n"
- " cheatrange <address>,<length> -- add to the cheat search the selected memory area\n"
- " cheatnext <condition>[,<comparisonvalue>] -- continue cheat search comparing with the last value\n"
+ " cheatinit [[<sign>[<width>[<swap>]]],[<address>,<length>[,<space>]]] -- initialize the cheat search to the selected memory area\n"
+ " cheatrange <address>,<length> -- add selected memory area to the cheat search\n"
+ " cheatnext <condition>[,<comparisonvalue>] -- continue cheat search comparing with the previous value\n"
" cheatnextf <condition>[,<comparisonvalue>] -- continue cheat search comparing with the first value\n"
" cheatlist [<filename>] -- show the list of cheat search matches or save them to <filename>\n"
" cheatundo -- undo the last cheat search (state only)\n"
@@ -263,9 +277,9 @@ static const help_item static_help_list[] =
"Image Commands\n"
"Type help <command> for further details on each command\n"
"\n"
- " images -- lists all image devices and mounted files\n"
- " mount <device>,<filename> -- mounts file to named device\n"
- " unmount <device> -- unmounts file from named device\n"
+ " images -- lists all image devices and mounted mounted media\n"
+ " mount <instance>,<filename> -- mounts file to specified device\n"
+ " unmount <instance> -- unmounts media from specified device\n"
},
{
"do",
@@ -345,16 +359,20 @@ static const help_item static_help_list[] =
"The printf command performs a C-style printf to the debugger console. Only a very limited set of "
"formatting options are available:\n"
"\n"
- " %[0][<n>]d -- prints <item> as a decimal value with optional digit count and zero-fill\n"
- " %[0][<n>]x -- prints <item> as a hexadecimal value with optional digit count and zero-fill\n"
+ " %c -- 8-bit character\n"
+ " %[-][0][<n>]d -- decimal number with optional left justification, zero fill and minimum width\n"
+ " %[-][0][<n>]o -- octal number with optional left justification, zero fill and minimum width\n"
+ " %[-][0][<n>]x -- lowercase hexadecimal number with optional left justification, zero fill and minimum width\n"
+ " %[-][0][<n>]X -- uppercase hexadecimal number with optional left justification, zero fill and minimum width\n"
+ " %[-][<n>][.[<n>]]s -- null-terminated string of 8-bit characters with optional left justification, minimum and maximum width\n"
"\n"
- "All remaining formatting options are ignored. Use %% together to output a % character. Multiple "
- "lines can be printed by embedding a \\n in the text.\n"
+ "All remaining formatting options are ignored. Use %% to output a % character. Multiple lines can be "
+ "printed by embedding a \\n in the text.\n"
"\n"
"Examples:\n"
"\n"
"printf \"PC=%04X\",pc\n"
- " Prints PC=<pcval> where <pcval> is displayed in hexadecimal with 4 digits with zero-fill.\n"
+ " Prints PC=<pcval> where <pcval> is displayed in uppercase hexadecimal with 4 digits and zero fill.\n"
"\n"
"printf \"A=%d, B=%d\\nC=%d\",a,b,a+b\n"
" Prints A=<aval>, B=<bval> on one line, and C=<a+bval> on a second line.\n"
@@ -365,18 +383,12 @@ static const help_item static_help_list[] =
" logerror <format>[,<item>[,...]]\n"
"\n"
"The logerror command performs a C-style printf to the error log. Only a very limited set of "
- "formatting options are available:\n"
- "\n"
- " %[0][<n>]d -- logs <item> as a decimal value with optional digit count and zero-fill\n"
- " %[0][<n>]x -- logs <item> as a hexadecimal value with optional digit count and zero-fill\n"
- "\n"
- "All remaining formatting options are ignored. Use %% together to output a % character. Multiple "
- "lines can be printed by embedding a \\n in the text.\n"
+ "formatting options are available. See the 'printf' help for details.\n"
"\n"
"Examples:\n"
"\n"
- "logerror \"PC=%04X\",pc\n"
- " Logs PC=<pcval> where <pcval> is displayed in hexadecimal with 4 digits with zero-fill.\n"
+ "logerror \"PC=%04x\",pc\n"
+ " Logs PC=<pcval> where <pcval> is displayed in lowercase hexadecimal with 4 digits and zero fill.\n"
"\n"
"logerror \"A=%d, B=%d\\nC=%d\",a,b,a+b\n"
" Logs A=<aval>, B=<bval> on one line, and C=<a+bval> on a second line.\n"
@@ -395,31 +407,54 @@ static const help_item static_help_list[] =
"tracelog \"PC=%04X\",pc\n"
" Outputs PC=<pcval> where <pcval> is displayed in hexadecimal with 4 digits with zero-fill.\n"
"\n"
- "printf \"A=%d, B=%d\\nC=%d\",a,b,a+b\n"
+ "tracelog \"A=%d, B=%d\\nC=%d\",a,b,a+b\n"
" Outputs A=<aval>, B=<bval> on one line, and C=<a+bval> on a second line.\n"
},
{
"tracesym",
"\n"
" tracesym <item>[,...]\n"
- "\n"
- "The tracesym command prints the specified symbols and routes the output to the currently open trace "
- "file (see the 'trace' command for details). If no file is currently open, tracesym does nothing. "
- "\n"
- "Examples:\n"
- "\n"
- "tracelog pc\n"
- " Outputs PC=<pcval> where <pcval> is displayed in the default format.\n"
+ "\n"
+ "The tracesym command prints the specified symbols and routes the output to the currently open trace "
+ "file (see the 'trace' command for details). If no file is currently open, tracesym does nothing. "
+ "\n"
+ "Examples:\n"
+ "\n"
+ "tracesym pc\n"
+ " Outputs PC=<pcval> where <pcval> is displayed in the default format.\n"
+ },
+ {
+ "history",
+ "\n"
+ " history [<CPU>,[<length>]]\n"
+ "\n"
+ "The history command displays recently visited PC addresses, and the disassembly of the "
+ "instructions at those addresses. If present, the first argument is a CPU selector "
+ "(either a tag or a CPU number); if no CPU is specified, the current CPU is assumed. "
+ "The second argument, if present, limits the maximum number of addresses shown. "
+ "Addresses are shown in order from least to most recently visited.\n"
+ "\n"
+ "Examples:\n"
+ "\n"
+ "history ,5\n"
+ " Displays up to five most recently visited PC addresses for the current CPU.\n"
+ "\n"
+ "history 3\n"
+ " Displays recently visited PC addresses for CPU 3.\n"
+ "\n"
+ "history audiocpu,1\n"
+ " Displays the most recently visited PC addresses for the CPU ':audiocpu'.\n"
},
{
"trackpc",
"\n"
- " trackpc [<bool>,<CPU>,<bool>]\n"
+ " trackpc [<bool>,[<CPU>,[<bool>]]]\n"
"\n"
- "The trackpc command displays which program counters have already been visited in all disassembler "
- "windows. The first boolean argument toggles the process on and off. The second argument is a "
- "CPU selector; if no CPU is specified, the current CPU is automatically selected. The third argument "
- "is a boolean denoting if the existing data should be cleared or not.\n"
+ "The trackpc command displays which program counters have already been visited in all "
+ "disassembler views. The first Boolean argument toggles the process on and off. The "
+ "second argument is a CPU selector (either a tag or a debugger CPU number); if no CPU is "
+ "specified, the current CPU is assumed. The third argument is a Boolean indicating "
+ "whether the existing data should be cleared.\n"
"\n"
"Examples:\n"
"\n"
@@ -432,38 +467,53 @@ static const help_item static_help_list[] =
{
"trackmem",
"\n"
- " trackmem [<bool>,<CPU>,<bool>]\n"
+ " trackmem [<bool>,[<CPU>,[<bool>]]]\n"
"\n"
"The trackmem command logs the PC at each time a memory address is written to. "
- "The first boolean argument toggles the process on and off. The second argument is a CPU "
- "selector; if no CPU is specified, the current CPU is automatically selected. The third argument "
- " is a boolean denoting if the existing data should be cleared or not. Please refer to the "
- "pcatmem command for information on how to retrieve this data. Also, right clicking in "
- "a memory window will display the logged PC for the given address.\n"
+ "The first Boolean argument toggles the process on and off. The second argument is a CPU "
+ "selector (either a tag or a debugger CPU number); if no CPU is specified, the current CPU "
+ "is assumed. The third argument is a Boolean indicating whether the existing data should "
+ "be cleared. Please refer to the 'pcatmem' command for information on how to retrieve this "
+ "data. Also, right-clicking in a memory view will display the logged PC for the given "
+ "address.\n"
"\n"
"Examples:\n"
"\n"
"trackmem\n"
- " Begin tracking the current CPU's pc.\n"
+ " Begin tracking memory writes for the current CPU.\n"
"\n"
"trackmem 1, 0, 1\n"
- " Continue tracking memory writes on CPU 0, but clear existing track info.\n"
+ " Continue tracking memory writes on CPU 0, but clear existing tracking data.\n"
},
{
"pcatmem",
"\n"
- " pcatmem(p/d/i) <address>[,<CPU>]\n"
+ " pcatmem[{d|i|o}] <address>[:<space>]\n"
"\n"
- "The pcatmem command returns which PC wrote to a given memory address for the current CPU. "
- "The first argument is the requested address. The second argument is a CPU selector; if no "
- "CPU is specified, the current CPU is automatically selected. Right clicking in a memory window "
- "will also display the logged PC for the given address.\n"
+ "The pcatmem command returns which PC value at the time the specified address was most "
+ "recently written. The argument is the requested address, optionally followed by a colon "
+ "and a CPU and/or address space. The CPU may be specified as a tag or debugger CPU number; "
+ "if no CPU is specified, the CPU currently visible in the debugger is assumed. If an "
+ "address space is not specified, the command suffix sets the address space: 'pcatmem' "
+ "defaults to the first space exposed by the device, 'pcatmemd' defaults to the data space, "
+ "'pcatmemi' defaults to the I/O space, and 'pcatmemo' defaults to the opcodes space.\n"
+ "\n"
+ "Right-clicking in a memory view will also display the logged PC for the given address.\n"
"\n"
"Examples:\n"
"\n"
"pcatmem 400000\n"
- " Print which PC wrote this CPU's memory location 0x400000.\n"
+ " Print which PC wrote to this CPU's program space at location 0x400000.\n"
+ "\n"
+ "pcatmem 3bc:io\n"
+ " Print which PC wrote this CPU's memory io space at location 0x3bc.\n"
+ "\n"
+ "pcatmem 1400:audiocpu\n"
+ " Print which PC wrote the CPU :audiocpu's memory program space at location 0x1400.\n"
},
+ { "pcatmemd", "#pcatmem" },
+ { "pcatmemi", "#pcatmem" },
+ { "pcatmemo", "#pcatmem" },
{
"rewind[rw]",
"\n"
@@ -510,22 +560,25 @@ static const help_item static_help_list[] =
{
"snap",
"\n"
- " snap [[<filename>], <scrnum>]\n"
+ " snap [<filename>[,<scrnum>]]\n"
+ "\n"
+ "Takes a snapshot of the emulated video display and saves it to the configured snapshot "
+ "directory. If a filename is specified, a single screenshot for the specified screen is "
+ "saved using the specified filename (or the first emulated screen in the system if a screen "
+ "is not specified). If a file name is not specified, the configured snapshot view and file "
+ "name pattern are used.\n"
"\n"
- "The snap command takes a snapshot of the current video display and saves it to the configured "
- "snapshot directory. If <filename> is specified explicitly, a single screenshot for <scrnum> is "
- "saved under the requested filename. If <filename> is omitted, all screens are saved using the "
- "same default rules as the \"save snapshot\" key in MAME proper.\n"
+ "If a file name is specified, the .png extension is automatically appended. The screen "
+ "number is specified as a zero-based index.\n"
"\n"
"Examples:\n"
"\n"
"snap\n"
- " Takes a snapshot of the current video screen and saves to the next non-conflicting filename "
- " in the configured snapshot directory.\n"
+ " Takes a snapshot using the configured snapshot view and file name options.\n"
"\n"
"snap shinobi\n"
- " Takes a snapshot of the current video screen and saves it as 'shinobi.png' in the configured "
- " snapshot directory.\n"
+ " Takes a snapshot of the first emulated video screen and saves it as 'shinobi.png' in the "
+ " configured snapshot directory.\n"
},
{
"source",
@@ -541,6 +594,13 @@ static const help_item static_help_list[] =
" Reads in debugger commands from break_and_trace.cmd and executes them.\n"
},
{
+ "time",
+ "\n"
+ " time\n"
+ "\n"
+ "The time command prints the current machine time to the console.\n"
+ },
+ {
"quit",
"\n"
" quit\n"
@@ -572,50 +632,91 @@ static const help_item static_help_list[] =
{
"find",
"\n"
- " f[ind][{d|i}] <address>,<length>[,<data>[,...]]\n"
- "\n"
- "The find/findd/findi commands search through memory for the specified sequence of data. "
- "'find' will search program space memory, while 'findd' will search data space memory "
- "and 'findi' will search I/O space memory. <address> indicates the address to begin searching, "
- "and <length> indicates how much memory to search. <data> can either be a quoted string "
- "or a numeric value or expression or the wildcard character '?'. Strings by default imply a "
- "byte-sized search; non-string data is searched by default in the native word size of the CPU. "
- "To override the search size for non-strings, you can prefix the value with b. to force byte- "
- "sized search, w. for word-sized search, d. for dword-sized, and q. for qword-sized. Overrides "
- "are remembered, so if you want to search for a series of words, you need only to prefix the "
- "first value with a w. Note also that you can intermix sizes in order to perform more complex "
- "searches. The entire range <address> through <address>+<length>-1 inclusive will be searched "
- "for the sequence, and all occurrences will be displayed.\n"
+ " f[ind][{d|i|o}] <address>[:<space>],<length>[,<data>[,...]]\n"
+ "\n"
+ "The find commands search through memory for the specified sequence of data. The <address> "
+ "is the address to begin searching from, optionally followed by a device and/or address "
+ "space; the <length> specifies how much memory to search. The device may be specified as a "
+ "tag or a debugger CPU number; if no device is specified, the CPU currently visible in the "
+ "debugger is assumed. If an address space is not specified, the command suffix sets the "
+ "address space: 'find' defaults to the first address space exposed by the device, 'findd' "
+ "defaults to the data space, 'findi' defaults to the I/O space, and 'findo' defaults to the "
+ "opcodes space.\n"
+ "\n"
+ "The <data> can either be a quoted string or a numeric value or expression or the wildcard "
+ "character '?'. By default, strings imply a byte-sized search; by default non-string data "
+ "is searched using the native word size of the address space. To override the search size "
+ "for non-string data, you can prefix values with b. to force byte-sized search, w. for "
+ "word-sized search, d. for dword-sized search, and q. for qword-sized search. Overrides "
+ "propagate to subsequent values, so if you want to search for a sequence of words, you need "
+ "only prefix the first value with a w. Also note that you can intermix sizes to perform "
+ "more complex searches. The entire range <address> through <address>+<length>-1, "
+ "inclusive, will be searched for the sequence, and all occurrences will be displayed.\n"
"\n"
"Examples:\n"
"\n"
"find 0,10000,\"HIGH SCORE\",0\n"
- " Searches the address range 0-ffff in the current CPU for the string \"HIGH SCORE\" followed "
- "by a 0 byte.\n"
+ " Searches the address range 0-ffff in the current CPU for the string \"HIGH SCORE\" "
+ "followed by a 0 byte.\n"
"\n"
- "findd 3000,1000,w.abcd,4567\n"
- " Searches the data memory address range 3000-3fff for the word-sized value abcd followed by "
- "the word-sized value 4567.\n"
+ "find 300:tms9918a,100,w.abcd,4567\n"
+ " Searches the address range 300-3ff in the first address space exposed by the device "
+ "':tms9918a' for the word-sized value abcd followed by the word-sized value 4567.\n"
"\n"
"find 0,8000,\"AAR\",d.0,\"BEN\",w.0\n"
" Searches the address range 0000-7fff for the string \"AAR\" followed by a dword-sized 0 "
"followed by the string \"BEN\", followed by a word-sized 0.\n"
},
+ { "findd", "#find" },
+ { "findi", "#find" },
+ { "findo", "#find" },
{
- "dump",
+ "fill",
"\n"
- " dump[{d|i}] <filename>,<address>,<length>[,<size>[,<ascii>[,<CPU>]]]\n"
+ " fill[{d|i|o}] <address>[:<space>],<length>[,<data>[,...]]\n"
"\n"
- "The dump/dumpd/dumpi/dumpo commands dump memory to the text file specified in the <filename> "
- "parameter. 'dump' will dump program space memory, while 'dumpd' will dump data space memory, "
- "'dumpi' will dump I/O space memory and 'dumpo' will dump opcodes memory. <address> indicates "
- "the address of the start of dumping, and <length> indicates how much memory to dump. The range "
- "<address> through <address>+<length>-1 inclusive will be output to the file. By default, the data "
- "will be output in byte format, unless the underlying address space is word/dword/qword-only. "
- "You can override this by specifying the <size> parameter, which can be used to group the data in "
- "1, 2, 4 or 8-byte chunks. The optional <ascii> parameter can be used to enable (1) or disable (0) "
- "the output of ASCII characters to the right of each line; by default, this is enabled. Finally, "
- "you can dump memory from another CPU by specifying the <CPU> parameter.\n"
+ "The fill commands overwrite a block of memory with copies of the supplied data sequence. "
+ "The <address> specifies the address to begin writing at, optionally followed by a device "
+ "and/or address space; the <length> specifies how much memory to fill. The device may be "
+ "specified as a tag or a debugger CPU number; if no device is specified, the CPU currently "
+ "visible in the debugger is assumed. If an address space is not specified, the command "
+ "suffix sets the address space: 'fill' defaults to the first address space exposed by the "
+ "device, 'filld' defaults to the data space, 'filli' defaults to the I/O space, and 'fillo' "
+ "defaults to the opcodes space.\n"
+ "\n"
+ "The <data> can either be a quoted string or a numeric value or expression. By default, "
+ "non-string data is written using the native word size of the address space. To override "
+ "the data size for non-string data, you can prefix values with b. to force byte-sized fill, "
+ "w. for word-sized fill, d. for dword-sized fill, and q. for qword-sized fill. Overrides "
+ "propagate to subsequent values, so if you want to fill with a series of words, you need "
+ "only prefix the first value with a w. Also note that you can intermix sizes to perform "
+ "more complex fills. The fill operation may be truncated if a page fault occurs or if part "
+ "of the sequence or string would fall beyond <address>+<length>-1.\n"
+ },
+ { "filld", "#fill" },
+ { "filli", "#fill" },
+ { "fillo", "#fill" },
+ {
+ "dump",
+ "\n"
+ " dump[{d|i|o}] <filename>,<address>[:<space>],<length>[,<group>[,<ascii>[,<rowsize>]]]\n"
+ "\n"
+ "The dump commands dump memory to the text file specified in the <filename> parameter. The "
+ "<address> specifies the address to start dumping from, optionally followed by a device "
+ "and/or address space; the <length> specifies how much memory to dump. The device may be "
+ "specified as a tag or a debugger CPU number; if no device is specified, the CPU currently "
+ "visible in the debugger is assumed. If an address space is not specified, the command "
+ "suffix sets the address space: 'dump' defaults to the first address space exposed by the "
+ "device, 'dumpd' defaults to the data space, 'dumpi' defaults to the I/O space, and 'dumpo' "
+ "defaults to the opcodes space.\n"
+ "\n"
+ "The range <address> through <address>+<length>-1, inclusive, will be output to the file. "
+ "By default, the data will be output using the native word size of the address space. You "
+ "can override this by specifying the <group> parameter, which can be used to group the data "
+ "in 1-, 2-, 4- or 8-byte chunks. The optional <ascii> parameter is a Boolean value used to "
+ "enable or disable output of ASCII characters on the right of each line (enabled by "
+ "default). The optional <rowsize> parameter specifies the amount of data on each line in "
+ "address units (defaults to 16 bytes).\n"
"\n"
"Examples:\n"
"\n"
@@ -623,43 +724,111 @@ static const help_item static_help_list[] =
" Dumps addresses 0-ffff in the current CPU in 1-byte chunks, including ASCII data, to "
"the file 'venture.dmp'.\n"
"\n"
- "dumpd harddriv.dmp,3000,1000,4,0,3\n"
+ "dumpd harddriv.dmp,3000:3,1000,4,0\n"
" Dumps data memory addresses 3000-3fff from CPU #3 in 4-byte chunks, with no ASCII data, "
"to the file 'harddriv.dmp'.\n"
+ "\n"
+ "dump vram.dmp,0:sms_vdp:videoram,4000,1,0,8\n"
+ " Dumps 'videoram' space addresses 0000-3fff from the device ':sms_vdp' in 1-byte chunks, "
+ "with no ASCII data, and 8 bytes per line, to the file 'vram.dmp'.\n"
},
+ { "dumpd", "#dump" },
+ { "dumpi", "#dump" },
+ { "dumpo", "#dump" },
+ {
+ "strdump",
+ "\n"
+ " strdump[{d|i|o}] <filename>,<address>[:<space>],<length>[,<term>]\n"
+ "\n"
+ "The strdump commands dump memory to the text file specified in the <filename> parameter. "
+ "The <address> specifies the address to start dumping from, optionally followed by a device "
+ "and/or address space; the <length> specifies how much memory to dump. The device may be "
+ "specified as a tag or a debugger CPU number; if no device is specified, the CPU currently "
+ "visible in the debugger is assumed. If an address space is not specified, the command "
+ "suffix sets the address space: 'strdump' defaults to the first address space exposed by "
+ "the device, 'strdumpd' defaults to the data space, 'strdumpi' defaults to the I/O space, "
+ "and 'strdumpo' defaults to the opcodes space.\n"
+ "\n"
+ "By default, the data will be interpreted as a series of NUL-terminated strings, the dump "
+ "will have one string per line, and C-style escapes will be used for non-ASCII characters. "
+ "The optional <term> parameter can be used to specify a different string terminator "
+ "character.\n"
+ },
+ { "strdumpd", "#strdump" },
+ { "strdumpi", "#strdump" },
+ { "strdumpo", "#strdump" },
{
"save",
"\n"
- " save[{d|i}] <filename>,<address>,<length>[,<CPU>]\n"
+ " save[{d|i|o}] <filename>,<address>[:<space>],<length>\n"
"\n"
- "The save/saved/savei commands save raw memory to the binary file specified in the <filename> "
- "parameter. 'save' will save program space memory, while 'saved' will save data space memory "
- "and 'savei' will save I/O space memory. <address> indicates the address of the start of saving, "
- "and <length> indicates how much memory to save. The range <address> through <address>+<length>-1 "
- "inclusive will be output to the file. You can also save memory from another CPU by specifying the "
- "<CPU> parameter.\n"
+ "The save commands save raw memory to the binary file specified in the <filename> "
+ "parameter. The <address> specifies the address to start saving from, optionally followed "
+ "by a device and/or address space; the <length> specifies how much memory to save. The "
+ "device may be specified as a tag or a debugger CPU number; if no device is specified, the "
+ "CPU currently visible in the debugger is assumed. If an address space is not specified, "
+ "the command suffix sets the address space: 'save' defaults to the first address space "
+ "exposed by the device, 'saved' defaults to the data space, 'savei' defaults to the I/O "
+ "space, and 'saveo' defaults to the opcodes space.\n"
+ "\n"
+ "The range <address> through <address>+<length>-1, inclusive, will be output to the file.\n"
"\n"
"Examples:\n"
"\n"
"save venture.bin,0,10000\n"
" Saves addresses 0-ffff in the current CPU to the binary file 'venture.bin'.\n"
"\n"
- "saved harddriv.bin,3000,1000,3\n"
+ "saved harddriv.bin,3000:3,1000\n"
" Saves data memory addresses 3000-3fff from CPU #3 to the binary file 'harddriv.bin'.\n"
+ "\n"
+ "save vram.bin,0:sms_vdp:videoram,4000\n"
+ " Saves 'videoram' space addresses 0000-3fff from the device ':sms_vdp' to the binary file "
+ "'vram.bin'.\n"
+ },
+ { "saved", "#save" },
+ { "savei", "#save" },
+ { "saveo", "#save" },
+ {
+ "saver",
+ "\n"
+ " saver <filename>,<address>,<length>,<region>\n"
+ "\n"
+ "The saver command saves the raw content of memory region <region> to the binary file "
+ "specified in the <filename> parameter. The <address> specifies the address to start "
+ "saving from, and the <length> specifies how much memory to save. The range <address> "
+ "through <address>+<length>-1, inclusive, will be output to the file.\n"
+ "\n"
+ "Examples:\n"
+ "\n"
+ "saver data.bin,200,100,:monitor\n"
+ " Saves ':monitor' region addresses 200-2ff to the binary file 'data.bin'.\n"
+ "\n"
+ "saver cpurom.bin,1000,400,.\n"
+ " Saves addresses 1000-13ff from the memory region with the same tag as the visible CPU to "
+ "the binary file 'cpurom.bin'.\n"
},
{
"load",
"\n"
- " load[{d|i}] <filename>,<address>[,<length>,<CPU>]\n"
+ " load[{d|i|o}] <filename>,<address>[:<space>][,<length>]\n"
"\n"
- "The load/loadd/loadi commands load raw memory from the binary file specified in the <filename> "
- "parameter. 'load' will load program space memory, while 'loadd' will load data space memory "
- "and 'loadi' will load I/O space memory. <address> indicates the address of the start of saving, "
- "and <length> indicates how much memory to load. The range <address> through <address>+<length>-1 "
- "inclusive will be read in from the file. If you specify <length> = 0 or a length greater than the "
- "total length of the file it will load the entire contents of the file and no more. You can also load "
- "memory from another CPU by specifying the <CPU> parameter.\n"
- "NOTE: This will only actually write memory that is possible to overwrite in the Memory Window\n"
+ "The load commands load raw memory from the binary file specified in the <filename> "
+ "parameter. The <address> specifies the address to start loading to, optionally followed "
+ "by a device and/or address space; the <length> specifies how much memory to load. The "
+ "device may be specified as a tag or a debugger CPU number; if no device is specified, the "
+ "CPU currently visible in the debugger is assumed. If an address space is not specified, "
+ "the command suffix sets the address space: 'load' defaults to the first address space "
+ "exposed by the device, 'loadd' defaults to the data space, 'loadi' defaults to the I/O "
+ "space, and 'loado' defaults to the opcodes space.\n"
+ "\n"
+ "The range <address> through <address>+<length>-1, inclusive, will be read in from the "
+ "file. If the <length> is omitted, if it is zero, or if it is greater than the total "
+ "length of the file, the entire contents of the file will be loaded but no more.\n"
+ "\n"
+ "NOTE: this has the same effect as writing to the address space from a debugger memory "
+ "view, or using memory accessors in debugger expressions. Read-only memory will not be "
+ "overwritten, and writing to I/O addresses may have effects beyond setting register "
+ "values.\n"
"\n"
"Examples:\n"
"\n"
@@ -668,6 +837,34 @@ static const help_item static_help_list[] =
"\n"
"loadd harddriv.bin,3000,1000,3\n"
" Loads data memory addresses 3000-3fff from CPU #3 from the binary file 'harddriv.bin'.\n"
+ "\n"
+ "load vram.bin,0:sms_vdp:videoram\n"
+ " Loads 'videoram' space starting at address 0000 from the device ':sms_vdp' with the "
+ "entire content of the binary file 'vram.bin'.\n"
+ },
+ { "loadd", "#load" },
+ { "loadi", "#load" },
+ { "loado", "#load" },
+ {
+ "loadr",
+ "\n"
+ " loadr <filename>,<address>,<length>,<region>\n"
+ "\n"
+ "The loadr command loads raw memory in the memory region <region> from the binary file "
+ "specified by the <filename> parameter. The <address> specifies the address to start "
+ "loading to, and the <length> specifies how much memory to load. The range <address> "
+ "through <address>+<length>-1, inclusive, will be read in from the file. If the <length> "
+ "is zero, or is greater than the total length of the file, the entire contents of the file "
+ "will be loaded but no more.\n"
+ "\n"
+ "Examples:\n"
+ "\n"
+ "loadr data.bin,200,100,:monitor\n"
+ " Loads ':monitor' region addresses 200-2ff from the binary file 'data.bin'.\n"
+ "\n"
+ "loadr cpurom.bin,1000,400,.\n"
+ " Loads addresses 1000-13ff in the memory region with the same tag as the visible CPU from "
+ "the binary file 'cpurom.bin'.\n"
},
{
"step",
@@ -722,7 +919,7 @@ static const help_item static_help_list[] =
"Note that the step out functionality may not be implemented on all CPU types. If it is not "
"implemented, then 'out' will behave exactly like 'step'.\n"
"\n"
- "Examples:\n"
+ "Example:\n"
"\n"
"out\n"
" Steps until the current subroutine or exception handler returns.\n"
@@ -746,41 +943,132 @@ static const help_item static_help_list[] =
" Resume execution, stopping at address 1234 unless something else stops us first.\n"
},
{
- "gvblank",
+ "gbf",
"\n"
- " gv[blank]\n"
+ " gbf [<condition>]\n"
"\n"
- "The gvblank command resumes execution of the current code. Control will not be returned to "
- "the debugger until a breakpoint or watchpoint is hit, or until the next VBLANK occurs in the "
- "emulator.\n"
+ "The gbf command resumes execution of the current code. Control will not be returned to the "
+ "debugger until a conditional branch or skip instruction tests false and falls through to the "
+ "next instruction, or the instruction that follows its delay slot. The optional conditional "
+ "expression, if provided, will be evaluated at (not after) each conditional branch; execution "
+ "will not halt regardless of consequent program flow unless its result is true (non-zero).\n"
"\n"
"Examples:\n"
"\n"
- "gv\n"
- " Resume execution until the next break/watchpoint or until the next VBLANK.\n"
+ "gbf\n"
+ " Resume execution until the next break/watchpoint or until the next false branch.\n"
+ "\n"
+ "gbf {pc != 1234}\n"
+ " Resume execution until the next false branch, disregarding the instruction at address 1234.\n"
+ },
+ {
+ "gbt",
+ "\n"
+ " gbt [<condition>]\n"
+ "\n"
+ "The gbt command resumes execution of the current code. Control will not be returned to the "
+ "debugger until a conditional branch or skip instruction tests true and program flow transfers "
+ "to any instruction other than the next one following the delay slot (if any). The optional "
+ "conditional expression, if provided, will be evaluated at (not after) each conditional "
+ "branch; execution will not halt regardless of consequent program flow unless its result is "
+ "true (non-zero).\n"
+ "\n"
+ "Examples:\n"
+ "\n"
+ "gbt\n"
+ " Resume execution until the next break/watchpoint or until the next true branch.\n"
+ "\n"
+ "gbt {pc != 1234}\n"
+ " Resume execution until the next true branch, disregarding the instruction at address 1234.\n"
+ },
+ {
+ "gni",
+ "\n"
+ " gn[i] [<count>]\n"
+ "\n"
+ "The gni command resumes execution of the current code. Control will not be returned to the "
+ "debugger until a breakpoint or watchpoint is hit, or until you manually break in using the "
+ "assigned key. Before executing, the gni command sets a temporary unconditional breakpoint "
+ "<count> instructions sequentially past the current one, which is automatically removed when "
+ "hit. If <count> is omitted, its default value is 1. If <count> is specified as zero, the "
+ "command does nothing. <count> is not permitted to exceed 512 decimal."
+ "\n"
+ "Examples:\n"
+ "\n"
+ "gni\n"
+ " Resume execution, stopping at the address of the next instruction unless something else "
+ "stops us first.\n"
+ "\n"
+ "gni 2\n"
+ " Resume execution, stopping at two instructions past the current one.\n"
+ },
+ {
+ "gex",
+ "\n"
+ " ge[x] [<exception>,[<condition>]]\n"
+ "\n"
+ "The gex command resumes execution of the current code. Control will not be returned to "
+ "the debugger until a breakpoint or watchpoint is hit, or until an exception condition "
+ "is raised on the current CPU. You can specify <exception> if you wish to stop execution "
+ "only on a particular exception condition occurring. If <exception> is omitted, then any "
+ "exception condition will stop execution. The optional <condition> parameter lets you "
+ "specify an expression that will be evaluated each time the specified exception condition "
+ "is raised. If the result of the expression is true (non-zero), the exception will halt "
+ "execution; otherwise, execution will continue with no notification.\n"
+ "\n"
+ "Examples:\n"
+ "\n"
+ "gex\n"
+ " Resume execution until the next break/watchpoint or until any exception condition is "
+ "raised on the current CPU.\n"
+ "\n"
+ "ge 2\n"
+ " Resume execution until the next break/watchpoint or until exception condition 2 is "
+ "raised on the current CPU.\n"
},
{
"gint",
"\n"
" gi[nt] [<irqline>]\n"
"\n"
- "The gint command resumes execution of the current code. Control will not be returned to the "
- "debugger until a breakpoint or watchpoint is hit, or until an IRQ is asserted and acknowledged "
- "on the current CPU. You can specify <irqline> if you wish to stop execution only on a particular "
- "IRQ line being asserted and acknowledged. If <irqline> is omitted, then any IRQ line will stop "
- "execution.\n"
+ "The gint command resumes execution of the current code. Control will not be returned to "
+ "the debugger until a breakpoint or watchpoint is hit, or until an IRQ is asserted and "
+ "acknowledged on the current CPU. You can specify <irqline> if you wish to stop execution "
+ "only on a particular IRQ line being asserted and acknowledged. If <irqline> is omitted, "
+ "then any IRQ line will stop execution.\n"
"\n"
"Examples:\n"
"\n"
"gi\n"
- " Resume execution until the next break/watchpoint or until any IRQ is asserted and acknowledged "
- "on the current CPU.\n"
+ " Resume execution until the next break/watchpoint or until any IRQ is asserted and "
+ "acknowledged on the current CPU.\n"
"\n"
"gint 4\n"
" Resume execution until the next break/watchpoint or until IRQ line 4 is asserted and "
"acknowledged on the current CPU.\n"
},
{
+ "gp",
+ "\n"
+ " gp [<condition>]\n"
+ "\n"
+ "The gp command resumes execution of the current code. Control will not be returned to "
+ "the debugger until a breakpoint or watchpoint is hit, or the privilege level of the current "
+ "CPU changes. The optional <condition> parameter lets you specify an expression that will "
+ "be evaluated each time the privilege level changes. If the expression evaluates to true "
+ "(non-zero), execution will halt; otherwise, execution will continue with no notification.\n"
+ "\n"
+ "Examples:\n"
+ "\n"
+ "gp\n"
+ " Resume execution until the next break/watchpoint or the privilege level of the current "
+ "CPU changes.\n"
+ "\n"
+ "gp {pc != 1234}\n"
+ " Resume execution until the next break/watchpoint or the privilege level of the current "
+ "CPU changes, disregarding the instruction at address 1234.\n"
+ },
+ {
"gtime",
"\n"
" gt[ime] <milliseconds>\n"
@@ -794,13 +1082,32 @@ static const help_item static_help_list[] =
" Resume execution for ten seconds\n"
},
{
+ "gvblank",
+ "\n"
+ " gv[blank]\n"
+ "\n"
+ "The gvblank command resumes execution of the current code. Control will not be returned to "
+ "the debugger until a breakpoint or watchpoint is hit, or until the beginning of the "
+ " vertical blanking interval for an emulated screen.\n"
+ "\n"
+ "Example:\n"
+ "\n"
+ "gv\n"
+ " Resume execution until the next break/watchpoint or until the next VBLANK.\n"
+ },
+ {
"next",
"\n"
" n[ext]\n"
"\n"
"The next command resumes execution and continues executing until the next time a different "
- "CPU is scheduled. Note that if you have used 'ignore' to ignore certain CPUs, you will not "
- "stop until a non-'ignore'd CPU is scheduled.\n"
+ "CPU is scheduled. Note that if you have used 'focus' or 'ignore' to ignore certain CPUs, "
+ "execution will continue until a non-'ignore'd CPU is scheduled.\n"
+ "\n"
+ "Example:\n"
+ "\n"
+ "n\n"
+ " Resume execution, stopping when a different CPU that is not ignored is scheduled.\n"
},
{
"focus",
@@ -814,21 +1121,25 @@ static const help_item static_help_list[] =
"\n"
"focus 1\n"
" Focus exclusively CPU #1 while ignoring all other CPUs when using the debugger.\n"
+ "\n"
+ "focus audiopcb:melodycpu\n"
+ " Focus exclusively on the CPU ':audiopcb:melodycpu'.\n"
},
{
"ignore",
"\n"
" ignore [<CPU>[,<CPU>[,...]]]\n"
"\n"
- "Ignores the specified <CPU> in the debugger. This means that you won't ever see execution "
- "on that CPU, nor will you be able to set breakpoints on that CPU. To undo this change use "
- "the 'observe' command. You can specify multiple <CPU>s in a single command. Note also that "
- "you are not permitted to ignore all CPUs; at least one must be active at all times.\n"
+ "Ignores the specified CPUs in the debugger. CPUs can be specified by tag or debugger CPU "
+ "number. The debugger never shows execution for ignored CPUs, and breakpoints or "
+ "watchpoints on ignored CPUs have no effect. If no CPUs are specified, currently ignored "
+ "CPUs will be listed. Use the 'observe' command to stop ignoring a CPU. Note that you "
+ "cannot ignore all CPUs; at least CPU must be observed at all times.\n"
"\n"
"Examples:\n"
"\n"
- "ignore 1\n"
- " Ignore CPU #1 when using the debugger.\n"
+ "ignore audiocpu\n"
+ " Ignore the CPU ':audiocpu' when using the debugger.\n"
"\n"
"ignore 2,3,4\n"
" Ignore CPU #2, #3 and #4 when using the debugger.\n"
@@ -846,8 +1157,8 @@ static const help_item static_help_list[] =
"\n"
"Examples:\n"
"\n"
- "observe 1\n"
- " Stop ignoring CPU #1 when using the debugger.\n"
+ "observe audiocpu\n"
+ " Stop ignoring the CPU ':audiocpu' when using the debugger.\n"
"\n"
"observe 2,3,4\n"
" Stop ignoring CPU #2, #3 and #4 when using the debugger.\n"
@@ -858,106 +1169,120 @@ static const help_item static_help_list[] =
{
"trace",
"\n"
- " trace {<filename>|OFF}[,<CPU>[,[noloop|logerror][,<action>]]]\n"
- "\n"
- "Starts or stops tracing of the execution of the specified <CPU>. If <CPU> is omitted, "
- "the currently active CPU is specified. When enabling tracing, specify the filename in the "
- "<filename> parameter. To disable tracing, substitute the keyword 'off' for <filename>. "
- "<detectloops> should be either true or false. If 'noloop' is omitted, the trace "
- "will have loops detected and condensed to a single line. If 'noloop' is specified, the trace "
- "will contain every opcode as it is executed. If 'logerror' is specified, logerror output "
- "will augment the trace. If you "
- "wish to log additional information on each trace, you can append an <action> parameter which "
- "is a command that is executed before each trace is logged. Generally, this is used to include "
- "a 'tracelog' command. Note that you may need to embed the action within braces { } in order "
- "to prevent commas and semicolons from being interpreted as applying to the trace command "
- "itself.\n"
+ " trace {<filename>|off}[,<CPU>[,[noloop|logerror][,<action>]]]\n"
+ "\n"
+ "Starts or stops tracing of the execution of the specified <CPU>, or the currently visible "
+ "CPU if no CPU is specified. To enable tracing, specify the trace log file name in the "
+ "<filename> parameter. To disable tracing, use the keyword 'off' for <filename> "
+ "parameter. If the **<filename>** begins with two right angle brackets (>>), it is treated "
+ "as a directive to open the file for appending rather than overwriting.\n"
+ "\n"
+ "The optional third parameter is a flags field. The supported flags are 'noloop' and "
+ "'logerror'. Multiple flags must be separated by | (pipe) characters. By default, loops "
+ "are detected and condensed to a single line. If the 'noloop' flag is specified, loops "
+ "will not be detected and every instruction will be logged as executed. If the 'logerror' "
+ "flag is specified, error log output will be included in the trace log.\n"
+ "\n"
+ "The optional <action> parameter is a debugger command to execute before each trace message "
+ "is logged. Generally, this will include a 'tracelog' or 'tracesym' command to include "
+ "additional information in the trace log. Note that you may need to embed the action "
+ "within braces { } in order to prevent commas and semicolons from being interpreted as "
+ "applying to the trace command itself.\n"
"\n"
"Examples:\n"
"\n"
"trace joust.tr\n"
- " Begin tracing the currently active CPU, logging output to joust.tr.\n"
+ " Begin tracing the execution of the currently visible CPU, logging output to joust.tr.\n"
"\n"
- "trace dribling.tr,0\n"
- " Begin tracing the execution of CPU #0, logging output to dribling.tr.\n"
+ "trace dribling.tr,maincpu\n"
+ " Begin tracing the execution of the CPU ':maincpu', logging output to dribling.tr.\n"
"\n"
- "trace starswep.tr,0,noloop\n"
- " Begin tracing the execution of CPU #0, logging output to starswep.tr, with loop detection disabled.\n"
+ "trace starswep.tr,,noloop\n"
+ " Begin tracing the execution of the currently visible CPU, logging output to starswep.tr, "
+ "with loop detection disabled.\n"
"\n"
- "trace starswep.tr,0,logerror\n"
- " Begin tracing the execution of CPU #0, logging output (along with logerror output) to starswep.tr.\n"
+ "trace starswep.tr,1,logerror\n"
+ " Begin tracing the execution of CPU #1, logging output (along with logerror output) to "
+ "starswep.tr.\n"
"\n"
"trace starswep.tr,0,logerror|noloop\n"
- " Begin tracing the execution of CPU #0, logging output (along with logerror output) to starswep.tr, with loop detection disabled.\n"
+ " Begin tracing the execution of CPU #0, logging output (along with logerror output) to "
+ "starswep.tr, with loop detection disabled.\n"
"\n"
"trace >>pigskin.tr\n"
- " Begin tracing the currently active CPU, appending log output to pigskin.tr.\n"
+ " Begin tracing execution of the currently visible CPU, appending log output to "
+ "pigskin.tr.\n"
"\n"
"trace off,0\n"
" Turn off tracing on CPU #0.\n"
"\n"
- "trace asteroid.tr,0,,{tracelog \"A=%02X \",a}\n"
- " Begin tracing the execution of CPU #0, logging output to asteroid.tr. Before each line, "
- "output A=<aval> to the tracelog.\n"
+ "trace asteroid.tr,,,{tracelog \"A=%02X \",a}\n"
+ " Begin tracing the execution of the currently visible CPU, logging output to asteroid.tr. "
+ "Before each line, output A=<aval> to the trace log.\n"
},
{
"traceover",
"\n"
- " traceover {<filename>|OFF}[,<CPU>[,<detectloops>[,<action>]]]\n"
- "\n"
- "Starts or stops tracing of the execution of the specified <CPU>. When tracing reaches "
- "a subroutine or call, tracing will skip over the subroutine. The same algorithm is used as is "
- "used in the step over command. This means that traceover will not work properly when calls "
- "are recusive or the return address is not immediately following the call instruction. If "
- "<detectloops> should be either true or false. If <detectloops> is true or omitted, the trace "
- "will have loops detected and condensed to a single line. If it is false, the trace will contain "
- "every opcode as it is executed. If <CPU> is omitted, the currently active CPU is specified. When "
- "enabling tracing, specify the filename in the <filename> parameter. To disable tracing, substitute "
- "the keyword 'off' for <filename>. If you wish to log additional information on each trace, you can "
- "append an <action> parameter which is a command that is executed before each trace is logged. "
- "Generally, this is used to include a 'tracelog' command. Note that you may need to embed the "
- "action within braces { } in order to prevent commas and semicolons from being interpreted as "
- "applying to the trace command itself.\n"
+ " traceover {<filename>|off}[,<CPU>[,[noloop|logerror][,<action>]]]\n"
+ "\n"
+ "Starts or stops tracing for execution of the specified **<CPU>**, or the currently visible "
+ "CPU if no CPU is specified. When a subroutine call is encountered, tracing will skip over "
+ "the subroutine. The same algorithm is used as is used in the step over command. It will "
+ "not work properly with recursive functions, or if the return address does not immediately "
+ "follow the call instruction.\n"
+ "\n"
+ "This command accepts the same parameters as the 'trace' command. Please refer to the "
+ "corresponding section for a detailed description of options and more examples.\n"
"\n"
"Examples:\n"
"\n"
"traceover joust.tr\n"
- " Begin tracing the currently active CPU, logging output to joust.tr.\n"
+ " Begin tracing the execution of the currently visible CPU, logging output to joust.tr.\n"
"\n"
- "traceover dribling.tr,0\n"
- " Begin tracing the execution of CPU #0, logging output to dribling.tr.\n"
+ "traceover dribling.tr,maincpu\n"
+ " Begin tracing the execution of the CPU ':maincpu', logging output to dribling.tr.\n"
"\n"
- "traceover starswep.tr,0,false\n"
- " Begin tracing the execution of CPU #0, logging output to starswep.tr, with loop detection disabled.\n"
+ "traceover starswep.tr,,noloop\n"
+ " Begin tracing the execution of the currently visible CPU, logging output to starswep.tr, "
+ "with loop detection disabled.\n"
"\n"
"traceover off,0\n"
" Turn off tracing on CPU #0.\n"
"\n"
- "traceover asteroid.tr,0,true,{tracelog \"A=%02X \",a}\n"
- " Begin tracing the execution of CPU #0, logging output to asteroid.tr. Before each line, "
- "output A=<aval> to the tracelog.\n"
+ "traceover asteroid.tr,,,{tracelog \"A=%02X \",a}\n"
+ " Begin tracing the execution of the currently visible CPU, logging output to "
+ "asteroid.tr. Before each line, output A=<aval> to the trace log.\n"
},
{
"traceflush",
"\n"
" traceflush\n"
"\n"
- "Flushes all open trace files.\n"
+ "Flushes all open trace log files to disk.\n"
+ "\n"
+ "Example:\n"
+ "\n"
+ "traceflush\n"
+ " Flush trace log files.\n"
},
{
"bpset",
"\n"
- " bp[set] <address>[,<condition>[,<action>]]\n"
+ " bp[set] <address>[:<CPU>][,<condition>[,<action>]]\n"
"\n"
- "Sets a new execution breakpoint at the specified <address>. The optional <condition> "
- "parameter lets you specify an expression that will be evaluated each time the breakpoint is "
- "hit. If the result of the expression is true (non-zero), the breakpoint will actually halt "
- "execution; otherwise, execution will continue with no notification. The optional <action> "
- "parameter provides a command that is executed whenever the breakpoint is hit and the "
- "<condition> is true. Note that you may need to embed the action within braces { } in order "
- "to prevent commas and semicolons from being interpreted as applying to the bpset command "
- "itself. Each breakpoint that is set is assigned an index which can be used in other "
- "breakpoint commands to reference this breakpoint.\n"
+ "Sets a new execution breakpoint at the specified <address>. The <address> may optionally "
+ "be followed by a colon and a tag or debugger CPU number to specify a CPU explicitly. If "
+ "no CPU is specified, the CPU currently visible in the debugger is assumed. The optional "
+ "<condition> parameter lets you specify an expression that will be evaluated each time the "
+ "breakpoint is hit. If the result of the expression is true (non-zero), the breakpoint "
+ "will halt execution; otherwise, execution will continue with no notification. The "
+ "optional <action> parameter provides a command that is executed whenever the breakpoint is "
+ "hit and the <condition> is true. Note that you may need to embed the action within braces "
+ "{ } in order to prevent commas and semicolons from being interpreted as applying to the "
+ "'bpset' command itself.\n"
+ "\n"
+ "Each breakpoint that is set is assigned an index which can be used to refer to it in other "
+ "breakpoint commands.\n"
"\n"
"Examples:\n"
"\n"
@@ -968,31 +1293,32 @@ static const help_item static_help_list[] =
" Set a breakpoint that will halt execution whenever the PC is equal to 23456 AND the "
"expression (a0 == 0 && a1 == 0) is true.\n"
"\n"
- "bp 3456,1,{printf \"A0=%08X\\n\",a0; g}\n"
- " Set a breakpoint that will halt execution whenever the PC is equal to 3456. When "
- "this happens, print A0=<a0val> and continue executing.\n"
+ "bp 3456:audiocpu,1,{ printf \"A0=%08X\\n\",a0 ; g }\n"
+ " Set a breakpoint on the CPU ':audiocpu' that will halt execution whenever the PC is "
+ "equal to 3456. When this happens, print A0=<a0val> and continue executing.\n"
"\n"
- "bp 45678,a0==100,{a0 = ff; g}\n"
- " Set a breakpoint that will halt execution whenever the PC is equal to 45678 AND the "
- "expression (a0 == 100) is true. When that happens, set a0 to ff and resume execution.\n"
+ "bp 45678:2,a0==100,{ a0 = ff ; g }\n"
+ " Set a breakpoint on CPU #2 that will halt execution whenever the PC is equal to 45678 "
+ "and the expression (a0 == 100) is true. When that happens, set a0 to ff and resume "
+ "execution.\n"
"\n"
- "temp0 = 0; bp 567890,++temp0 >= 10\n"
- " Set a breakpoint that will halt execution whenever the PC is equal to 567890 AND the "
- "expression (++temp0 >= 10) is true. This effectively breaks only after the breakpoint "
+ "temp0 = 0 ; bp 567890,++temp0 >= 10\n"
+ " Set a breakpoint that will halt execution whenever the PC is equal to 567890 and the "
+ "expression (++temp0 >= 10) is true. This effectively breaks only after the breakpoint "
"has been hit 16 times.\n"
},
{
"bpclear",
"\n"
- " bpclear [<bpnum>]\n"
+ " bpclear [<bpnum>[,...]]\n"
"\n"
- "The bpclear command clears a breakpoint. If <bpnum> is specified, only the requested "
- "breakpoint is cleared, otherwise all breakpoints are cleared.\n"
+ "The bpclear command clears breakpoints. If <bpnum> is specified, only the requested "
+ "breakpoints are cleared; otherwise all breakpoints are cleared.\n"
"\n"
"Examples:\n"
"\n"
"bpclear 3\n"
- " Clear breakpoint index 3.\n"
+ " Clear the breakpoint with index 3.\n"
"\n"
"bpclear\n"
" Clear all breakpoints.\n"
@@ -1000,16 +1326,16 @@ static const help_item static_help_list[] =
{
"bpdisable",
"\n"
- " bpdisable [<bpnum>]\n"
+ " bpdisable [<bpnum>,[...]]\n"
"\n"
- "The bpdisable command disables a breakpoint. If <bpnum> is specified, only the requested "
- "breakpoint is disabled, otherwise all breakpoints are disabled. Note that disabling a "
+ "The bpdisable command disables breakpoints. If <bpnum> is specified, only the requested "
+ "breakpoints are disabled; otherwise all breakpoints are disabled. Note that disabling a "
"breakpoint does not delete it, it just temporarily marks the breakpoint as inactive.\n"
"\n"
"Examples:\n"
"\n"
"bpdisable 3\n"
- " Disable breakpoint index 3.\n"
+ " Disable the breakpoint with index 3.\n"
"\n"
"bpdisable\n"
" Disable all breakpoints.\n"
@@ -1017,15 +1343,15 @@ static const help_item static_help_list[] =
{
"bpenable",
"\n"
- " bpenable [<bpnum>]\n"
+ " bpenable [<bpnum>,[...]]\n"
"\n"
- "The bpenable command enables a breakpoint. If <bpnum> is specified, only the requested "
- "breakpoint is enabled, otherwise all breakpoints are enabled.\n"
+ "The bpenable command enable breakpoints. If <bpnum> is specified, only the requested "
+ "breakpoints enabled; otherwise all breakpoints are enabled.\n"
"\n"
"Examples:\n"
"\n"
"bpenable 3\n"
- " Enable breakpoint index 3.\n"
+ " Enable the breakpoint with index 3.\n"
"\n"
"bpenable\n"
" Enable all breakpoints.\n"
@@ -1033,35 +1359,55 @@ static const help_item static_help_list[] =
{
"bplist",
"\n"
- " bplist\n"
+ " bplist [<CPU>]\n"
"\n"
- "The bplist command lists all the current breakpoints, along with their index and any "
- "conditions or actions attached to them.\n"
- },
- {
- "wpset",
+ "The bplist list current breakpoints, along with their indices and any associated "
+ "conditions or actions. If no <CPU> is specified, breakpoints for all CPUs in the system "
+ "will be listed; if a <CPU> is specified, only breakpoints for that CPU will be listed. "
+ "The <CPU> can be specified by tag or by debugger CPU number.\n"
"\n"
- " wp[{d|i}][set] <address>,<length>,<type>[,<condition>[,<action>]]\n"
+ "Examples:\n"
+ "\n"
+ "bplist\n"
+ " List all breakpoints.\n"
"\n"
- "Sets a new watchpoint starting at the specified <address> and extending for <length>. The "
- "inclusive range of the watchpoint is <address> through <address> + <length> - 1. The 'wpset' "
- "command sets a watchpoint on program memory; the 'wpdset' command sets a watchpoint on data "
- "memory; and the 'wpiset' sets a watchpoint on I/O memory. The <type> parameter specifies "
- "which sort of accesses to trap on. It can be one of three values: 'r' for a read watchpoint "
- "'w' for a write watchpoint, and 'rw' for a read/write watchpoint.\n"
+ "bplist .\n"
+ " List all breakpoints for the visible CPU.\n"
"\n"
- "The optional <condition> parameter lets you specify an expression that will be evaluated each "
- "time the watchpoint is hit. If the result of the expression is true (non-zero), the watchpoint "
- "will actually halt execution; otherwise, execution will continue with no notification. The "
- "optional <action> parameter provides a command that is executed whenever the watchpoint is hit "
- "and the <condition> is true. Note that you may need to embed the action within braces { } in "
- "order to prevent commas and semicolons from being interpreted as applying to the wpset command "
- "itself. Each watchpoint that is set is assigned an index which can be used in other "
- "watchpoint commands to reference this watchpoint.\n"
+ "bplist maincpu\n"
+ " List all breakpoints for the CPU ':maincpu'.\n"
+ },
+ {
+ "wpset",
"\n"
- "In order to help <condition> expressions, two variables are available. For all watchpoints, "
- "the variable 'wpaddr' is set to the address that actually triggered the watchpoint. For write "
- "watchpoints, the variable 'wpdata' is set to the data that is being written.\n"
+ " wp[{d|i|o}][set] <address>[:<space>],<length>,<type>[,<condition>[,<action>]]\n"
+ "\n"
+ "Sets a new watchpoint starting at the specified <address> and extending for <length>. The "
+ "inclusive range of the watchpoint is <address> through <address>+<length>-1. The "
+ "<address> may optionally be followed by a CPU and/or address space. The CPU may be "
+ "specified as a tag or a debugger CPU number; if no CPU is specified, the CPU currently "
+ "visible in the debugger is assumed. If an address space is not specified, the command "
+ "suffix sets the address space: 'wpset' defaults to the first address space exposed by the "
+ "CPU, 'wpdset' defaults to the data space, 'wpiset' defaults to the I/O space, and 'wposet' "
+ "defaults to the opcodes space. The <type> parameter specifies the access types to trap "
+ "on - it can be one of three values: 'r' for read accesses, 'w' for write accesses, or 'rw' "
+ "for both read and write accesses.\n"
+ "\n"
+ "The optional <condition> parameter lets you specify an expression that will be evaluated "
+ "each time the watchpoint is triggered. If the result of the expression is true "
+ "(non-zero), the watchpoint will halt execution; otherwise, execution will continue with no "
+ "notification. The optional <action> parameter provides a command that is executed "
+ "whenever the watchpoint is triggered and the <condition> is true. Note that you may need "
+ "to embed the action within braces { } in order to prevent commas and semicolons from being "
+ "interpreted as applying to the wpset command itself.\n"
+ "\n"
+ "Each watchpoint that is set is assigned an index which can be used to refer to it in other "
+ "watchpoint commands\n"
+ "\n"
+ "To make <condition> expressions more useful, two variables are available: for all "
+ "watchpoints, the variable 'wpaddr' is set to the access address that triggered the "
+ "watchpoint; for write watchpoints, the variable 'wpdata' is set to the data being "
+ "written.\n"
"\n"
"Examples:\n"
"\n"
@@ -1069,31 +1415,35 @@ static const help_item static_help_list[] =
" Set a watchpoint that will halt execution whenever a read or write occurs in the address "
"range 1234-1239 inclusive.\n"
"\n"
- "wp 23456,a,w,wpdata == 1\n"
+ "wp 23456:data,a,w,wpdata == 1\n"
" Set a watchpoint that will halt execution whenever a write occurs in the address range "
- "23456-2345f AND the data written is equal to 1.\n"
+ "23456-2345f of the data space and the data written is equal to 1.\n"
"\n"
- "wp 3456,20,r,1,{printf \"Read @ %08X\\n\",wpaddr; g}\n"
- " Set a watchpoint that will halt execution whenever a read occurs in the address range "
- "3456-3475. When this happens, print Read @ <wpaddr> and continue executing.\n"
+ "wp 3456:maincpu,20,r,1,{ printf \"Read @ %08X\\n\",wpaddr ; g }\n"
+ " Set a watchpoint on the CPU ':maincpu' that will halt execution whenever a read occurs "
+ "in the address range 3456-3475. When this happens, print Read @ <wpaddr> and continue "
+ "execution.\n"
"\n"
- "temp0 = 0; wp 45678,1,w,wpdata==f0,{temp0++; g}\n"
- " Set a watchpoint that will halt execution whenever a write occurs to the address 45678 AND "
- "the value being written is equal to f0. When that happens, increment the variable temp0 and "
- "resume execution.\n"
+ "temp0 = 0 ; wp 45678,1,w,wpdata==f0,{ temp0++ ; g }\n"
+ " Set a watchpoint that will halt execution whenever a write occurs to the address 45678 "
+ "and the value being written is equal to f0. When that happens, increment the variable "
+ "temp0 and continue execution.\n"
},
+ { "wpdset", "#wpset" },
+ { "wpiset", "#wpset" },
+ { "wposet", "#wpset" },
{
"wpclear",
"\n"
- " wpclear [<wpnum>]\n"
+ " wpclear [<wpnum>[,...]]\n"
"\n"
- "The wpclear command clears a watchpoint. If <wpnum> is specified, only the requested "
- "watchpoint is cleared, otherwise all watchpoints are cleared.\n"
+ "The wpclear command clears watchpoints. If <wpnum> is specified, only the requested "
+ "watchpoints are cleared; otherwise all watchpoints are cleared.\n"
"\n"
"Examples:\n"
"\n"
"wpclear 3\n"
- " Clear watchpoint index 3.\n"
+ " Clear the watchpoint with index 3.\n"
"\n"
"wpclear\n"
" Clear all watchpoints.\n"
@@ -1101,16 +1451,16 @@ static const help_item static_help_list[] =
{
"wpdisable",
"\n"
- " wpdisable [<wpnum>]\n"
+ " wpdisable [<wpnum>[,...]]\n"
"\n"
- "The wpdisable command disables a watchpoint. If <wpnum> is specified, only the requested "
- "watchpoint is disabled, otherwise all watchpoints are disabled. Note that disabling a "
+ "The wpdisable command disables watchpoints. If <wpnum> is specified, only the requested "
+ "watchpoints are disabled; otherwise all watchpoints are disabled. Note that disabling a "
"watchpoint does not delete it, it just temporarily marks the watchpoint as inactive.\n"
"\n"
"Examples:\n"
"\n"
"wpdisable 3\n"
- " Disable watchpoint index 3.\n"
+ " Disable the watchpoint with index 3.\n"
"\n"
"wpdisable\n"
" Disable all watchpoints.\n"
@@ -1118,15 +1468,15 @@ static const help_item static_help_list[] =
{
"wpenable",
"\n"
- " wpenable [<wpnum>]\n"
+ " wpenable [<wpnum>[,...]]\n"
"\n"
- "The wpenable command enables a watchpoint. If <wpnum> is specified, only the requested "
- "watchpoint is enabled, otherwise all watchpoints are enabled.\n"
+ "The wpenable command enables watchpoints. If <wpnum> is specified, only the requested "
+ "watchpoints are enabled; otherwise all watchpoints are enabled.\n"
"\n"
"Examples:\n"
"\n"
"wpenable 3\n"
- " Enable watchpoint index 3.\n"
+ " Enable the watchpoint with index 3.\n"
"\n"
"wpenable\n"
" Enable all watchpoints.\n"
@@ -1134,64 +1484,47 @@ static const help_item static_help_list[] =
{
"wplist",
"\n"
- " wplist\n"
+ " wplist [<CPU>]\n"
"\n"
- "The wplist command lists all the current watchpoints, along with their index and any "
- "conditions or actions attached to them.\n"
- },
- {
- "hotspot",
- "\n"
- " hotspot [<CPU>,[<depth>[,<hits>]]]\n"
- "\n"
- "The hotspot command attempts to help locate hotspots in the code where speedup opportunities "
- "might be present. <CPU>, which defaults to the currently active CPU, specified which "
- "processor's memory to track. <depth>, which defaults to 64, controls the depth of the search "
- "buffer. The search buffer tracks the last <depth> memory reads from unique PCs. The <hits> "
- "parameter, which defaults to 250, specifies the minimum number of hits to report.\n"
- "\n"
- "The basic theory of operation is like this: each memory read is trapped by the debugger and "
- "logged in the search buffer according to the address which was read and the PC that executed "
- "the opcode. If the search buffer already contains a matching entry, that entry's count is "
- "incremented and the entry is moved to the top of the list. If the search buffer does not "
- "contain a matching entry, the entry from the bottom of the list is removed, and a new entry "
- "is created at the top with an initial count of 1. Entries which fall off the bottom are "
- "examined and if their count is larger than <hits>, they are reported to the debugger "
- "console.\n"
+ "The wplist list current watchpoints, along with their indices and any associated "
+ "conditions or actions. If no <CPU> is specified, watchpoints for all CPUs in the system "
+ "will be listed; if a <CPU> is specified, only watchpoints for that CPU will be listed. "
+ "The <CPU> can be specified by tag or by debugger CPU number.\n"
"\n"
"Examples:\n"
"\n"
- "hotspot 0,10\n"
- " Looks for hotspots on CPU 0 using a search buffer of 16 entries, reporting any entries which "
- "end up with 250 or more hits.\n"
+ "wplist\n"
+ " List all watchpoints.\n"
+ "\n"
+ "wplist .\n"
+ " List all watchpoints for the visible CPU.\n"
"\n"
- "hotspot 1,40,#1000\n"
- " Looks for hotspots on CPU 1 using a search buffer of 64 entries, reporting any entries which "
- "end up with 1000 or more hits.\n"
+ "wplist maincpu\n"
+ " List all watchpoints for the CPU ':maincpu'.\n"
},
{
"rpset",
"\n"
- " rp[set] {<condition>}[,<action>]]\n"
+ " rp[set] <condition>[,<action>]\n"
"\n"
- "Sets a new registerpoint which will be triggered when <condition> is met. The condition must "
- "be specified between curly braces to prevent the condition from being evaluated as an "
- "assignment.\n"
+ "Sets a new registerpoint which will be triggered when <condition> is true (evaluates to a "
+ "non-zero value). The condition must be embedded in braces { } to prevent it from being "
+ "interpreted as an assignment.\n"
"\n"
- "The optional <action> parameter provides a command that is executed whenever the registerpoint "
- "is hit. Note that you may need to embed the action within braces { } in "
- "order to prevent commas and semicolons from being interpreted as applying to the rpset command "
- "itself. Each registerpoint that is set is assigned an index which can be used in other "
- "registerpoint commands to reference this registerpoint.\n"
+ "The optional <action> parameter provides a command that is executed whenever the "
+ "registerpoint is hit. Note that you may need to embed the action within braces { } in "
+ "order to prevent commas and semicolons from being interpreted as applying to the rpset "
+ "command itself. Each registerpoint that is set is assigned an index which can be used in "
+ "other registerpoint commands to reference this registerpoint.\n"
"\n"
"Examples:\n"
"\n"
- "rp {PC==0150}\n"
- " Set a registerpoint that will halt execution whenever the PC register equals 0x150.\n"
+ "rp {PC==150}\n"
+ " Set a registerpoint that will halt execution whenever the PC register equals 150.\n"
"\n"
- "temp0=0; rp {PC==0150},{temp0++; g}\n"
+ "temp0=0; rp {PC==150},{temp0++; g}\n"
" Set a registerpoint that will increment the variable temp0 whenever the PC register "
- "equals 0x0150.\n"
+ "equals 150.\n"
"\n"
"rp {temp0==5}\n"
" Set a registerpoint that will halt execution whenever the temp0 variable equals 5.\n"
@@ -1199,15 +1532,15 @@ static const help_item static_help_list[] =
{
"rpclear",
"\n"
- " rpclear [<rpnum>]\n"
+ " rpclear [<rpnum>[,...]]\n"
"\n"
- "The rpclear command clears a registerpoint. If <rpnum> is specified, only the requested "
- "registerpoint is cleared, otherwise all registerpoints are cleared.\n"
+ "The rpclear command clears registerpoints. If <rpnum> is specified, only the requested "
+ "registerpoints are cleared, otherwise all registerpoints are cleared.\n"
"\n"
"Examples:\n"
"\n"
"rpclear 3\n"
- " Clear registerpoint index 3.\n"
+ " Clear the registerpoint with index 3.\n"
"\n"
"rpclear\n"
" Clear all registerpoints.\n"
@@ -1215,16 +1548,17 @@ static const help_item static_help_list[] =
{
"rpdisable",
"\n"
- " rpdisable [<rpnum>]\n"
+ " rpdisable [<rpnum>[,...]]\n"
"\n"
- "The rpdisable command disables a registerpoint. If <rpnum> is specified, only the requested "
- "registerpoint is disabled, otherwise all registerpoints are disabled. Note that disabling a "
- "registerpoint does not delete it, it just temporarily marks the registerpoint as inactive.\n"
+ "The rpdisable command disables registerpoints. If <rpnum> is specified, only the "
+ "requested registerpoints are disabled, otherwise all registerpoints are disabled. Note "
+ "that disabling a registerpoint does not delete it, it just temporarily marks the "
+ "registerpoint as inactive.\n"
"\n"
"Examples:\n"
"\n"
"rpdisable 3\n"
- " Disable registerpoint index 3.\n"
+ " Disable the registerpoint with index 3.\n"
"\n"
"rpdisable\n"
" Disable all registerpoints.\n"
@@ -1232,15 +1566,15 @@ static const help_item static_help_list[] =
{
"rpenable",
"\n"
- " rpenable [<rpnum>]\n"
+ " rpenable [<rpnum>[,...]]\n"
"\n"
- "The rpenable command enables a registerpoint. If <rpnum> is specified, only the requested "
- "registerpoint is enabled, otherwise all registerpoints are enabled.\n"
+ "The rpenable command enables registerpoints. If <rpnum> is specified, only the requested "
+ "registerpoints are enabled, otherwise all registerpoints are enabled.\n"
"\n"
"Examples:\n"
"\n"
"rpenable 3\n"
- " Enable registerpoint index 3.\n"
+ " Enable the registerpoint with index 3.\n"
"\n"
"rpenable\n"
" Enable all registerpoints.\n"
@@ -1254,33 +1588,141 @@ static const help_item static_help_list[] =
"actions attached to them.\n"
},
{
+ "epset",
+ "\n"
+ " ep[set] <type>[,<condition>[,<action>]]\n"
+ "\n"
+ "Sets a new exceptionpoint for exceptions of type <type> on the currently visible CPU. "
+ "The optional <condition> parameter lets you specify an expression that will be evaluated "
+ "each time the exceptionpoint is hit. If the result of the expression is true (non-zero), "
+ "the exceptionpoint will actually halt execution at the start of the exception handler; "
+ "otherwise, execution will continue with no notification. The optional <action> parameter "
+ "provides a command that is executed whenever the exceptionpoint is hit and the "
+ "<condition> is true. Note that you may need to embed the action within braces { } in order "
+ "to prevent commas and semicolons from being interpreted as applying to the epset command "
+ "itself.\n"
+ "\n"
+ "The numbering of exceptions depends upon the CPU type. Causes of exceptions may include "
+ "internally or externally vectored interrupts, errors occurring within instructions and "
+ "system calls.\n"
+ "\n"
+ "Each exceptionpoint that is set is assigned an index which can be used in other "
+ "exceptionpoint commands to reference this exceptionpoint.\n"
+ "\n"
+ "Examples:\n"
+ "\n"
+ "ep 2\n"
+ " Set an exception that will halt execution whenever the visible CPU raises exception "
+ "number 2.\n"
+ },
+ {
+ "epclear",
+ "\n"
+ " epclear [<epnum>[,...]]\n"
+ "\n"
+ "The epclear command clears exceptionpoints. If <epnum> is specified, only the requested "
+ "exceptionpoints are cleared, otherwise all exceptionpoints are cleared.\n"
+ "\n"
+ "Examples:\n"
+ "\n"
+ "epclear 3\n"
+ " Clear exceptionpoint index 3.\n"
+ "\n"
+ "epclear\n"
+ " Clear all exceptionpoints.\n"
+ },
+ {
+ "epdisable",
+ "\n"
+ " epdisable [<epnum>[,...]]\n"
+ "\n"
+ "The epdisable command disables exceptionpoints. If <epnum> is specified, only the requested "
+ "exceptionpoints are disabled, otherwise all exceptionpoints are disabled. Note that "
+ "disabling an exceptionpoint does not delete it, it just temporarily marks the "
+ "exceptionpoint as inactive.\n"
+ "\n"
+ "Examples:\n"
+ "\n"
+ "epdisable 3\n"
+ " Disable exceptionpoint index 3.\n"
+ "\n"
+ "epdisable\n"
+ " Disable all exceptionpoints.\n"
+ },
+ {
+ "epenable",
+ "\n"
+ " epenable [<epnum>[,...]]\n"
+ "\n"
+ "The epenable command enables exceptionpoints. If <epnum> is specified, only the "
+ "requested exceptionpoints are enabled, otherwise all exceptionpoints are enabled.\n"
+ "\n"
+ "Examples:\n"
+ "\n"
+ "epenable 3\n"
+ " Enable exceptionpoint index 3.\n"
+ "\n"
+ "epenable\n"
+ " Enable all exceptionpoints.\n"
+ },
+ {
+ "eplist",
+ "\n"
+ " eplist\n"
+ "\n"
+ "The eplist command lists all the current exceptionpoints, along with their index and "
+ "any conditions or actions attached to them.\n"
+ },
+ {
"map",
"\n"
- " map[{d|i}] <address>\n"
+ " map[{d|i|o}] <address>[:<space>]\n"
"\n"
- "The map/mapd/mapi commands map a logical address in memory to the correct physical address, as "
- "well as specifying the bank. 'map' will map program space memory, while 'mapd' will map data space "
- "memory and 'mapi' will map I/O space memory.\n"
+ "The map commands map a logical memory address to the corresponding physical address, as "
+ "well as reporting the handler name. The address may optionally be followed by a colon "
+ "and device and/or address space. The device may be specified as a tag or a debugger CPU "
+ "number; if no device is specified, the CPU currently visible in the debugger is assumed. "
+ "If an address space is not specified, the command suffix sets the address space: 'map' "
+ "defaults to the first address space exposed by the device, 'mapd' defaults to the data "
+ "space, 'mapi' defaults to the I/O space, and 'mapo' defaults to the opcodes space.\n"
"\n"
- "Example:\n"
+ "Examples:\n"
"\n"
"map 152d0\n"
- " Gives physical address and bank for logical address 152d0 in program memory\n"
+ " Gives physical address and handler name for logical address 152d0 in program memory for "
+ "the currently visible CPU.\n"
+ "\n"
+ "map 107:sms_vdp\n"
+ " Gives physical address and handler name for logical address 107 in the first address "
+ "space for the device ':sms_vdp'.\n"
},
+ { "mapd", "#map" },
+ { "mapi", "#map" },
+ { "mapo", "#map" },
{
"memdump",
"\n"
- " memdump [<filename>]\n"
+ " memdump [<filename>,[<device>]]\n"
"\n"
- "Dumps the current memory map to <filename>. If <filename> is omitted, then dumps to memdump.log"
+ "Dumps the current memory maps to the file specified by <filename>, or memdump.log if "
+ "omitted. If <device> is specified, only memory maps for the part of the device tree "
+ "rooted at this device will be dumped. Devices may be specified using tags or CPU "
+ "numbers.\n"
"\n"
"Examples:\n"
"\n"
"memdump mylog.log\n"
- " Dumps memory to mylog.log.\n"
+ " Dumps memory maps for all devices in the system to the file mylog.log.\n"
"\n"
"memdump\n"
- " Dumps memory to memdump.log.\n"
+ " Dumps memory maps for all devices in the system to the file memdump.log.\n"
+ "\n"
+ "memdump audiomaps.log,audiopcb\n"
+ " Dumps memory maps for device ':audiopcb' and all its child devices to the file "
+ "audiomaps.log.\n"
+ "\n"
+ "memdump mylog.log,1\n"
+ " Dumps memory maps for the CPU 1 and all its child devices to the file mylog.log.\n"
},
{
"comlist",
@@ -1311,6 +1753,7 @@ static const help_item static_help_list[] =
" Adds the comment 'undocumented opcode!' to the code at address 0x10\n"
"\n"
},
+ { "//", "#comadd" },
{
"commit",
"\n"
@@ -1322,10 +1765,10 @@ static const help_item static_help_list[] =
"Examples:\n"
"\n"
"commit 0, hello world.\n"
- " Adds the comment 'hello world.' to the code at address 0x0\n"
+ " Adds the comment 'hello world.' to the code at address 0x0 and saves comments\n"
"\n"
"/* 10, undocumented opcode!\n"
- " Adds the comment 'undocumented opcode!' to the code at address 0x10\n"
+ " Adds the comment 'undocumented opcode!' to the code at address 0x10 and saves comments\n"
"\n"
},
{
@@ -1358,240 +1801,322 @@ static const help_item static_help_list[] =
{
"cheatinit",
"\n"
- " cheatinit [<sign><width><swap>,[<address>,<length>[,<CPU>]]]\n"
+ " cheatinit [[<sign>[<width>[<swap>]]],[<address>,<length>[,<space>]]]\n"
"\n"
- "The cheatinit command initializes the cheat search to the selected memory area.\n"
- "If no parameter is specified the cheat search is initialized to all changeable memory of the main CPU.\n"
- "<sign> can be s(signed) or u(unsigned)\n"
- "<width> can be b(8 bit), w(16 bit), d(32 bit) or q(64 bit)\n"
- "<swap> append s for swapped search\n"
+ "The cheatinit command initializes the cheat search to writable RAM areas in the specified "
+ "address space. The device may be specified as a tag or a debugger CPU number; if no "
+ "device is specified, the CPU currently visible in the debugger is assumed. If an address "
+ "space is not specified, the first address space exposed by the device is used.\n"
+ "\n"
+ "The first argument specifies the data format to search for:\n"
+ " <sign>: 's' (signed), 'u' (unsigned)\n"
+ " <width>: 'b' (8-bit), 'w' (16-bit), 'd' (32-bit), 'q' (64-bit)\n"
+ " <swap>: 's' (reverse byte order)\n"
+ "If the first argument is omitted or empty, the format from the previous cheat search is "
+ "used, or unsigned 8-bit for the first cheat search.\n"
+ "\n"
+ "The <address> specifies the address to start searching from, and the <length> specifies "
+ "how much memory to search. If specified, writable RAM in the range <address> through "
+ "<address>+<length>-1, inclusive, will be searched; otherwise, all writable RAM in the "
+ "address space will be searched.\n"
"\n"
"Examples:\n"
"\n"
"cheatinit ub,0x1000,0x10\n"
- " Initialize the cheat search from 0x1000 to 0x1010 of the first CPU.\n"
+ " Initialize the cheat search for unsigned 8-bit values in addresses 0x1000-0x100f in the "
+ "program space of the visible CPU.\n"
"\n"
"cheatinit sw,0x2000,0x1000,1\n"
- " Initialize the cheat search with width of 2 byte in signed mode from 0x2000 to 0x3000 of the second CPU.\n"
+ " Initialize the cheat search for signed 16-bit values in addresses 0x2000-0x3000 in the "
+ "program space of CPU #1.\n"
"\n"
"cheatinit uds,0x0000,0x1000\n"
- " Initialize the cheat search with width of 4 byte swapped from 0x0000 to 0x1000.\n"
+ " Initialize the cheat search for unsigned 32-bit values with reverse byte order in "
+ "addresses 0x0000-0x0fff of the program space of the visible CPU.\n"
},
{
"cheatrange",
"\n"
" cheatrange <address>,<length>\n"
"\n"
- "The cheatrange command adds the selected memory area to the cheat search.\n"
- "Before using cheatrange it is necessary to initialize the cheat search with cheatinit.\n"
+ "The cheatrange command adds writable RAM areas in the range <address> through "
+ "through <address>+<length>-1, inclusive, to the cheat search. Before using cheatrange, "
+ "the cheatinit command must be used to initialize the cheat search.\n"
"\n"
"Examples:\n"
"\n"
"cheatrange 0x1000,0x10\n"
- " Add the bytes from 0x1000 to 0x1010 to the cheat search.\n"
+ " Add addresses 0x1000-0x100f to the cheat search.\n"
},
{
"cheatnext",
"\n"
" cheatnext <condition>[,<comparisonvalue>]\n"
"\n"
- "The cheatnext command will make comparisons with the last search matches.\n"
+ "The cheatnext command makes comparisons with the previous search matches.\n"
"Possible <condition>:\n"
" all\n"
- " no <comparisonvalue> needed.\n"
- " use to update the last value without changing the current matches.\n"
- " equal [eq]\n"
- " without <comparisonvalue> search for all bytes that are equal to the last search.\n"
- " with <comparisonvalue> search for all bytes that are equal to the <comparisonvalue>.\n"
- " notequal [ne]\n"
- " without <comparisonvalue> search for all bytes that are not equal to the last search.\n"
- " with <comparisonvalue> search for all bytes that are not equal to the <comparisonvalue>.\n"
- " decrease [de, +]\n"
- " without <comparisonvalue> search for all bytes that have decreased since the last search.\n"
- " with <comparisonvalue> search for all bytes that have decreased by the <comparisonvalue> since the last search.\n"
- " increase [in, -]\n"
- " without <comparisonvalue> search for all bytes that have increased since the last search.\n"
- " with <comparisonvalue> search for all bytes that have increased by the <comparisonvalue> since the last search.\n"
- " decreaseorequal [deeq]\n"
- " no <comparisonvalue> needed.\n"
- " search for all bytes that have decreased or have same value since the last search.\n"
- " increaseorequal [ineq]\n"
- " no <comparisonvalue> needed.\n"
- " search for all bytes that have decreased or have same value since the last search.\n"
- " smallerof [lt]\n"
- " without <comparisonvalue> this condition is invalid\n"
- " with <comparisonvalue> search for all bytes that are smaller than the <comparisonvalue>.\n"
- " greaterof [gt]\n"
- " without <comparisonvalue> this condition is invalid\n"
- " with <comparisonvalue> search for all bytes that are larger than the <comparisonvalue>.\n"
- " changedby [ch, ~]\n"
- " without <comparisonvalue> this condition is invalid\n"
- " with <comparisonvalue> search for all bytes that have changed by the <comparisonvalue> since the last search.\n"
+ " Use to update the last value without changing the current matches.\n"
+ " The <comparisonvalue> is not used.\n"
+ " equal (eq)\n"
+ " Without <comparisonvalue>**, search for values that are equal to the previous "
+ "search.\n"
+ " With <comparisonvalue>, search for values that are equal to the <comparisonvalue>.\n"
+ " notequal (ne)\n"
+ " Without <comparisonvalue>, search for values that are not equal to the previous "
+ "search.\n"
+ " With <comparisonvalue>, search for values that are not equal to the <comparisonvalue>.\n"
+ " decrease (de, -)\n"
+ " Without <comparisonvalue>, search for values that have decreased since the previous "
+ "search.\n"
+ " With <comparisonvalue>, search for values that have decreased by the <comparisonvalue> "
+ "since the previous search.\n"
+ " increase (in, +)\n"
+ " Without <comparisonvalue>, search for values that have increased since the previous "
+ "search.\n"
+ " With <comparisonvalue>, search for values that have increased by the <comparisonvalue> "
+ "since the previous search.\n"
+ " decreaseorequal (deeq)\n"
+ " Search for values that have decreased or are unchanged since the previous search.\n"
+ " The <comparisonvalue> is not used.\n"
+ " increaseorequal (ineq)\n"
+ " Search for values that have increased or are unchanged since the previous search.\n"
+ " The <comparisonvalue> is not used.\n"
+ " smallerof (lt, <)\n"
+ " Search for values that are less than the <comparisonvalue>.\n"
+ " The <comparisonvalue> is required.\n"
+ " greaterof (gt, >)\n"
+ " Search for values that are greater than the <comparisonvalue>.\n"
+ " The <comparisonvalue> is required.\n"
+ " changedby (ch, ~)\n"
+ " Search for values that have changed by the <comparisonvalue> since the previous "
+ "search.\n"
+ " The <comparisonvalue> is required.\n"
"\n"
"Examples:\n"
"\n"
"cheatnext increase\n"
- " search for all bytes that have increased since the last search.\n"
+ " Search for all values that have increased since the previous search.\n"
"\n"
- "cheatnext decrease, 1\n"
- " search for all bytes that have decreased by 1 since the last search.\n"
+ "cheatnext decrease,1\n"
+ " Search for all values that have decreased by 1 since the previous search.\n"
},
{
"cheatnextf",
"\n"
" cheatnextf <condition>[,<comparisonvalue>]\n"
"\n"
- "The cheatnextf command will make comparisons with the initial search.\n"
+ "The cheatnextf command makes comparisons with the initial search matches.\n"
"Possible <condition>:\n"
" all\n"
- " no <comparisonvalue> needed.\n"
- " use to update the last value without changing the current matches.\n"
- " equal [eq]\n"
- " without <comparisonvalue> search for all bytes that are equal to the initial search.\n"
- " with <comparisonvalue> search for all bytes that are equal to the <comparisonvalue>.\n"
- " notequal [ne]\n"
- " without <comparisonvalue> search for all bytes that are not equal to the initial search.\n"
- " with <comparisonvalue> search for all bytes that are not equal to the <comparisonvalue>.\n"
- " decrease [de, +]\n"
- " without <comparisonvalue> search for all bytes that have decreased since the initial search.\n"
- " with <comparisonvalue> search for all bytes that have decreased by the <comparisonvalue> since the initial search.\n"
- " increase [in, -]\n"
- " without <comparisonvalue> search for all bytes that have increased since the initial search.\n"
- " with <comparisonvalue> search for all bytes that have increased by the <comparisonvalue> since the initial search.\n"
- " decreaseorequal [deeq]\n"
- " no <comparisonvalue> needed.\n"
- " search for all bytes that have decreased or have same value since the initial search.\n"
- " increaseorequal [ineq]\n"
- " no <comparisonvalue> needed.\n"
- " search for all bytes that have decreased or have same value since the initial search.\n"
- " smallerof [lt]\n"
- " without <comparisonvalue> this condition is invalid.\n"
- " with <comparisonvalue> search for all bytes that are smaller than the <comparisonvalue>.\n"
- " greaterof [gt]\n"
- " without <comparisonvalue> this condition is invalid.\n"
- " with <comparisonvalue> search for all bytes that are larger than the <comparisonvalue>.\n"
- " changedby [ch, ~]\n"
- " without <comparisonvalue> this condition is invalid\n"
- " with <comparisonvalue> search for all bytes that have changed by the <comparisonvalue> since the initial search.\n"
+ " Use to update the last value without changing the current matches.\n"
+ " The <comparisonvalue> is not used.\n"
+ " equal (eq)\n"
+ " Without <comparisonvalue>**, search for values that are equal to the initial search.\n"
+ " With <comparisonvalue>, search for values that are equal to the <comparisonvalue>.\n"
+ " notequal (ne)\n"
+ " Without <comparisonvalue>, search for values that are not equal to the initial search.\n"
+ " With <comparisonvalue>, search for values that are not equal to the <comparisonvalue>.\n"
+ " decrease (de, -)\n"
+ " Without <comparisonvalue>, search for values that have decreased since the initial "
+ "search.\n"
+ " With <comparisonvalue>, search for values that have decreased by the <comparisonvalue> "
+ "since the initial search.\n"
+ " increase (in, +)\n"
+ " Without <comparisonvalue>, search for values that have increased since the initial "
+ "search.\n"
+ " With <comparisonvalue>, search for values that have increased by the <comparisonvalue> "
+ "since the initial search.\n"
+ " decreaseorequal (deeq)\n"
+ " Search for values that have decreased or are unchanged since the initial search.\n"
+ " The <comparisonvalue> is not used.\n"
+ " increaseorequal (ineq)\n"
+ " Search for values that have increased or are unchanged since the initial search.\n"
+ " The <comparisonvalue> is not used.\n"
+ " smallerof (lt, <)\n"
+ " Search for values that are less than the <comparisonvalue>.\n"
+ " The <comparisonvalue> is required.\n"
+ " greaterof (gt, >)\n"
+ " Search for values that are greater than the <comparisonvalue>.\n"
+ " The <comparisonvalue> is required.\n"
+ " changedby (ch, ~)\n"
+ " Search for values that have changed by the <comparisonvalue> since the initial search.\n"
+ " The <comparisonvalue> is required.\n"
"\n"
"Examples:\n"
"\n"
"cheatnextf increase\n"
- " search for all bytes that have increased since the initial search.\n"
+ " Search for all values that have increased since the initial search.\n"
"\n"
- "cheatnextf decrease, 1\n"
- " search for all bytes that have decreased by 1 since the initial search.\n"
+ "cheatnextf decrease,1\n"
+ " Search for all values that have decreased by 1 since the initial search.\n"
},
{
"cheatlist",
"\n"
" cheatlist [<filename>]\n"
"\n"
- "Without <filename> show the list of matches in the debug console.\n"
- "With <filename> save the list of matches in basic xml format to <filename>.\n"
+ "Without <filename>, show the current cheat matches in the debugger console; with "
+ "<filename>, save the current cheat matches in basic XML format to the specified file.\n"
"\n"
"Examples:\n"
"\n"
"cheatlist\n"
- " Show the current matches in the debug console.\n"
- "cheatlist cheat.txt\n"
- " Save the current matches to cheat.txt.\n"
+ " Show the current matches in the console.\n"
+ "cheatlist cheat.xml\n"
+ " Save the current matches to cheat.xml in XML format.\n"
},
{
"cheatundo",
"\n"
" cheatundo\n"
"\n"
- "Undo the results of the last search.\n"
- "The undo command has no effect on the last value.\n"
+ "Undo filtering of cheat candidates by the most recent cheatnext or cheatnextf command.\n"
+ "Note that the previous values ARE NOT rolled back.\n"
"\n"
"Examples:\n"
"\n"
"cheatundo\n"
- " Undo the last search (state only).\n"
+ " Restore cheat candidates filtered out by the most recent cheatnext or cheatnextf "
+ "command.\n"
},
{
"images",
"\n"
" images\n"
"\n"
- "Used to display list of available image devices.\n"
+ "Lists the instance names for media images devices in the system and the currently mounted "
+ "media images, if any. Brief instance names, as allowed for command line media options, "
+ "are listed.\n"
"\n"
"Examples:\n"
"\n"
"images\n"
- " Show list of devices and mounted files for current driver.\n"
+ " Lists image device instance names and mounted media.\n"
},
{
"mount",
"\n"
- " mount <device>,<filename>\n"
+ " mount <instance>,<filename>\n"
"\n"
- "Mount <filename> to image <device>.\n"
- "<filename> can be softlist item or full path to file.\n"
+ "Mounts a file on a media device. The device may be specified by its instance name or "
+ "brief instance name, as allowed for command line media options.\n"
+ "\n"
+ "Some media devices allow software list items to be mounted using this command by supplying "
+ "the short name of the software list item in place of a filename for the <filename> "
+ "parameter.\n"
"\n"
"Examples:\n"
"\n"
- "mount cart,aladdin\n"
- " Mounts softlist item aladdin on cart device.\n"
+ "mount flop1,os1xutls.td0\n"
+ " Mount the file 'os1xutls.td0' on the media device with instance name 'flop1'.\n"
+ "mount cart,10yard\n"
+ " Mount the software list item with short name '10yard' on the media device with instance "
+ "name 'cart'.\n"
},
{
"unmount",
"\n"
- " unmount <device>\n"
+ " unmount <instance>\n"
"\n"
- "Unmounts file from image <device>.\n"
+ "Unmounts the mounted media image (if any) from a device. The device may be specified by "
+ "its instance name or brief instance name, as allowed for command line media options.\n"
"\n"
"Examples:\n"
"\n"
"unmount cart\n"
- " Unmounts any file mounted on device named cart.\n"
+ " Unmounts any media image mounted on the device with instance name 'cart'.\n"
}
};
/***************************************************************************
- CODE
+ HELP_MANAGER
***************************************************************************/
-const char *debug_get_help(const char *tag)
+class help_manager
{
- static char ambig_message[1024];
- const help_item *found = nullptr;
- int i, msglen, foundcount = 0;
- int taglen = (int)strlen(tag);
- char tagcopy[256];
+private:
+ using help_map = std::map<std::string_view, char const *>;
- /* make a lowercase copy of the tag */
- for (i = 0; i <= taglen; i++)
- tagcopy[i] = tolower(u8(tag[i]));
+ help_map m_help_list;
+ help_item const *m_uncached_help = std::begin(f_static_help_list);
+
+ util::ovectorstream m_message_buffer;
+
+ help_manager() = default;
+
+public:
+ std::string_view find(std::string_view tag)
+ {
+ // find a cached exact match if possible
+ std::string const lower = strmakelower(tag);
+ auto const found = m_help_list.find(lower);
+ if (m_help_list.end() != found)
+ return found->second;
+
+ // cache more entries while searching for an exact match
+ while (std::end(f_static_help_list) != m_uncached_help)
+ {
+ help_map::iterator ins;
+ if (*m_uncached_help->help == '#')
+ {
+ auto const xref = m_help_list.find(&m_uncached_help->help[1]);
+ assert(m_help_list.end() != xref);
+ ins = m_help_list.emplace(m_uncached_help->tag, xref->second).first;
+ }
+ else
+ {
+ ins = m_help_list.emplace(m_uncached_help->tag, m_uncached_help->help).first;
+ }
+ ++m_uncached_help;
+ if (lower == ins->first)
+ return ins->second;
+ }
- /* find a match */
- for (i = 0; i < ARRAY_LENGTH(static_help_list); i++)
- if (!strncmp(static_help_list[i].tag, tagcopy, taglen))
+ // find a partial match
+ auto candidate = m_help_list.lower_bound(lower);
+ if ((m_help_list.end() != candidate) && (candidate->first.substr(0, lower.length()) == lower))
{
- foundcount++;
- found = &static_help_list[i];
- if (strlen(found->tag) == taglen)
+ // if only one partial match, take it
+ auto const next = std::next(candidate);
+ if ((m_help_list.end() == next) || (next->first.substr(0, lower.length()) != lower))
+ return candidate->second;
+
+ m_message_buffer.clear();
+ m_message_buffer.reserve(1024);
+ m_message_buffer.seekp(0, util::ovectorstream::beg);
+ m_message_buffer << "Ambiguous help request, did you mean:\n";
+ do
{
- foundcount = 1;
- break;
+ util::stream_format(m_message_buffer, " help %.*s?\n", int(candidate->first.length()), &candidate->first[0]);
+ ++candidate;
}
+ while ((m_help_list.end() != candidate) && (candidate->first.substr(0, lower.length()) == lower));
+ return util::buf_to_string_view(m_message_buffer);
}
- /* only a single match makes sense */
- if (foundcount == 1)
- return found->help;
+ // take the first help entry if no matches at all
+ return f_static_help_list[0].help;
+ }
- /* if not found, return the first entry */
- if (foundcount == 0)
- return static_help_list[0].help;
+ static help_manager &instance()
+ {
+ static help_manager s_instance;
+ return s_instance;
+ }
+};
- /* otherwise, indicate ambiguous help */
- msglen = sprintf(ambig_message, "Ambiguous help request, did you mean:\n");
- for (i = 0; i < ARRAY_LENGTH(static_help_list); i++)
- if (!strncmp(static_help_list[i].tag, tagcopy, taglen))
- msglen += sprintf(&ambig_message[msglen], " help %s?\n", static_help_list[i].tag);
- return ambig_message;
+} // anonymous namespace
+
+
+
+/***************************************************************************
+ PUBLIC INTERFACE
+***************************************************************************/
+
+std::string_view debug_get_help(std::string_view tag)
+{
+ return help_manager::instance().find(tag);
}
diff --git a/src/emu/debug/debughlp.h b/src/emu/debug/debughlp.h
index 30cf6d5820f..9efe12acaca 100644
--- a/src/emu/debug/debughlp.h
+++ b/src/emu/debug/debughlp.h
@@ -13,12 +13,14 @@
#pragma once
+#include <string_view>
+
/***************************************************************************
FUNCTION PROTOTYPES
***************************************************************************/
-/* help management */
-const char * debug_get_help(const char *tag);
+// help management
+std::string_view debug_get_help(std::string_view tag);
#endif // MAME_EMU_DEBUG_DEBUGHLP_H
diff --git a/src/emu/debug/debugvw.cpp b/src/emu/debug/debugvw.cpp
index f351896480e..7412ddd2f60 100644
--- a/src/emu/debug/debugvw.cpp
+++ b/src/emu/debug/debugvw.cpp
@@ -2,24 +2,29 @@
// copyright-holders:Aaron Giles
/*********************************************************************
- debugvw.c
+ debugvw.cpp
Debugger view engine.
***************************************************************************/
#include "emu.h"
-#include "express.h"
#include "debugvw.h"
-#include "dvtext.h"
-#include "dvstate.h"
+
+#include "debugcpu.h"
+#include "dvbpoints.h"
#include "dvdisasm.h"
+#include "dvepoints.h"
#include "dvmemory.h"
-#include "dvbpoints.h"
+#include "dvrpoints.h"
+#include "dvstate.h"
+#include "dvtext.h"
#include "dvwpoints.h"
-#include "debugcpu.h"
+#include "express.h"
+
#include "debugger.h"
-#include <ctype.h>
+
+#include <cctype>
@@ -31,9 +36,8 @@
// debug_view_source - constructor
//-------------------------------------------------
-debug_view_source::debug_view_source(const char *name, device_t *device)
- : m_next(nullptr),
- m_name(name),
+debug_view_source::debug_view_source(std::string &&name, device_t *device)
+ : m_name(std::move(name)),
m_device(device)
{
}
@@ -232,10 +236,10 @@ void debug_view::set_source(const debug_view_source &source)
const debug_view_source *debug_view::source_for_device(device_t *device) const
{
- for (debug_view_source &source : m_source_list)
- if (device == source.device())
- return &source;
- return m_source_list.first();
+ for (auto &source : m_source_list)
+ if (device == source->device())
+ return source.get();
+ return first_source();
}
@@ -251,6 +255,7 @@ void debug_view::adjust_visible_x_for_cursor()
m_topleft.x = m_cursor.x;
else if (m_cursor.x >= m_topleft.x + m_visible.x - 1)
m_topleft.x = m_cursor.x - m_visible.x + 2;
+ m_topleft.x = (std::max)((std::min)(m_topleft.x, m_total.x - m_visible.x), 0);
}
@@ -266,6 +271,7 @@ void debug_view::adjust_visible_y_for_cursor()
m_topleft.y = m_cursor.y;
else if (m_cursor.y >= m_topleft.y + m_visible.y - 1)
m_topleft.y = m_cursor.y - m_visible.y + 2;
+ m_topleft.y = (std::max)((std::min)(m_topleft.y, m_total.y - m_visible.y), 0);
}
@@ -327,7 +333,7 @@ debug_view_manager::~debug_view_manager()
{
debug_view *oldhead = m_viewlist;
m_viewlist = oldhead->m_next;
- global_free(oldhead);
+ delete oldhead;
}
}
@@ -341,25 +347,31 @@ debug_view *debug_view_manager::alloc_view(debug_view_type type, debug_view_osd_
switch (type)
{
case DVT_CONSOLE:
- return append(global_alloc(debug_view_console(machine(), osdupdate, osdprivate)));
+ return append(new debug_view_console(machine(), osdupdate, osdprivate));
case DVT_STATE:
- return append(global_alloc(debug_view_state(machine(), osdupdate, osdprivate)));
+ return append(new debug_view_state(machine(), osdupdate, osdprivate));
case DVT_DISASSEMBLY:
- return append(global_alloc(debug_view_disasm(machine(), osdupdate, osdprivate)));
+ return append(new debug_view_disasm(machine(), osdupdate, osdprivate));
case DVT_MEMORY:
- return append(global_alloc(debug_view_memory(machine(), osdupdate, osdprivate)));
+ return append(new debug_view_memory(machine(), osdupdate, osdprivate));
case DVT_LOG:
- return append(global_alloc(debug_view_log(machine(), osdupdate, osdprivate)));
+ return append(new debug_view_log(machine(), osdupdate, osdprivate));
case DVT_BREAK_POINTS:
- return append(global_alloc(debug_view_breakpoints(machine(), osdupdate, osdprivate)));
+ return append(new debug_view_breakpoints(machine(), osdupdate, osdprivate));
case DVT_WATCH_POINTS:
- return append(global_alloc(debug_view_watchpoints(machine(), osdupdate, osdprivate)));
+ return append(new debug_view_watchpoints(machine(), osdupdate, osdprivate));
+
+ case DVT_REGISTER_POINTS:
+ return append(new debug_view_registerpoints(machine(), osdupdate, osdprivate));
+
+ case DVT_EXCEPTION_POINTS:
+ return append(new debug_view_exceptionpoints(machine(), osdupdate, osdprivate));
default:
fatalerror("Attempt to create invalid debug view type %d\n", type);
@@ -379,7 +391,7 @@ void debug_view_manager::free_view(debug_view &view)
if (*viewptr == &view)
{
*viewptr = view.m_next;
- global_free(&view);
+ delete &view;
break;
}
}
@@ -450,7 +462,7 @@ debug_view_expression::debug_view_expression(running_machine &machine)
: m_machine(machine)
, m_dirty(true)
, m_result(0)
- , m_parsed(machine.debugger().cpu().get_global_symtable())
+ , m_parsed(machine.debugger().cpu().global_symtable())
, m_string("0")
{
}
@@ -472,7 +484,10 @@ debug_view_expression::~debug_view_expression()
void debug_view_expression::set_context(symbol_table *context)
{
- m_parsed.set_symbols((context != nullptr) ? context : m_machine.debugger().cpu().get_global_symtable());
+ if (context != nullptr)
+ m_parsed.set_symbols(*context);
+ else
+ m_parsed.set_symbols(m_machine.debugger().cpu().global_symtable());
m_dirty = true;
}
@@ -492,11 +507,11 @@ bool debug_view_expression::recompute()
std::string oldstring(m_parsed.original_string());
try
{
- m_parsed.parse(m_string.c_str());
+ m_parsed.parse(m_string);
}
catch (expression_error &)
{
- m_parsed.parse(oldstring.c_str());
+ m_parsed.parse(oldstring);
}
}
diff --git a/src/emu/debug/debugvw.h b/src/emu/debug/debugvw.h
index 374af7f384e..64bd5dac10a 100644
--- a/src/emu/debug/debugvw.h
+++ b/src/emu/debug/debugvw.h
@@ -13,6 +13,12 @@
#include "express.h"
+#include <algorithm>
+#include <iterator>
+#include <memory>
+#include <string>
+#include <vector>
+
//**************************************************************************
// CONSTANTS
@@ -28,7 +34,9 @@ enum debug_view_type
DVT_MEMORY,
DVT_LOG,
DVT_BREAK_POINTS,
- DVT_WATCH_POINTS
+ DVT_WATCH_POINTS,
+ DVT_REGISTER_POINTS,
+ DVT_EXCEPTION_POINTS
};
@@ -107,23 +115,19 @@ class debug_view_source
{
DISABLE_COPYING(debug_view_source);
- friend class simple_list<debug_view_source>;
-
public:
// construction/destruction
- debug_view_source(const char *name, device_t *device = nullptr);
+ debug_view_source(std::string &&name, device_t *device = nullptr);
virtual ~debug_view_source();
// getters
const char *name() const { return m_name.c_str(); }
- debug_view_source *next() const { return m_next; }
device_t *device() const { return m_device; }
private:
// internal state
- debug_view_source * m_next; // link to next item
- std::string m_name; // name of the source item
- device_t * m_device; // associated device (if applicable)
+ std::string const m_name; // name of the source item
+ device_t *const m_device; // associated device (if applicable)
};
@@ -149,9 +153,16 @@ public:
debug_view_xy cursor_position() { flush_updates(); return m_cursor; }
bool cursor_supported() { flush_updates(); return m_supports_cursor; }
bool cursor_visible() { flush_updates(); return m_cursor_visible; }
+ size_t source_count() const { return m_source_list.size(); }
const debug_view_source *source() const { return m_source; }
- const debug_view_source *first_source() const { return m_source_list.first(); }
- const simple_list<debug_view_source> &source_list() const { return m_source_list; }
+ const debug_view_source *source(unsigned i) const { return (m_source_list.size() > i) ? m_source_list[i].get() : nullptr; }
+ const debug_view_source *first_source() const { return m_source_list.empty() ? nullptr : m_source_list[0].get(); }
+ auto source_index(const debug_view_source &source) const
+ {
+ const auto it(std::find_if(m_source_list.begin(), m_source_list.end(), [&source] (const auto &x) { return x.get() == &source; }));
+ return (m_source_list.end() != it) ? std::distance(m_source_list.begin(), it) : -1;
+ }
+ const std::vector<std::unique_ptr<const debug_view_source> > &source_list() const { return m_source_list; }
// setters
void set_visible_size(debug_view_xy size);
@@ -188,7 +199,7 @@ protected:
debug_view * m_next; // link to the next view
debug_view_type m_type; // type of view
const debug_view_source *m_source; // currently selected data source
- simple_list<debug_view_source> m_source_list; // list of available data sources
+ std::vector<std::unique_ptr<const debug_view_source> > m_source_list; // list of available data sources
// OSD data
debug_view_osd_update_func m_osdupdate; // callback for the update
@@ -258,12 +269,13 @@ public:
u64 last_value() const { return m_result; }
u64 value() { recompute(); return m_result; }
const char *string() const { return m_string.c_str(); }
- symbol_table *context() const { return m_parsed.symbols(); }
+ symbol_table &context() const { return m_parsed.symbols(); }
// setters
void mark_dirty() { m_dirty = true; }
template <typename... Params> void set_string(Params &&... args) { m_string.assign(std::forward<Params>(args)...); m_dirty = true; }
void set_context(symbol_table *context);
+ void set_default_base(int base) { m_parsed.set_default_base(base); }
private:
// internal helpers
diff --git a/src/emu/debug/dvbpoints.cpp b/src/emu/debug/dvbpoints.cpp
index d86ce1150cc..0290f8719dc 100644
--- a/src/emu/debug/dvbpoints.cpp
+++ b/src/emu/debug/dvbpoints.cpp
@@ -1,79 +1,81 @@
// license:BSD-3-Clause
-// copyright-holders:Aaron Giles
+// copyright-holders:Andrew Gardner, Vas Crabb
/*********************************************************************
- dvbpoints.c
+ dvbpoints.cpp
Breakpoint debugger view.
***************************************************************************/
#include "emu.h"
-#include "debugger.h"
#include "dvbpoints.h"
+#include "debugcpu.h"
+#include "points.h"
+
#include <algorithm>
#include <iomanip>
// Sorting functors for the qsort function
-static bool cIndexAscending(const device_debug::breakpoint *a, const device_debug::breakpoint *b)
+static bool cIndexAscending(const debug_breakpoint *a, const debug_breakpoint *b)
{
return a->index() < b->index();
}
-static bool cIndexDescending(const device_debug::breakpoint *a, const device_debug::breakpoint *b)
+static bool cIndexDescending(const debug_breakpoint *a, const debug_breakpoint *b)
{
return cIndexAscending(b, a);
}
-static bool cEnabledAscending(const device_debug::breakpoint *a, const device_debug::breakpoint *b)
+static bool cEnabledAscending(const debug_breakpoint *a, const debug_breakpoint *b)
{
return !a->enabled() && b->enabled();
}
-static bool cEnabledDescending(const device_debug::breakpoint *a, const device_debug::breakpoint *b)
+static bool cEnabledDescending(const debug_breakpoint *a, const debug_breakpoint *b)
{
return cEnabledAscending(b, a);
}
-static bool cCpuAscending(const device_debug::breakpoint *a, const device_debug::breakpoint *b)
+static bool cCpuAscending(const debug_breakpoint *a, const debug_breakpoint *b)
{
return strcmp(a->debugInterface()->device().tag(), b->debugInterface()->device().tag()) < 0;
}
-static bool cCpuDescending(const device_debug::breakpoint *a, const device_debug::breakpoint *b)
+static bool cCpuDescending(const debug_breakpoint *a, const debug_breakpoint *b)
{
return cCpuAscending(b, a);
}
-static bool cAddressAscending(const device_debug::breakpoint *a, const device_debug::breakpoint *b)
+static bool cAddressAscending(const debug_breakpoint *a, const debug_breakpoint *b)
{
return a->address() < b->address();
}
-static bool cAddressDescending(const device_debug::breakpoint *a, const device_debug::breakpoint *b)
+static bool cAddressDescending(const debug_breakpoint *a, const debug_breakpoint *b)
{
return cAddressAscending(b, a);
}
-static bool cConditionAscending(const device_debug::breakpoint *a, const device_debug::breakpoint *b)
+static bool cConditionAscending(const debug_breakpoint *a, const debug_breakpoint *b)
{
return strcmp(a->condition(), b->condition()) < 0;
}
-static bool cConditionDescending(const device_debug::breakpoint *a, const device_debug::breakpoint *b)
+static bool cConditionDescending(const debug_breakpoint *a, const debug_breakpoint *b)
{
return cConditionAscending(b, a);
}
-static bool cActionAscending(const device_debug::breakpoint *a, const device_debug::breakpoint *b)
+static bool cActionAscending(const debug_breakpoint *a, const debug_breakpoint *b)
{
- return strcmp(a->action(), b->action()) < 0;
+ return a->action() < b->action();
}
-static bool cActionDescending(const device_debug::breakpoint *a, const device_debug::breakpoint *b)
+static bool cActionDescending(const debug_breakpoint *a, const debug_breakpoint *b)
{
return cActionAscending(b, a);
}
@@ -96,7 +98,7 @@ debug_view_breakpoints::debug_view_breakpoints(running_machine &machine, debug_v
{
// fail if no available sources
enumerate_sources();
- if (m_source_list.count() == 0)
+ if (m_source_list.empty())
throw std::bad_alloc();
}
@@ -118,18 +120,20 @@ debug_view_breakpoints::~debug_view_breakpoints()
void debug_view_breakpoints::enumerate_sources()
{
// start with an empty list
- m_source_list.reset();
+ m_source_list.clear();
// iterate over devices with disassembly interfaces
- for (device_disasm_interface &dasm : disasm_interface_iterator(machine().root_device()))
+ for (device_disasm_interface &dasm : disasm_interface_enumerator(machine().root_device()))
{
- std::string name;
- name = string_format("%s '%s'", dasm.device().name(), dasm.device().tag());
- m_source_list.append(*global_alloc(debug_view_source(name.c_str(), &dasm.device())));
+ m_source_list.emplace_back(
+ std::make_unique<debug_view_source>(
+ util::string_format("%s '%s'", dasm.device().name(), dasm.device().tag()),
+ &dasm.device()));
}
// reset the source to a known good entry
- set_source(*m_source_list.first());
+ if (!m_source_list.empty())
+ set_source(*m_source_list[0]);
}
@@ -167,7 +171,7 @@ void debug_view_breakpoints::view_click(const int button, const debug_view_xy& p
return;
// Enable / disable
- const_cast<device_debug::breakpoint &>(*m_buffer[bpIndex]).setEnabled(!m_buffer[bpIndex]->enabled());
+ const_cast<debug_breakpoint &>(*m_buffer[bpIndex]).setEnabled(!m_buffer[bpIndex]->enabled());
machine().debug_view().update_all(DVT_DISASSEMBLY);
}
@@ -189,12 +193,12 @@ void debug_view_breakpoints::pad_ostream_to_length(std::ostream& str, int len)
void debug_view_breakpoints::gather_breakpoints()
{
m_buffer.resize(0);
- for (const debug_view_source &source : m_source_list)
+ for (auto &source : m_source_list)
{
// Collect
- device_debug &debugInterface = *source.device()->debug();
- for (const device_debug::breakpoint &bp : debugInterface.breakpoint_list())
- m_buffer.push_back(&bp);
+ device_debug &debugInterface = *source->device()->debug();
+ for (const auto &bpp : debugInterface.breakpoint_list())
+ m_buffer.push_back(bpp.second.get());
}
// And now for the sort
@@ -214,7 +218,7 @@ void debug_view_breakpoints::view_update()
gather_breakpoints();
// Set the view region so the scroll bars update
- m_total.x = tableBreaks[ARRAY_LENGTH(tableBreaks) - 1];
+ m_total.x = tableBreaks[std::size(tableBreaks) - 1];
m_total.y = m_buffer.size() + 1;
if (m_total.y < 10)
m_total.y = 10;
@@ -222,7 +226,7 @@ void debug_view_breakpoints::view_update()
// Draw
debug_view_char *dest = &m_viewdata[0];
util::ovectorstream linebuf;
- linebuf.reserve(ARRAY_LENGTH(tableBreaks) - 1);
+ linebuf.reserve(std::size(tableBreaks) - 1);
// Header
if (m_visible.y > 0)
@@ -268,7 +272,7 @@ void debug_view_breakpoints::view_update()
int bpi = row + m_topleft.y - 1;
if ((bpi < m_buffer.size()) && (bpi >= 0))
{
- const device_debug::breakpoint *const bp = m_buffer[bpi];
+ const debug_breakpoint *const bp = m_buffer[bpi];
linebuf.clear();
linebuf.rdbuf()->clear();
diff --git a/src/emu/debug/dvbpoints.h b/src/emu/debug/dvbpoints.h
index 2cba6cd3081..be291fa66d8 100644
--- a/src/emu/debug/dvbpoints.h
+++ b/src/emu/debug/dvbpoints.h
@@ -1,5 +1,5 @@
// license:BSD-3-Clause
-// copyright-holders:Aaron Giles
+// copyright-holders:Andrew Gardner, Vas Crabb
/*********************************************************************
dvbpoints.h
@@ -7,19 +7,14 @@
Breakpoint debugger view.
***************************************************************************/
-
#ifndef MAME_EMU_DEBUG_DVBPOINTS_H
#define MAME_EMU_DEBUG_DVBPOINTS_H
#pragma once
#include "debugvw.h"
-#include "debugcpu.h"
-
-//**************************************************************************
-// CONSTANTS
-//**************************************************************************
+#include <vector>
//**************************************************************************
@@ -46,10 +41,9 @@ private:
void pad_ostream_to_length(std::ostream& str, int len);
void gather_breakpoints();
-
// internal state
- bool (*m_sortType)(const device_debug::breakpoint *, const device_debug::breakpoint *);
- std::vector<const device_debug::breakpoint *> m_buffer;
+ bool (*m_sortType)(const debug_breakpoint *, const debug_breakpoint *);
+ std::vector<const debug_breakpoint *> m_buffer;
};
#endif // MAME_EMU_DEBUG_DVBPOINTS_H
diff --git a/src/emu/debug/dvdisasm.cpp b/src/emu/debug/dvdisasm.cpp
index 667542483a4..17cc766ef94 100644
--- a/src/emu/debug/dvdisasm.cpp
+++ b/src/emu/debug/dvdisasm.cpp
@@ -2,17 +2,18 @@
// copyright-holders:Aaron Giles, Olivier Galibert
/*********************************************************************
- dvdisasm.c
+ dvdisasm.cpp
Disassembly debugger view.
***************************************************************************/
#include "emu.h"
-#include "debugvw.h"
#include "dvdisasm.h"
+
+#include "debugbuf.h"
#include "debugcpu.h"
-#include "debugger.h"
+
//**************************************************************************
// DEBUG VIEW DISASM SOURCE
@@ -22,11 +23,15 @@
// debug_view_disasm_source - constructor
//-------------------------------------------------
-debug_view_disasm_source::debug_view_disasm_source(const char *name, device_t &device)
- : debug_view_source(name, &device),
+debug_view_disasm_source::debug_view_disasm_source(std::string &&name, device_t &device)
+ : debug_view_source(std::move(name), &device),
m_space(device.memory().space(AS_PROGRAM)),
- m_decrypted_space(device.memory().has_space(AS_OPCODES) ? device.memory().space(AS_OPCODES) : device.memory().space(AS_PROGRAM))
+ m_decrypted_space(device.memory().has_space(AS_OPCODES) ? device.memory().space(AS_OPCODES) : device.memory().space(AS_PROGRAM)),
+ m_pcbase(nullptr)
{
+ const device_state_interface *state;
+ if (device.interface(state))
+ m_pcbase = state->state_find_entry(STATE_GENPCBASE);
}
@@ -35,9 +40,6 @@ debug_view_disasm_source::debug_view_disasm_source(const char *name, device_t &d
// DEBUG VIEW DISASM
//**************************************************************************
-const int debug_view_disasm::DEFAULT_DASM_LINES, debug_view_disasm::DEFAULT_DASM_WIDTH, debug_view_disasm::DASM_MAX_BYTES;
-
-
//-------------------------------------------------
// debug_view_disasm - constructor
//-------------------------------------------------
@@ -52,16 +54,16 @@ debug_view_disasm::debug_view_disasm(running_machine &machine, debug_view_osd_up
{
// fail if no available sources
enumerate_sources();
- if(m_source_list.count() == 0)
+ if(m_source_list.empty())
throw std::bad_alloc();
// count the number of comments
- int total_comments = 0;
- for(const debug_view_source &source : m_source_list)
- {
- const debug_view_disasm_source &dasmsource = downcast<const debug_view_disasm_source &>(source);
- total_comments += dasmsource.device()->debug()->comment_count();
- }
+ //int total_comments = 0;
+ //for(auto &source : m_source_list)
+ //{
+ //const debug_view_disasm_source &dasmsource = downcast<const debug_view_disasm_source &>(*source);
+ //total_comments += dasmsource.device()->debug()->comment_count();
+ //}
// configure the view
m_total.y = DEFAULT_DASM_LINES;
@@ -86,19 +88,23 @@ debug_view_disasm::~debug_view_disasm()
void debug_view_disasm::enumerate_sources()
{
// start with an empty list
- m_source_list.reset();
+ m_source_list.clear();
// iterate over devices with disassembly interfaces
- std::string name;
- for(device_disasm_interface &dasm : disasm_interface_iterator(machine().root_device()))
+ for (device_disasm_interface &dasm : disasm_interface_enumerator(machine().root_device()))
{
- name = string_format("%s '%s'", dasm.device().name(), dasm.device().tag());
- if(dasm.device().memory().space_config(AS_PROGRAM)!=nullptr)
- m_source_list.append(*global_alloc(debug_view_disasm_source(name.c_str(), dasm.device())));
+ if (dasm.device().memory().space_config(AS_PROGRAM))
+ {
+ m_source_list.emplace_back(
+ std::make_unique<debug_view_disasm_source>(
+ util::string_format("%s '%s'", dasm.device().name(), dasm.device().tag()),
+ dasm.device()));
+ }
}
// reset the source to a known good entry
- set_source(*m_source_list.first());
+ if (!m_source_list.empty())
+ set_source(*m_source_list[0]);
}
@@ -113,7 +119,11 @@ void debug_view_disasm::view_notify(debug_view_notification type)
adjust_visible_y_for_cursor();
else if(type == VIEW_NOTIFY_SOURCE_CHANGED)
- m_expression.set_context(&downcast<const debug_view_disasm_source *>(m_source)->device()->debug()->symtable());
+ {
+ const debug_view_disasm_source &source = downcast<const debug_view_disasm_source &>(*m_source);
+ m_expression.set_context(&source.device()->debug()->symtable());
+ m_expression.set_default_base(source.space().is_octal() ? 8 : 16);
+ }
}
@@ -159,7 +169,7 @@ void debug_view_disasm::view_char(int chval)
case DCH_HOME: // set the active column to the PC
{
const debug_view_disasm_source &source = downcast<const debug_view_disasm_source &>(*m_source);
- offs_t pc = source.device()->state().pcbase() & source.m_space.logaddrmask();
+ offs_t pc = source.pcbase();
// figure out which row the pc is on
for(unsigned int curline = 0; curline < m_dasm.size(); curline++)
@@ -225,6 +235,7 @@ void debug_view_disasm::generate_from_address(debug_disasm_buffer &buffer, offs_
m_dasm.emplace_back(address, size, dasm);
address = next_address;
}
+ m_recompute = false;
}
bool debug_view_disasm::generate_with_pc(debug_disasm_buffer &buffer, offs_t pc)
@@ -242,6 +253,7 @@ bool debug_view_disasm::generate_with_pc(debug_disasm_buffer &buffer, offs_t pc)
backwards_offset = 64 << shift;
m_dasm.clear();
+ m_recompute = false;
offs_t address = (pc - m_backwards_steps*backwards_offset) & source.m_space.logaddrmask();
// Handle wrap at 0
if(address > pc)
@@ -316,7 +328,7 @@ void debug_view_disasm::generate_dasm(debug_disasm_buffer &buffer, offs_t pc)
return;
}
- if(address_position(pc) != -1) {
+ if(!m_recompute && address_position(pc) != -1) {
generate_from_address(buffer, m_dasm[0].m_address);
int pos = address_position(pc);
if(pos != -1) {
@@ -352,13 +364,7 @@ void debug_view_disasm::complete_information(const debug_view_disasm_source &sou
dasm.m_is_pc = adr == pc;
- dasm.m_is_bp = false;
- for(const device_debug::breakpoint &bp : source.device()->debug()->breakpoint_list())
- if(adr == (bp.address() & source.m_space.logaddrmask())) {
- dasm.m_is_bp = true;
- break;
- }
-
+ dasm.m_is_bp = source.device()->debug()->breakpoint_find(adr) != nullptr;
dasm.m_is_visited = source.device()->debug()->track_pc_visited(adr);
const char *comment = source.device()->debug()->comment_text(adr);
@@ -376,7 +382,7 @@ void debug_view_disasm::view_update()
{
const debug_view_disasm_source &source = downcast<const debug_view_disasm_source &>(*m_source);
debug_disasm_buffer buffer(*source.device());
- offs_t pc = source.device()->state().pcbase() & source.m_space.logaddrmask();
+ offs_t pc = source.pcbase();
generate_dasm(buffer, pc);
@@ -389,25 +395,25 @@ void debug_view_disasm::view_update()
// print - print a string in the disassembly view
//-------------------------------------------------
-void debug_view_disasm::print(int row, std::string text, int start, int end, u8 attrib)
+void debug_view_disasm::print(u32 row, std::string text, s32 start, s32 end, u8 attrib)
{
- int view_end = end - m_topleft.x;
+ s32 view_end = end - m_topleft.x;
if(view_end < 0)
return;
- int string_0 = start - m_topleft.x;
+ s32 string_0 = start - m_topleft.x;
if(string_0 >= m_visible.x)
return;
- int view_start = string_0 > 0 ? string_0 : 0;
+ s32 view_start = string_0 > 0 ? string_0 : 0;
debug_view_char *dest = &m_viewdata[row * m_visible.x + view_start];
if(view_end >= m_visible.x)
view_end = m_visible.x;
- for(int pos = view_start; pos < view_end; pos++) {
- int spos = pos - string_0;
- if(spos >= int(text.size()))
+ for(s32 pos = view_start; pos < view_end; pos++) {
+ s32 spos = pos - string_0;
+ if(spos >= s32(text.size()))
*dest++ = { ' ', attrib };
else
*dest++ = { u8(text[spos]), attrib };
@@ -422,13 +428,15 @@ void debug_view_disasm::print(int row, std::string text, int start, int end, u8
void debug_view_disasm::redraw()
{
// determine how many characters we need for an address and set the divider
- int m_divider1 = 1 + m_dasm[0].m_tadr.size() + 1;
+ s32 divider1 = 1 + m_dasm[0].m_tadr.size() + 1;
// assume a fixed number of characters for the disassembly
- int m_divider2 = m_divider1 + 1 + m_dasm_width + 1;
+ s32 divider2 = divider1 + 1 + m_dasm_width + 1;
// set the width of the third column to max comment length
- m_total.x = m_divider2 + 1 + 50; // DEBUG_COMMENT_MAX_LINE_LENGTH
+ m_total.x = divider2 + 1 + 50; // DEBUG_COMMENT_MAX_LINE_LENGTH
+
+ const s32 max_visible_col = m_topleft.x + m_visible.x;
// loop over visible rows
for(u32 row = 0; row < m_visible.y; row++)
@@ -455,22 +463,22 @@ void debug_view_disasm::redraw()
if(m_dasm[effrow].m_is_visited)
attrib |= DCA_VISITED;
- print(row, ' ' + m_dasm[effrow].m_tadr, 0, m_divider1, attrib | DCA_ANCILLARY);
- print(row, ' ' + m_dasm[effrow].m_dasm, m_divider1, m_divider2, attrib);
+ print(row, ' ' + m_dasm[effrow].m_tadr, 0, divider1, attrib | DCA_ANCILLARY);
+ print(row, ' ' + m_dasm[effrow].m_dasm, divider1, divider2, attrib);
if(m_right_column == DASM_RIGHTCOL_RAW || m_right_column == DASM_RIGHTCOL_ENCRYPTED) {
std::string text = ' ' +(m_right_column == DASM_RIGHTCOL_RAW ? m_dasm[effrow].m_topcodes : m_dasm[effrow].m_tparams);
- print(row, text, m_divider2, m_visible.x, attrib | DCA_ANCILLARY);
- if(int(text.size()) > m_visible.x - m_divider2) {
- int base = m_total.x - 3;
- if(base < m_divider2)
- base = m_divider2;
- print(row, "...", base, m_visible.x, attrib | DCA_ANCILLARY);
+ print(row, text, divider2, max_visible_col, attrib | DCA_ANCILLARY);
+ if(s32(text.size()) > max_visible_col - divider2) {
+ s32 base = max_visible_col - 3;
+ if(base < divider2)
+ base = divider2;
+ print(row, "...", base, max_visible_col, attrib | DCA_ANCILLARY);
}
} else if(!m_dasm[effrow].m_comment.empty())
- print(row, " // " + m_dasm[effrow].m_comment, m_divider2, m_visible.x, attrib | DCA_COMMENT | DCA_ANCILLARY);
+ print(row, " // " + m_dasm[effrow].m_comment, divider2, max_visible_col, attrib | DCA_COMMENT | DCA_ANCILLARY);
else
- print(row, "", m_divider2, m_visible.x, attrib | DCA_COMMENT | DCA_ANCILLARY);
+ print(row, "", divider2, max_visible_col, attrib | DCA_COMMENT | DCA_ANCILLARY);
}
}
}
@@ -511,7 +519,7 @@ void debug_view_disasm::set_right_column(disasm_right_column contents)
{
begin_update();
m_right_column = contents;
- m_recompute = m_update_pending = true;
+ m_update_pending = true;
end_update();
}
@@ -568,7 +576,7 @@ void debug_view_disasm::set_selected_address(offs_t address)
void debug_view_disasm::set_source(const debug_view_source &source)
{
if(&source != m_source) {
+ m_recompute = true;
debug_view::set_source(source);
- m_dasm.clear();
}
}
diff --git a/src/emu/debug/dvdisasm.h b/src/emu/debug/dvdisasm.h
index ef9e4209f1b..ef4c5e9cf87 100644
--- a/src/emu/debug/dvdisasm.h
+++ b/src/emu/debug/dvdisasm.h
@@ -14,9 +14,6 @@
#pragma once
#include "debugvw.h"
-#include "debugbuf.h"
-
-#include "vecstream.h"
//**************************************************************************
@@ -38,22 +35,27 @@ enum disasm_right_column
// TYPE DEFINITIONS
//**************************************************************************
+// forward declaration
+class debug_disasm_buffer;
+
// a disassembly view_source
class debug_view_disasm_source : public debug_view_source
{
friend class debug_view_disasm;
+public:
// construction/destruction
- debug_view_disasm_source(const char *name, device_t &device);
+ debug_view_disasm_source(std::string &&name, device_t &device);
-public:
// getters
address_space &space() const { return m_space; }
+ offs_t pcbase() const { return m_pcbase != nullptr ? m_pcbase->value() & m_space.logaddrmask() : 0; }
private:
// internal state
address_space & m_space; // address space to display
address_space & m_decrypted_space; // address space to display for decrypted opcodes
+ const device_state_entry *m_pcbase;
};
@@ -117,7 +119,7 @@ private:
void complete_information(const debug_view_disasm_source &source, debug_disasm_buffer &buffer, offs_t pc);
void enumerate_sources();
- void print(int row, std::string text, int start, int end, u8 attrib);
+ void print(u32 row, std::string text, s32 start, s32 end, u8 attrib);
void redraw();
// internal state
diff --git a/src/emu/debug/dvepoints.cpp b/src/emu/debug/dvepoints.cpp
new file mode 100644
index 00000000000..2ebc53a6609
--- /dev/null
+++ b/src/emu/debug/dvepoints.cpp
@@ -0,0 +1,314 @@
+// license:BSD-3-Clause
+// copyright-holders:Andrew Gardner, Vas Crabb
+/*********************************************************************
+
+ dvepoints.cpp
+
+ Exceptionpoint debugger view.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "dvepoints.h"
+
+#include "debugcpu.h"
+#include "points.h"
+
+#include <algorithm>
+#include <iomanip>
+
+
+
+// Sorting functors for the qsort function
+static bool cIndexAscending(const debug_exceptionpoint *a, const debug_exceptionpoint *b)
+{
+ return a->index() < b->index();
+}
+
+static bool cIndexDescending(const debug_exceptionpoint *a, const debug_exceptionpoint *b)
+{
+ return cIndexAscending(b, a);
+}
+
+static bool cEnabledAscending(const debug_exceptionpoint *a, const debug_exceptionpoint *b)
+{
+ return !a->enabled() && b->enabled();
+}
+
+static bool cEnabledDescending(const debug_exceptionpoint *a, const debug_exceptionpoint *b)
+{
+ return cEnabledAscending(b, a);
+}
+
+static bool cCpuAscending(const debug_exceptionpoint *a, const debug_exceptionpoint *b)
+{
+ return strcmp(a->debugInterface()->device().tag(), b->debugInterface()->device().tag()) < 0;
+}
+
+static bool cCpuDescending(const debug_exceptionpoint *a, const debug_exceptionpoint *b)
+{
+ return cCpuAscending(b, a);
+}
+
+static bool cTypeAscending(const debug_exceptionpoint *a, const debug_exceptionpoint *b)
+{
+ return a->type() < b->type();
+}
+
+static bool cTypeDescending(const debug_exceptionpoint *a, const debug_exceptionpoint *b)
+{
+ return cTypeAscending(b, a);
+}
+
+static bool cConditionAscending(const debug_exceptionpoint *a, const debug_exceptionpoint *b)
+{
+ return strcmp(a->condition(), b->condition()) < 0;
+}
+
+static bool cConditionDescending(const debug_exceptionpoint *a, const debug_exceptionpoint *b)
+{
+ return cConditionAscending(b, a);
+}
+
+static bool cActionAscending(const debug_exceptionpoint *a, const debug_exceptionpoint *b)
+{
+ return a->action() < b->action();
+}
+
+static bool cActionDescending(const debug_exceptionpoint *a, const debug_exceptionpoint *b)
+{
+ return cActionAscending(b, a);
+}
+
+
+//**************************************************************************
+// DEBUG VIEW BREAK POINTS
+//**************************************************************************
+
+static const int tableBreaks[] = { 5, 9, 31, 45, 63, 80 };
+
+
+//-------------------------------------------------
+// debug_view_exceptionpoints - constructor
+//-------------------------------------------------
+
+debug_view_exceptionpoints::debug_view_exceptionpoints(running_machine &machine, debug_view_osd_update_func osdupdate, void *osdprivate)
+ : debug_view(machine, DVT_EXCEPTION_POINTS, osdupdate, osdprivate)
+ , m_sortType(cIndexAscending)
+{
+ // fail if no available sources
+ enumerate_sources();
+ if (m_source_list.empty())
+ throw std::bad_alloc();
+}
+
+
+//-------------------------------------------------
+// ~debug_view_exceptionpoints - destructor
+//-------------------------------------------------
+
+debug_view_exceptionpoints::~debug_view_exceptionpoints()
+{
+}
+
+
+//-------------------------------------------------
+// enumerate_sources - enumerate all possible
+// sources for a disassembly view
+//-------------------------------------------------
+
+void debug_view_exceptionpoints::enumerate_sources()
+{
+ // start with an empty list
+ m_source_list.clear();
+
+ // iterate over devices with disassembly interfaces
+ for (device_disasm_interface &dasm : disasm_interface_enumerator(machine().root_device()))
+ {
+ m_source_list.emplace_back(
+ std::make_unique<debug_view_source>(
+ util::string_format("%s '%s'", dasm.device().name(), dasm.device().tag()),
+ &dasm.device()));
+ }
+
+ // reset the source to a known good entry
+ if (!m_source_list.empty())
+ set_source(*m_source_list[0]);
+}
+
+
+//-------------------------------------------------
+// view_click - handle a mouse click within the
+// current view
+//-------------------------------------------------
+
+void debug_view_exceptionpoints::view_click(const int button, const debug_view_xy& pos)
+{
+ bool clickedTopRow = (m_topleft.y == pos.y);
+
+ if (clickedTopRow)
+ {
+ if (pos.x < tableBreaks[0])
+ m_sortType = (m_sortType == &cIndexAscending) ? &cIndexDescending : &cIndexAscending;
+ else if (pos.x < tableBreaks[1])
+ m_sortType = (m_sortType == &cEnabledAscending) ? &cEnabledDescending : &cEnabledAscending;
+ else if (pos.x < tableBreaks[2])
+ m_sortType = (m_sortType == &cCpuAscending) ? &cCpuDescending : &cCpuAscending;
+ else if (pos.x < tableBreaks[3])
+ m_sortType = (m_sortType == &cTypeAscending) ? &cTypeDescending : &cTypeAscending;
+ else if (pos.x < tableBreaks[4])
+ m_sortType = (m_sortType == &cConditionAscending) ? &cConditionDescending : &cConditionAscending;
+ else if (pos.x < tableBreaks[5])
+ m_sortType = (m_sortType == &cActionAscending) ? &cActionDescending : &cActionAscending;
+ }
+ else
+ {
+ // Gather a sorted list of all the exceptionpoints for all the CPUs
+ gather_exceptionpoints();
+
+ int epIndex = pos.y - 1;
+ if ((epIndex >= m_buffer.size()) || (epIndex < 0))
+ return;
+
+ // Enable / disable
+ const_cast<debug_exceptionpoint &>(*m_buffer[epIndex]).setEnabled(!m_buffer[epIndex]->enabled());
+
+ machine().debug_view().update_all(DVT_DISASSEMBLY);
+ }
+
+ begin_update();
+ m_update_pending = true;
+ end_update();
+}
+
+
+void debug_view_exceptionpoints::pad_ostream_to_length(std::ostream& str, int len)
+{
+ auto const current = str.tellp();
+ if (current < decltype(current)(len))
+ str << std::setw(decltype(current)(len) - current) << "";
+}
+
+
+void debug_view_exceptionpoints::gather_exceptionpoints()
+{
+ m_buffer.resize(0);
+ for (auto &source : m_source_list)
+ {
+ // Collect
+ device_debug &debugInterface = *source->device()->debug();
+ for (const auto &epp : debugInterface.exceptionpoint_list())
+ m_buffer.push_back(epp.second.get());
+ }
+
+ // And now for the sort
+ if (!m_buffer.empty())
+ std::stable_sort(m_buffer.begin(), m_buffer.end(), m_sortType);
+}
+
+
+//-------------------------------------------------
+// view_update - update the contents of the
+// exceptionpoints view
+//-------------------------------------------------
+
+void debug_view_exceptionpoints::view_update()
+{
+ // Gather a list of all the exceptionpoints for all the CPUs
+ gather_exceptionpoints();
+
+ // Set the view region so the scroll bars update
+ m_total.x = tableBreaks[std::size(tableBreaks) - 1];
+ m_total.y = m_buffer.size() + 1;
+ if (m_total.y < 10)
+ m_total.y = 10;
+
+ // Draw
+ debug_view_char *dest = &m_viewdata[0];
+ util::ovectorstream linebuf;
+ linebuf.reserve(std::size(tableBreaks) - 1);
+
+ // Header
+ if (m_visible.y > 0)
+ {
+ linebuf.clear();
+ linebuf.rdbuf()->clear();
+ linebuf << "ID";
+ if (m_sortType == &cIndexAscending) linebuf.put('\\');
+ else if (m_sortType == &cIndexDescending) linebuf.put('/');
+ pad_ostream_to_length(linebuf, tableBreaks[0]);
+ linebuf << "En";
+ if (m_sortType == &cEnabledAscending) linebuf.put('\\');
+ else if (m_sortType == &cEnabledDescending) linebuf.put('/');
+ pad_ostream_to_length(linebuf, tableBreaks[1]);
+ linebuf << "CPU";
+ if (m_sortType == &cCpuAscending) linebuf.put('\\');
+ else if (m_sortType == &cCpuDescending) linebuf.put('/');
+ pad_ostream_to_length(linebuf, tableBreaks[2]);
+ linebuf << "Type";
+ if (m_sortType == &cTypeAscending) linebuf.put('\\');
+ else if (m_sortType == &cTypeDescending) linebuf.put('/');
+ pad_ostream_to_length(linebuf, tableBreaks[3]);
+ linebuf << "Condition";
+ if (m_sortType == &cConditionAscending) linebuf.put('\\');
+ else if (m_sortType == &cConditionDescending) linebuf.put('/');
+ pad_ostream_to_length(linebuf, tableBreaks[4]);
+ linebuf << "Action";
+ if (m_sortType == &cActionAscending) linebuf.put('\\');
+ else if (m_sortType == &cActionDescending) linebuf.put('/');
+ pad_ostream_to_length(linebuf, tableBreaks[5]);
+
+ auto const &text(linebuf.vec());
+ for (u32 i = m_topleft.x; i < (m_topleft.x + m_visible.x); i++, dest++)
+ {
+ dest->byte = (i < text.size()) ? text[i] : ' ';
+ dest->attrib = DCA_ANCILLARY;
+ }
+ }
+
+ for (int row = 1; row < m_visible.y; row++)
+ {
+ // Breakpoints
+ int epi = row + m_topleft.y - 1;
+ if ((epi < m_buffer.size()) && (epi >= 0))
+ {
+ const debug_exceptionpoint *const ep = m_buffer[epi];
+
+ linebuf.clear();
+ linebuf.rdbuf()->clear();
+ util::stream_format(linebuf, "%2X", ep->index());
+ pad_ostream_to_length(linebuf, tableBreaks[0]);
+ linebuf.put(ep->enabled() ? 'X' : 'O');
+ pad_ostream_to_length(linebuf, tableBreaks[1]);
+ linebuf << ep->debugInterface()->device().tag();
+ pad_ostream_to_length(linebuf, tableBreaks[2]);
+ util::stream_format(linebuf, "%X", ep->type());
+ pad_ostream_to_length(linebuf, tableBreaks[3]);
+ if (strcmp(ep->condition(), "1"))
+ linebuf << ep->condition();
+ pad_ostream_to_length(linebuf, tableBreaks[4]);
+ linebuf << ep->action();
+ pad_ostream_to_length(linebuf, tableBreaks[5]);
+
+ auto const &text(linebuf.vec());
+ for (u32 i = m_topleft.x; i < (m_topleft.x + m_visible.x); i++, dest++)
+ {
+ dest->byte = (i < text.size()) ? text[i] : ' ';
+ dest->attrib = DCA_NORMAL;
+
+ // Color disabled exceptionpoints red
+ if ((i >= tableBreaks[0]) && (i < tableBreaks[1]) && !ep->enabled())
+ dest->attrib |= DCA_CHANGED;
+ }
+ }
+ else
+ {
+ // Fill the remaining vertical space
+ for (u32 i = m_topleft.x; i < (m_topleft.x + m_visible.x); i++, dest++)
+ {
+ dest->byte = ' ';
+ dest->attrib = DCA_NORMAL;
+ }
+ }
+ }
+}
diff --git a/src/emu/debug/dvepoints.h b/src/emu/debug/dvepoints.h
new file mode 100644
index 00000000000..3b042218e2a
--- /dev/null
+++ b/src/emu/debug/dvepoints.h
@@ -0,0 +1,49 @@
+// license:BSD-3-Clause
+// copyright-holders:Andrew Gardner, Vas Crabb
+/*********************************************************************
+
+ dvepoints.h
+
+ Exceptionpoint debugger view.
+
+***************************************************************************/
+#ifndef MAME_EMU_DEBUG_DVEPOINTS_H
+#define MAME_EMU_DEBUG_DVEPOINTS_H
+
+#pragma once
+
+#include "debugvw.h"
+
+#include <vector>
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// debug view for exceptionpoints
+class debug_view_exceptionpoints : public debug_view
+{
+ friend class debug_view_manager;
+
+ // construction/destruction
+ debug_view_exceptionpoints(running_machine &machine, debug_view_osd_update_func osdupdate, void *osdprivate);
+ virtual ~debug_view_exceptionpoints();
+
+protected:
+ // view overrides
+ virtual void view_update() override;
+ virtual void view_click(const int button, const debug_view_xy& pos) override;
+
+private:
+ // internal helpers
+ void enumerate_sources();
+ void pad_ostream_to_length(std::ostream& str, int len);
+ void gather_exceptionpoints();
+
+ // internal state
+ bool (*m_sortType)(const debug_exceptionpoint *, const debug_exceptionpoint *);
+ std::vector<const debug_exceptionpoint *> m_buffer;
+};
+
+#endif // MAME_EMU_DEBUG_DVEPOINTS_H
diff --git a/src/emu/debug/dvmemory.cpp b/src/emu/debug/dvmemory.cpp
index 947c387800b..7c10407822b 100644
--- a/src/emu/debug/dvmemory.cpp
+++ b/src/emu/debug/dvmemory.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Aaron Giles
/*********************************************************************
- dvmemory.c
+ dvmemory.cpp
Memory debugger view.
@@ -12,35 +12,51 @@
#include "dvmemory.h"
#include "debugcpu.h"
-#include "debugger.h"
#include <algorithm>
-#include <ctype.h>
+#include <cctype>
#include <tuple>
//**************************************************************************
+// HELPER FUNCTIONS
+//**************************************************************************
+
+namespace {
+
+constexpr u8 sanitise_character(u8 ch)
+{
+ // assume ISO-8859-1 (low 256 Unicode codepoints) - tab, soft hyphen, C0 and C1 cause problems
+ return ('\t' == ch) ? ' ' : (0xadU == ch) ? '-' : ((' ' > ch) || (('~' < ch) && (0xa0U > ch))) ? '.' : ch;
+}
+
+} // anonymous namespace
+
+//**************************************************************************
// GLOBAL VARIABLES
//**************************************************************************
-const debug_view_memory::memory_view_pos debug_view_memory::s_memory_pos_table[12] =
+const debug_view_memory::memory_view_pos debug_view_memory::s_memory_pos_table[16] =
{
- /* 0 bytes per chunk: */ { 0, { 0 } },
- /* 1 byte per chunk: 00 11 22 33 44 55 66 77 */ { 3, { 0x04, 0x00, 0x80 } },
- /* 2 bytes per chunk: 0011 2233 4455 6677 */ { 6, { 0x8c, 0x0c, 0x08, 0x04, 0x00, 0x80 } },
- /* 3 bytes per chunk: */ { 0, { 0 } },
- /* 4 bytes per chunk: 00112233 44556677 */ { 12, { 0x9c, 0x9c, 0x1c, 0x18, 0x14, 0x10, 0x0c, 0x08, 0x04, 0x00, 0x80, 0x80 } },
- /* 5 bytes per chunk: */ { 0, { 0 } },
- /* 6 bytes per chunk: */ { 0, { 0 } },
- /* 7 bytes per chunk: */ { 0, { 0 } },
- /* 8 bytes per chunk: 0011223344556677 */ { 24, { 0xbc, 0xbc, 0xbc, 0xbc, 0x3c, 0x38, 0x34, 0x30, 0x2c, 0x28, 0x24, 0x20, 0x1c, 0x18, 0x14, 0x10, 0x0c, 0x08, 0x04, 0x00, 0x80, 0x80, 0x80, 0x80 } },
- /* 32 bit floating point: */ { 16, { 0 } },
- /* 64 bit floating point: */ { 32, { 0 } },
- /* 80 bit floating point: */ { 32, { 0 } },
+ /* 0 bytes per chunk: */ { 0, 0, { 0 } },
+ /* 1 byte per chunk: 00 11 22 33 44 55 66 77 */ { 1, 3, { 0x04, 0x00, 0x80 } },
+ /* 2 bytes per chunk: 0011 2233 4455 6677 */ { 2, 6, { 0x8c, 0x0c, 0x08, 0x04, 0x00, 0x80 } },
+ /* 3 bytes per chunk: */ { 0, 0, { 0 } },
+ /* 4 bytes per chunk: 00112233 44556677 */ { 4, 12, { 0x9c, 0x9c, 0x1c, 0x18, 0x14, 0x10, 0x0c, 0x08, 0x04, 0x00, 0x80, 0x80 } },
+ /* 5 bytes per chunk: */ { 0, 0, { 0 } },
+ /* 6 bytes per chunk: */ { 0, 0, { 0 } },
+ /* 7 bytes per chunk: */ { 0, 0, { 0 } },
+ /* 8 bytes per chunk: 0011223344556677 */ { 8, 24, { 0xbc, 0xbc, 0xbc, 0xbc, 0x3c, 0x38, 0x34, 0x30, 0x2c, 0x28, 0x24, 0x20, 0x1c, 0x18, 0x14, 0x10, 0x0c, 0x08, 0x04, 0x00, 0x80, 0x80, 0x80, 0x80 } },
+ /* 32 bit floating point: */ { 4, 16, { 0 } },
+ /* 64 bit floating point: */ { 8, 32, { 0 } },
+ /* 80 bit floating point: */ { 10, 32, { 0 } },
+ /* 8 bit octal: */ { 1, 4, { 0x06, 0x03, 0x00, 0x80 } },
+ /* 16 bit octal: */ { 2, 8, { 0x8f, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x00, 0x80 } },
+ /* 32 bit octal: */ { 4, 15, { 0x9e, 0x9e, 0x1e, 0x1b, 0x18, 0x15, 0x12, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x00, 0x80, 0x80 } },
+ /* 64 bit octal: */ { 8, 28, { 0xbf, 0xbf, 0xbf, 0x3f, 0x3c, 0x39, 0x36, 0x33, 0x30, 0x2d, 0x2a, 0x27, 0x24, 0x21, 0x1e, 0x1b, 0x18, 0x15, 0x12, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x00, 0x80, 0x80, 0x80 } },
};
-
//**************************************************************************
// DEBUG VIEW MEMORY SOURCE
//**************************************************************************
@@ -49,39 +65,45 @@ const debug_view_memory::memory_view_pos debug_view_memory::s_memory_pos_table[1
// debug_view_memory_source - constructors
//-------------------------------------------------
-debug_view_memory_source::debug_view_memory_source(const char *name, address_space &space)
- : debug_view_source(name, &space.device()),
- m_space(&space),
- m_memintf(dynamic_cast<device_memory_interface *>(&space.device())),
- m_base(nullptr),
- m_length(0),
- m_offsetxor(0),
- m_endianness(space.endianness()),
- m_prefsize(space.data_width() / 8)
+debug_view_memory_source::debug_view_memory_source(std::string &&name, address_space &space)
+ : debug_view_source(std::move(name), &space.device())
+ , m_space(&space)
+ , m_memintf(dynamic_cast<device_memory_interface *>(&space.device()))
+ , m_base(nullptr)
+ , m_blocklength(0)
+ , m_numblocks(0)
+ , m_blockstride(0)
+ , m_offsetxor(0)
+ , m_endianness(space.endianness())
+ , m_prefsize(space.data_width() / 8)
{
}
-debug_view_memory_source::debug_view_memory_source(const char *name, memory_region &region)
- : debug_view_source(name),
- m_space(nullptr),
- m_memintf(nullptr),
- m_base(region.base()),
- m_length(region.bytes()),
- m_offsetxor(ENDIAN_VALUE_NE_NNE(region.endianness(), 0, region.bytewidth() - 1)),
- m_endianness(region.endianness()),
- m_prefsize(std::min<u8>(region.bytewidth(), 8))
+debug_view_memory_source::debug_view_memory_source(std::string &&name, memory_region &region)
+ : debug_view_source(std::move(name))
+ , m_space(nullptr)
+ , m_memintf(nullptr)
+ , m_base(region.base())
+ , m_blocklength(region.bytes())
+ , m_numblocks(1)
+ , m_blockstride(0)
+ , m_offsetxor(region.endianness() == ENDIANNESS_NATIVE ? 0 : region.bytewidth() - 1)
+ , m_endianness(region.endianness())
+ , m_prefsize(std::min<u8>(region.bytewidth(), 8))
{
}
-debug_view_memory_source::debug_view_memory_source(const char *name, void *base, int element_size, int num_elements)
- : debug_view_source(name),
- m_space(nullptr),
- m_memintf(nullptr),
- m_base(base),
- m_length(element_size * num_elements),
- m_offsetxor(0),
- m_endianness(ENDIANNESS_NATIVE),
- m_prefsize(std::min(element_size, 8))
+debug_view_memory_source::debug_view_memory_source(std::string &&name, void *base, int element_size, int num_elements, int num_blocks, int block_stride)
+ : debug_view_source(std::move(name))
+ , m_space(nullptr)
+ , m_memintf(nullptr)
+ , m_base(base)
+ , m_blocklength(element_size * num_elements)
+ , m_numblocks(num_blocks)
+ , m_blockstride(block_stride)
+ , m_offsetxor(0)
+ , m_endianness(ENDIANNESS_NATIVE)
+ , m_prefsize(std::min(element_size, 8))
{
}
@@ -101,11 +123,13 @@ debug_view_memory::debug_view_memory(running_machine &machine, debug_view_osd_up
m_chunks_per_row(16),
m_bytes_per_chunk(1),
m_steps_per_chunk(1),
- m_data_format(1),
+ m_data_format(data_format::HEX_8BIT),
m_reverse_view(false),
m_ascii_view(true),
m_no_translation(false),
m_edit_enabled(true),
+ m_shift_bits(4),
+ m_address_radix(16),
m_maxaddr(0),
m_bytes_per_row(16),
m_byte_offset(0)
@@ -119,7 +143,7 @@ debug_view_memory::debug_view_memory(running_machine &machine, debug_view_osd_up
// fail if no available sources
enumerate_sources();
- if (m_source_list.count() == 0)
+ if (m_source_list.empty())
throw std::bad_alloc();
// configure the view
@@ -135,49 +159,56 @@ debug_view_memory::debug_view_memory(running_machine &machine, debug_view_osd_up
void debug_view_memory::enumerate_sources()
{
// start with an empty list
- m_source_list.reset();
- std::string name;
+ m_source_list.clear();
+ m_source_list.reserve(machine().save().registration_count());
// first add all the devices' address spaces
- for (device_memory_interface &memintf : memory_interface_iterator(machine().root_device()))
+ for (device_memory_interface &memintf : memory_interface_enumerator(machine().root_device()))
+ {
for (int spacenum = 0; spacenum < memintf.max_space_count(); ++spacenum)
+ {
if (memintf.has_space(spacenum))
{
- address_space &space = memintf.space(spacenum);
- name = string_format("%s '%s' %s space memory", memintf.device().name(), memintf.device().tag(), space.name());
- m_source_list.append(*global_alloc(debug_view_memory_source(name.c_str(), space)));
+ address_space &space(memintf.space(spacenum));
+ m_source_list.emplace_back(
+ std::make_unique<debug_view_memory_source>(
+ util::string_format("%s '%s' %s space memory", memintf.device().name(), memintf.device().tag(), space.name()),
+ space));
}
+ }
+ }
// then add all the memory regions
for (auto &region : machine().memory().regions())
{
- name = string_format("Region '%s'", region.second->name());
- m_source_list.append(*global_alloc(debug_view_memory_source(name.c_str(), *region.second.get())));
+ m_source_list.emplace_back(
+ std::make_unique<debug_view_memory_source>(
+ util::string_format("Region '%s'", region.second->name()),
+ *region.second.get()));
}
- // finally add all global array symbols in alphabetical order
- std::vector<std::tuple<std::string, void *, u32, u32> > itemnames;
- itemnames.reserve(machine().save().registration_count());
-
+ // finally add all global array symbols in ASCII order
+ std::string name;
+ std::size_t const firstsave = m_source_list.size();
for (int itemnum = 0; itemnum < machine().save().registration_count(); itemnum++)
{
- u32 valsize, valcount;
+ u32 valsize, valcount, blockcount, stride;
void *base;
- std::string name_string(machine().save().indexed_item(itemnum, base, valsize, valcount));
+ name = machine().save().indexed_item(itemnum, base, valsize, valcount, blockcount, stride);
// add pretty much anything that's not a timer (we may wish to cull other items later)
// also, don't trim the front of the name, it's important to know which VIA6522 we're looking at, e.g.
- if (strncmp(name_string.c_str(), "timer/", 6))
- itemnames.emplace_back(std::move(name_string), base, valsize, valcount);
+ if (strncmp(name.c_str(), "timer/", 6))
+ m_source_list.emplace_back(std::make_unique<debug_view_memory_source>(std::move(name), base, valsize, valcount, blockcount, stride));
}
-
- std::sort(itemnames.begin(), itemnames.end(), [] (auto const &x, auto const &y) { return std::get<0>(x) < std::get<0>(y); });
-
- for (auto const &item : itemnames)
- m_source_list.append(*global_alloc(debug_view_memory_source(std::get<0>(item).c_str(), std::get<1>(item), std::get<2>(item), std::get<3>(item))));
+ std::sort(
+ std::next(m_source_list.begin(), firstsave),
+ m_source_list.end(),
+ [] (auto const &x, auto const &y) { return 0 > std::strcmp(x->name(), y->name()); });
// reset the source to a known good entry
- set_source(*m_source_list.first());
+ if (!m_source_list.empty())
+ set_source(*m_source_list[0]);
}
@@ -201,12 +232,33 @@ void debug_view_memory::view_notify(debug_view_notification type)
m_bytes_per_chunk = source.m_prefsize;
if (m_bytes_per_chunk > 8)
m_bytes_per_chunk = 8;
- m_data_format = m_bytes_per_chunk;
+ bool octal = source.m_space != nullptr && source.m_space->is_octal();
+ switch (m_bytes_per_chunk)
+ {
+ case 1:
+ m_data_format = octal ? data_format::OCTAL_8BIT : data_format::HEX_8BIT;
+ break;
+
+ case 2:
+ m_data_format = octal ? data_format::OCTAL_16BIT : data_format::HEX_16BIT;
+ break;
+
+ case 4:
+ m_data_format = octal ? data_format::OCTAL_32BIT : data_format::HEX_32BIT;
+ break;
+
+ case 8:
+ m_data_format = octal ? data_format::OCTAL_64BIT : data_format::HEX_64BIT;
+ break;
+ }
+ m_shift_bits = octal ? 3 : 4;
m_steps_per_chunk = source.m_space ? source.m_space->byte_to_address(m_bytes_per_chunk) : m_bytes_per_chunk;
if (source.m_space != nullptr)
m_expression.set_context(&source.m_space->device().debug()->symtable());
else
m_expression.set_context(nullptr);
+ m_address_radix = octal ? 8 : 16;
+ m_expression.set_default_base(m_address_radix);
}
}
@@ -244,6 +296,122 @@ static inline float u64_to_double(u64 value)
}
//-------------------------------------------------
+// generate_row - read one row of data and make a
+// text representation of the chunks
+//-------------------------------------------------
+
+void debug_view_memory::generate_row(debug_view_char *destmin, debug_view_char *destmax, debug_view_char *destrow, offs_t address)
+{
+ // get positional data
+ const memory_view_pos &posdata = get_posdata(m_data_format);
+ int spacing = posdata.m_spacing;
+
+ // generate the address
+ char addrtext[20];
+ snprintf(addrtext, 20, m_addrformat.c_str(), address);
+ debug_view_char *dest = destrow + m_section[0].m_pos + 1;
+ for (int ch = 0; addrtext[ch] != 0 && ch < m_section[0].m_width - 1; ch++, dest++)
+ if (dest >= destmin && dest < destmax)
+ dest->byte = addrtext[ch];
+
+ // generate the data and the ASCII string
+ std::string chunkascii;
+ if (m_shift_bits != 0)
+ {
+ for (int chunknum = 0; chunknum < m_chunks_per_row; chunknum++)
+ {
+ u64 chunkdata;
+ bool ismapped = read_chunk(address, chunknum, chunkdata);
+
+ int chunkindex = m_reverse_view ? (m_chunks_per_row - 1 - chunknum) : chunknum;
+ dest = destrow + m_section[1].m_pos + 1 + chunkindex * spacing;
+ for (int ch = 0; ch < spacing; ch++, dest++)
+ if (dest >= destmin && dest < destmax)
+ {
+ u8 shift = posdata.m_shift[ch];
+ if (shift < 64)
+ dest->byte = ismapped ? "0123456789ABCDEF"[BIT(chunkdata, shift, m_shift_bits)] : '*';
+ }
+
+ for (int i = 0; i < m_bytes_per_chunk; i++)
+ {
+ u8 chval = chunkdata >> (8 * (m_bytes_per_chunk - i - 1));
+ chunkascii += char(ismapped ? sanitise_character(chval) : '.');
+ }
+ }
+ }
+ else
+ {
+ for (int chunknum = 0; chunknum < m_chunks_per_row; chunknum++)
+ {
+ char valuetext[64];
+ u64 chunkdata = 0;
+ extFloat80_t chunkdata80 = { 0, 0 };
+ bool ismapped;
+
+ if (m_data_format != data_format::FLOAT_80BIT)
+ ismapped = read(m_bytes_per_chunk, address + chunknum * m_steps_per_chunk, chunkdata);
+ else
+ ismapped = read(m_bytes_per_chunk, address + chunknum * m_steps_per_chunk, chunkdata80);
+
+ if (ismapped)
+ switch (m_data_format)
+ {
+ case data_format::FLOAT_32BIT:
+ snprintf(valuetext, 64, "%.8g", u32_to_float(u32(chunkdata)));
+ break;
+ case data_format::FLOAT_64BIT:
+ snprintf(valuetext, 64, "%.24g", u64_to_double(chunkdata));
+ break;
+ case data_format::FLOAT_80BIT:
+ {
+ float64_t f64 = extF80M_to_f64(&chunkdata80);
+ snprintf(valuetext, 64, "%.24g", u64_to_double(f64.v));
+ break;
+ }
+ default:
+ break;
+ }
+ else
+ {
+ valuetext[0] = '*';
+ valuetext[1] = 0;
+ }
+
+ int ch;
+ int chunkindex = m_reverse_view ? (m_chunks_per_row - 1 - chunknum) : chunknum;
+ dest = destrow + m_section[1].m_pos + 1 + chunkindex * spacing;
+ // first copy the text
+ for (ch = 0; (ch < spacing) && (valuetext[ch] != 0); ch++, dest++)
+ if (dest >= destmin && dest < destmax)
+ dest->byte = valuetext[ch];
+ // then fill with spaces
+ for (; ch < spacing; ch++, dest++)
+ if (dest >= destmin && dest < destmax)
+ dest->byte = ' ';
+
+ for (int i = 0; i < m_bytes_per_chunk; i++)
+ {
+ u8 chval = chunkdata >> (8 * (m_bytes_per_chunk - i - 1));
+ chunkascii += char(ismapped ? sanitise_character(chval) : '.');
+ }
+ }
+ }
+
+ // generate the ASCII data, but follow the chunks
+ if (m_section[2].m_width > 0)
+ {
+ dest = destrow + m_section[2].m_pos + 1;
+ for (size_t i = 0; i != chunkascii.size(); i++)
+ {
+ if (dest >= destmin && dest < destmax)
+ dest->byte = chunkascii[i];
+ dest++;
+ }
+ }
+}
+
+//-------------------------------------------------
// view_update - update the contents of the
// memory view
//-------------------------------------------------
@@ -256,9 +424,6 @@ void debug_view_memory::view_update()
if (needs_recompute())
recompute();
- // get positional data
- const memory_view_pos &posdata = s_memory_pos_table[m_data_format];
-
// loop over visible rows
for (u32 row = 0; row < m_visible.y; row++)
{
@@ -268,10 +433,9 @@ void debug_view_memory::view_update()
u32 effrow = m_topleft.y + row;
// reset the line of data; section 1 is normal, others are ancillary, cursor is selected
- debug_view_char *dest = destmin;
- for (int ch = 0; ch < m_visible.x; ch++, dest++)
+ u32 effcol = m_topleft.x;
+ for (debug_view_char *dest = destmin; dest != destmax; dest++, effcol++)
{
- u32 effcol = m_topleft.x + ch;
dest->byte = ' ';
dest->attrib = DCA_ANCILLARY;
if (m_section[1].contains(effcol))
@@ -287,96 +451,7 @@ void debug_view_memory::view_update()
{
offs_t addrbyte = m_byte_offset + effrow * m_bytes_per_row;
offs_t address = (source.m_space != nullptr) ? source.m_space->byte_to_address(addrbyte) : addrbyte;
- char addrtext[20];
-
- // generate the address
- sprintf(addrtext, m_addrformat.c_str(), address);
- dest = destrow + m_section[0].m_pos + 1;
- for (int ch = 0; addrtext[ch] != 0 && ch < m_section[0].m_width - 1; ch++, dest++)
- if (dest >= destmin && dest < destmax)
- dest->byte = addrtext[ch];
-
- // generate the data and the ascii string
- std::string chunkascii;
- for (int chunknum = 0; chunknum < m_chunks_per_row; chunknum++)
- {
- int chunkindex = m_reverse_view ? (m_chunks_per_row - 1 - chunknum) : chunknum;
- int spacing = posdata.m_spacing;
-
- if (m_data_format <= 8) {
- u64 chunkdata;
- bool ismapped = read_chunk(address, chunknum, chunkdata);
- dest = destrow + m_section[1].m_pos + 1 + chunkindex * spacing;
- for (int ch = 0; ch < posdata.m_spacing; ch++, dest++)
- if (dest >= destmin && dest < destmax)
- {
- u8 shift = posdata.m_shift[ch];
- if (shift < 64)
- dest->byte = ismapped ? "0123456789ABCDEF"[(chunkdata >> shift) & 0x0f] : '*';
- }
- for (int i=0; i < m_bytes_per_chunk; i++) {
- u8 chval = chunkdata >> (8 * (m_bytes_per_chunk - i - 1));
- chunkascii += char((ismapped && isprint(chval)) ? chval : '.');
- }
- }
- else {
- int ch;
- char valuetext[64];
- u64 chunkdata = 0;
- floatx80 chunkdata80 = { 0, 0 };
- bool ismapped;
-
- if (m_data_format != 11)
- ismapped = read(m_bytes_per_chunk, address + chunknum * m_steps_per_chunk, chunkdata);
- else
- ismapped = read(m_bytes_per_chunk, address + chunknum * m_steps_per_chunk, chunkdata80);
-
- if (ismapped)
- switch (m_data_format)
- {
- case 9:
- sprintf(valuetext, "%.8g", u32_to_float(u32(chunkdata)));
- break;
- case 10:
- sprintf(valuetext, "%.24g", u64_to_double(chunkdata));
- break;
- case 11:
- float64 f64 = floatx80_to_float64(chunkdata80);
- sprintf(valuetext, "%.24g", u64_to_double(f64));
- break;
- }
- else {
- valuetext[0] = '*';
- valuetext[1] = 0;
- }
-
- dest = destrow + m_section[1].m_pos + 1 + chunkindex * spacing;
- // first copy the text
- for (ch = 0; (ch < spacing) && (valuetext[ch] != 0); ch++, dest++)
- if (dest >= destmin && dest < destmax)
- dest->byte = valuetext[ch];
- // then fill with spaces
- for (; ch < spacing; ch++, dest++)
- if (dest >= destmin && dest < destmax)
- dest->byte = ' ';
-
- for (int i=0; i < m_bytes_per_chunk; i++) {
- u8 chval = chunkdata >> (8 * (m_bytes_per_chunk - i - 1));
- chunkascii += char((ismapped && isprint(chval)) ? chval : '.');
- }
- }
- }
-
- // generate the ASCII data, but follow the chunks
- if (m_section[2].m_width > 0)
- {
- dest = destrow + m_section[2].m_pos + 1;
- for (size_t i = 0; i != chunkascii.size(); i++) {
- if (dest >= destmin && dest < destmax)
- dest->byte = chunkascii[i];
- dest++;
- }
- }
+ generate_row(destmin, destmax, destrow, address);
}
}
}
@@ -429,12 +504,12 @@ void debug_view_memory::view_char(int chval)
case DCH_HOME:
pos.m_address -= pos.m_address % m_bytes_per_row;
- pos.m_shift = (m_bytes_per_chunk * 8) - 4;
+ pos.m_shift = get_posdata(m_data_format).m_shift[0] & 0x7f;
break;
case DCH_CTRLHOME:
pos.m_address = m_byte_offset;
- pos.m_shift = (m_bytes_per_chunk * 8) - 4;
+ pos.m_shift = get_posdata(m_data_format).m_shift[0] & 0x7f;
break;
case DCH_END:
@@ -458,43 +533,35 @@ void debug_view_memory::view_char(int chval)
break;
default:
- {
- static const char hexvals[] = "0123456789abcdef";
- char *hexchar = (char *)strchr(hexvals, tolower(chval));
- if (hexchar == nullptr)
- break;
-
- const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source);
- offs_t address = (source.m_space != nullptr) ? source.m_space->byte_to_address(pos.m_address) : pos.m_address;
- u64 data;
- bool ismapped = read(m_bytes_per_chunk, address, data);
- if (!ismapped)
- break;
+ {
+ static const char hexvals[] = "0123456789abcdef";
+ char *hexchar = (char *)strchr(hexvals, tolower(chval));
+ if (hexchar == nullptr || (m_shift_bits == 3 && chval >= '8'))
+ break;
- data &= ~(u64(0x0f) << pos.m_shift);
- data |= u64(hexchar - hexvals) << pos.m_shift;
- write(m_bytes_per_chunk, address, data);
+ if (!write_digit(pos.m_address, pos.m_shift, hexchar - hexvals))
+ break; // TODO: alert OSD?
+ }
// fall through to the right-arrow press
- }
-
+ [[fallthrough]];
case DCH_RIGHT:
- if (pos.m_shift == 0 && pos.m_address != m_maxaddr)
+ if (pos.m_shift != 0)
+ pos.m_shift -= m_shift_bits;
+ else if (pos.m_address != m_maxaddr)
{
- pos.m_shift = m_bytes_per_chunk * 8 - 4;
+ pos.m_shift = get_posdata(m_data_format).m_shift[0] & 0x7f;
pos.m_address += m_bytes_per_chunk;
}
- else
- pos.m_shift -= 4;
break;
case DCH_LEFT:
- if (pos.m_shift == m_bytes_per_chunk * 8 - 4 && pos.m_address != m_byte_offset)
+ if (pos.m_shift != (get_posdata(m_data_format).m_shift[0] & 0x7f))
+ pos.m_shift += m_shift_bits;
+ else if (pos.m_address != m_byte_offset)
{
pos.m_shift = 0;
pos.m_address -= m_bytes_per_chunk;
}
- else
- pos.m_shift += 4;
break;
}
@@ -551,19 +618,42 @@ void debug_view_memory::recompute()
{
m_maxaddr = m_no_translation ? source.m_space->addrmask() : source.m_space->logaddrmask();
maxbyte = source.m_space->address_to_byte_end(m_maxaddr);
- addrchars = m_no_translation ? source.m_space->addrchars() : source.m_space->logaddrchars();
+ if (m_address_radix == 8)
+ addrchars = ((m_no_translation ? source.m_space->addr_width() : source.m_space->logaddr_width()) + 2) / 3;
+ else
+ addrchars = m_no_translation ? source.m_space->addrchars() : source.m_space->logaddrchars();
}
else
{
- maxbyte = m_maxaddr = source.m_length - 1;
- addrchars = string_format("%X", m_maxaddr).size();
+ maxbyte = m_maxaddr = (source.m_blocklength * source.m_numblocks) - 1;
+ if (m_address_radix == 8)
+ addrchars = string_format("%o", m_maxaddr).size();
+ else
+ addrchars = string_format("%X", m_maxaddr).size();
}
// generate an 8-byte aligned format for the address
- if (!m_reverse_view)
- m_addrformat = string_format("%*s%%0%dX", 8 - addrchars, "", addrchars);
- else
- m_addrformat = string_format("%%0%dX%*s", addrchars, 8 - addrchars, "");
+ switch (m_address_radix)
+ {
+ case 8:
+ if (!m_reverse_view)
+ m_addrformat = string_format("%*s%%0%do", 11 - addrchars, "", addrchars);
+ else
+ m_addrformat = string_format("%%0%do%*s", addrchars, 11 - addrchars, "");
+ break;
+
+ case 10:
+ // omit leading zeros for decimal addresses
+ m_addrformat = m_reverse_view ? "%-10d" : "%10d";
+ break;
+
+ case 16:
+ if (!m_reverse_view)
+ m_addrformat = string_format("%*s%%0%dX", 8 - addrchars, "", addrchars);
+ else
+ m_addrformat = string_format("%%0%dX%*s", addrchars, 8 - addrchars, "");
+ break;
+ }
// if we are viewing a space with a minimum chunk size, clamp the bytes per chunk
// BAD
@@ -588,14 +678,21 @@ void debug_view_memory::recompute()
m_byte_offset = val % m_bytes_per_row;
// compute the section widths
- m_section[0].m_width = 1 + 8 + 1;
- if (m_data_format <= 8)
- m_section[1].m_width = 1 + 3 * m_bytes_per_row + 1;
- else {
- const memory_view_pos &posdata = s_memory_pos_table[m_data_format];
+ switch (m_address_radix)
+ {
+ case 8:
+ m_section[0].m_width = 1 + 11 + 1;
+ break;
- m_section[1].m_width = 1 + posdata.m_spacing * m_chunks_per_row + 1;
+ case 10:
+ m_section[0].m_width = 1 + 10 + 1;
+ break;
+
+ case 16:
+ m_section[0].m_width = 1 + 8 + 1;
+ break;
}
+ m_section[1].m_width = 1 + get_posdata(m_data_format).m_spacing * m_chunks_per_row + 1;
m_section[2].m_width = m_ascii_view ? (1 + m_bytes_per_row + 1) : 0;
// compute the section positions
@@ -661,11 +758,11 @@ debug_view_memory::cursor_pos debug_view_memory::get_cursor_pos(const debug_view
{
// start with the base address for this row
cursor_pos pos;
- const memory_view_pos &posdata = s_memory_pos_table[m_data_format];
+ const memory_view_pos &posdata = get_posdata(m_data_format);
pos.m_address = m_byte_offset + cursor.y * m_bytes_per_chunk * m_chunks_per_row;
// determine the X position within the middle section, clamping as necessary
- if (m_data_format <= 8) {
+ if (posdata.m_shift[0] != 0) {
int xposition = cursor.x - m_section[1].m_pos - 1;
if (xposition < 0)
xposition = 0;
@@ -712,7 +809,7 @@ debug_view_memory::cursor_pos debug_view_memory::get_cursor_pos(const debug_view
void debug_view_memory::set_cursor_pos(cursor_pos pos)
{
- const memory_view_pos &posdata = s_memory_pos_table[m_data_format];
+ const memory_view_pos &posdata = get_posdata(m_data_format);
// offset the address by the byte offset
if (pos.m_address < m_byte_offset)
@@ -727,7 +824,7 @@ void debug_view_memory::set_cursor_pos(cursor_pos pos)
if (m_reverse_view)
chunknum = m_chunks_per_row - 1 - chunknum;
- if (m_data_format <= 8) {
+ if (posdata.m_shift[0] != 0) {
// scan within the chunk to find the shift
for (m_cursor.x = 0; m_cursor.x < posdata.m_spacing; m_cursor.x++)
if (posdata.m_shift[m_cursor.x] == pos.m_shift)
@@ -764,22 +861,17 @@ bool debug_view_memory::read(u8 size, offs_t offs, u64 &data)
auto dis = machine().disable_side_effects();
bool ismapped = offs <= m_maxaddr;
+ address_space *tspace;
if (ismapped && !m_no_translation)
{
offs_t dummyaddr = offs;
- ismapped = source.m_memintf->translate(source.m_space->spacenum(), TRANSLATE_READ_DEBUG, dummyaddr);
+ ismapped = source.m_memintf->translate(source.m_space->spacenum(), device_memory_interface::TR_READ, dummyaddr, tspace);
}
+ else
+ tspace = source.m_space;
data = ~u64(0);
if (ismapped)
- {
- switch (size)
- {
- case 1: data = machine().debugger().cpu().read_byte(*source.m_space, offs, !m_no_translation); break;
- case 2: data = machine().debugger().cpu().read_word(*source.m_space, offs, !m_no_translation); break;
- case 4: data = machine().debugger().cpu().read_dword(*source.m_space, offs, !m_no_translation); break;
- case 8: data = machine().debugger().cpu().read_qword(*source.m_space, offs, !m_no_translation); break;
- }
- }
+ data = m_expression.context().read_memory(*tspace, offs, size, !m_no_translation);
return ismapped;
}
@@ -801,9 +893,9 @@ bool debug_view_memory::read(u8 size, offs_t offs, u64 &data)
// all 0xff if out of bounds
offs ^= source.m_offsetxor;
- if (offs >= source.m_length)
+ if (offs >= (source.m_blocklength * source.m_numblocks))
return false;
- data = *((u8 *)source.m_base + offs);
+ data = *(reinterpret_cast<const u8 *>(source.m_base) + (offs / source.m_blocklength * source.m_blockstride) + (offs % source.m_blocklength));
return true;
}
@@ -812,21 +904,21 @@ bool debug_view_memory::read(u8 size, offs_t offs, u64 &data)
// read - read a 80 bit value
//-------------------------------------------------
-bool debug_view_memory::read(u8 size, offs_t offs, floatx80 &data)
+bool debug_view_memory::read(u8 size, offs_t offs, extFloat80_t &data)
{
u64 t;
bool mappedhi, mappedlo;
const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source);
if (source.m_endianness == ENDIANNESS_LITTLE) {
- mappedlo = read(8, offs, data.low);
+ mappedlo = read(8, offs, data.signif);
mappedhi = read(2, offs+8, t);
- data.high = (bits16)t;
+ data.signExp = u16(t);
}
else {
mappedhi = read(2, offs, t);
- data.high = (bits16)t;
- mappedlo = read(8, offs + 2, data.low);
+ data.signExp = u16(t);
+ mappedlo = read(8, offs + 2, data.signif);
}
return mappedhi && mappedlo;
@@ -870,14 +962,7 @@ void debug_view_memory::write(u8 size, offs_t offs, u64 data)
if (source.m_space)
{
auto dis = machine().disable_side_effects();
-
- switch (size)
- {
- case 1: machine().debugger().cpu().write_byte(*source.m_space, offs, data, !m_no_translation); break;
- case 2: machine().debugger().cpu().write_word(*source.m_space, offs, data, !m_no_translation); break;
- case 4: machine().debugger().cpu().write_dword(*source.m_space, offs, data, !m_no_translation); break;
- case 8: machine().debugger().cpu().write_qword(*source.m_space, offs, data, !m_no_translation); break;
- }
+ m_expression.context().write_memory(*source.m_space, offs, data, size, !m_no_translation);
return;
}
@@ -900,18 +985,44 @@ void debug_view_memory::write(u8 size, offs_t offs, u64 data)
// ignore if out of bounds
offs ^= source.m_offsetxor;
- if (offs >= source.m_length)
+ if (offs >= (source.m_blocklength * source.m_numblocks))
return;
- *((u8 *)source.m_base + offs) = data;
+ *(reinterpret_cast<u8 *>(source.m_base) + (offs / source.m_blocklength * source.m_blockstride) + (offs % source.m_blocklength)) = data;
+}
+
+
+//-------------------------------------------------
+// write_digit - write one hex or octal digit
+// at the given address and bit position
+//-------------------------------------------------
+
+bool debug_view_memory::write_digit(offs_t offs, u8 pos, u8 digit)
+{
+ const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source);
+ offs_t address = (source.m_space != nullptr) ? source.m_space->byte_to_address(offs) : offs;
+ u64 data;
+ bool ismapped = read(m_bytes_per_chunk, address, data);
+ if (!ismapped)
+ return false;
-// hack for FD1094 editing
-#ifdef FD1094_HACK
- if (source.m_base == machine().root_device().memregion("user2"))
+ // clamp to chunk size
+ if (m_bytes_per_chunk * 8 < pos + m_shift_bits)
{
- extern void fd1094_regenerate_key(running_machine &machine);
- fd1094_regenerate_key(machine());
+ assert(m_bytes_per_chunk * 8 > pos);
+ digit &= util::make_bitmask<u8>(m_bytes_per_chunk * 8 - pos);
}
-#endif
+
+ u64 write_data = (data & ~(util::make_bitmask<u64>(m_shift_bits) << pos)) | (u64(digit) << pos);
+ write(m_bytes_per_chunk, address, write_data);
+
+ // verify that data reads back as it was written
+ if (source.m_space != nullptr)
+ {
+ read(m_bytes_per_chunk, address, data);
+ return data == write_data;
+ }
+ else
+ return true;
}
@@ -948,15 +1059,15 @@ void debug_view_memory::set_chunks_per_row(u32 rowchunks)
//-------------------------------------------------
// set_data_format - specify what kind of values
-// are shown, 1-8 8-64 bits, 9 32bit floating point
+// are shown
//-------------------------------------------------
-void debug_view_memory::set_data_format(int format)
+void debug_view_memory::set_data_format(data_format format)
{
cursor_pos pos;
// should never be
- if ((format <= 0) || (format > 11))
+ if (!is_valid_format(format))
return;
// no need to change
if (format == m_data_format)
@@ -964,47 +1075,42 @@ void debug_view_memory::set_data_format(int format)
pos = begin_update_and_get_cursor_pos();
const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source);
- if ((format <= 8) && (m_data_format <= 8)) {
-
+ if (is_hex_format(format) && is_hex_format(m_data_format)) {
pos.m_address += (pos.m_shift / 8) ^ ((source.m_endianness == ENDIANNESS_LITTLE) ? 0 : (m_bytes_per_chunk - 1));
pos.m_shift %= 8;
- m_bytes_per_chunk = format;
+ m_bytes_per_chunk = get_posdata(format).m_bytes;
m_steps_per_chunk = source.m_space ? source.m_space->byte_to_address(m_bytes_per_chunk) : m_bytes_per_chunk;
- m_chunks_per_row = m_bytes_per_row / format;
+ m_chunks_per_row = m_bytes_per_row / m_bytes_per_chunk;
if (m_chunks_per_row < 1)
m_chunks_per_row = 1;
pos.m_shift += 8 * ((pos.m_address % m_bytes_per_chunk) ^ ((source.m_endianness == ENDIANNESS_LITTLE) ? 0 : (m_bytes_per_chunk - 1)));
pos.m_address -= pos.m_address % m_bytes_per_chunk;
} else {
- if (format <= 8) {
+ if (is_hex_format(format)) {
m_supports_cursor = true;
m_edit_enabled = true;
-
- m_bytes_per_chunk = format;
+ m_shift_bits = 4;
+ }
+ else if (is_octal_format(format)) {
+ m_supports_cursor = true;
+ m_edit_enabled = true;
+ m_shift_bits = 3;
}
else {
m_supports_cursor = false;
m_edit_enabled = false;
m_cursor_visible = false;
-
- switch (format)
- {
- case 9:
- m_bytes_per_chunk = 4;
- break;
- case 10:
- m_bytes_per_chunk = 8;
- break;
- case 11:
- m_bytes_per_chunk = 10;
- break;
- }
+ m_shift_bits = 0;
}
+
+ m_bytes_per_chunk = get_posdata(format).m_bytes;
m_chunks_per_row = m_bytes_per_row / m_bytes_per_chunk;
+ if (m_chunks_per_row < 1)
+ m_chunks_per_row = 1;
m_steps_per_chunk = source.m_space ? source.m_space->byte_to_address(m_bytes_per_chunk) : m_bytes_per_chunk;
- pos.m_shift = 0;
+ pos.m_shift = get_posdata(format).m_shift[0] & 0x7f;
pos.m_address -= pos.m_address % m_bytes_per_chunk;
}
m_recompute = m_update_pending = true;
@@ -1053,3 +1159,22 @@ void debug_view_memory::set_physical(bool physical)
m_recompute = m_update_pending = true;
end_update_and_set_cursor_pos(pos);
}
+
+
+//-------------------------------------------------
+// set_address_radix - specify whether the memory
+// view should display addresses in base 8, base
+// 10 or base 16
+//-------------------------------------------------
+
+void debug_view_memory::set_address_radix(int radix)
+{
+ if (radix != 8 && radix != 10 && radix != 16)
+ return;
+
+ cursor_pos pos = begin_update_and_get_cursor_pos();
+ m_address_radix = radix;
+ m_expression.set_default_base(radix);
+ m_recompute = m_update_pending = true;
+ end_update_and_set_cursor_pos(pos);
+}
diff --git a/src/emu/debug/dvmemory.h b/src/emu/debug/dvmemory.h
index a9341afcf76..6f1ffab4e9e 100644
--- a/src/emu/debug/dvmemory.h
+++ b/src/emu/debug/dvmemory.h
@@ -15,8 +15,7 @@
#include "debugvw.h"
-#include "softfloat/mamesf.h"
-#include "softfloat/softfloat.h"
+#include "softfloat3/source/include/softfloat.h"
//**************************************************************************
@@ -28,21 +27,23 @@ class debug_view_memory_source : public debug_view_source
{
friend class debug_view_memory;
- debug_view_memory_source(const char *name, address_space &space);
- debug_view_memory_source(const char *name, memory_region &region);
- debug_view_memory_source(const char *name, void *base, int element_size, int num_elements);
-
public:
+ debug_view_memory_source(std::string &&name, address_space &space);
+ debug_view_memory_source(std::string &&name, memory_region &region);
+ debug_view_memory_source(std::string &&name, void *base, int element_size, int num_elements, int num_blocks, int block_stride);
+
address_space *space() const { return m_space; }
private:
- address_space *m_space; // address space we reference (if any)
+ address_space *m_space; // address space we reference (if any)
device_memory_interface *m_memintf; // pointer to the memory interface of the device
- void * m_base; // pointer to memory base
- offs_t m_length; // length of memory
- offs_t m_offsetxor; // XOR to apply to offsets
- endianness_t m_endianness; // endianness of memory
- u8 m_prefsize; // preferred bytes per chunk
+ void * m_base; // pointer to memory base
+ offs_t m_blocklength; // length of each block of memory
+ offs_t m_numblocks; // number of blocks of memory
+ offs_t m_blockstride; // stride between blocks of memory
+ offs_t m_offsetxor; // XOR to apply to offsets
+ endianness_t m_endianness; // endianness of memory
+ u8 m_prefsize; // preferred bytes per chunk
};
@@ -54,23 +55,43 @@ class debug_view_memory : public debug_view
// construction/destruction
debug_view_memory(running_machine &machine, debug_view_osd_update_func osdupdate, void *osdprivate);
+ struct memory_view_pos;
+
public:
+ enum class data_format
+ {
+ HEX_8BIT = 1,
+ HEX_16BIT = 2,
+ HEX_32BIT = 4,
+ HEX_64BIT = 8,
+ FLOAT_32BIT = 9,
+ FLOAT_64BIT = 10,
+ FLOAT_80BIT = 11,
+ OCTAL_8BIT = 12,
+ OCTAL_16BIT = 13,
+ OCTAL_32BIT = 14,
+ OCTAL_64BIT = 15
+ };
+ static bool is_valid_format(data_format format) { return int(format) >= 0 && int(format) < std::size(s_memory_pos_table) && get_posdata(format).m_bytes != 0; }
+
// getters
const char *expression() const { return m_expression.string(); }
- int get_data_format() { flush_updates(); return m_data_format; }
+ data_format get_data_format() { flush_updates(); return m_data_format; }
u32 chunks_per_row() { flush_updates(); return m_chunks_per_row; }
bool reverse() const { return m_reverse_view; }
bool ascii() const { return m_ascii_view; }
bool physical() const { return m_no_translation; }
+ int address_radix() const { return m_address_radix; }
offs_t addressAtCursorPosition(const debug_view_xy& pos) { return get_cursor_pos(pos).m_address; }
// setters
void set_expression(const std::string &expression);
void set_chunks_per_row(u32 rowchunks);
- void set_data_format(int format); // 1-8 current values 9 32bit floating point
+ void set_data_format(data_format format);
void set_reverse(bool reverse);
- void set_ascii(bool reverse);
+ void set_ascii(bool ascii);
void set_physical(bool physical);
+ void set_address_radix(int radix);
protected:
// view overrides
@@ -87,6 +108,11 @@ private:
u8 m_shift;
};
+ // data format helpers
+ static bool is_hex_format(data_format format) { return int(format) <= 8; }
+ static bool is_octal_format(data_format format) { return int(format) >= 12; }
+ static const memory_view_pos &get_posdata(data_format format) { return s_memory_pos_table[int(format)]; }
+
// internal helpers
void enumerate_sources();
void recompute();
@@ -101,19 +127,23 @@ private:
// memory access
bool read(u8 size, offs_t offs, u64 &data);
void write(u8 size, offs_t offs, u64 data);
- bool read(u8 size, offs_t offs, floatx80 &data);
+ bool write_digit(offs_t offs, u8 pos, u8 digit);
+ bool read(u8 size, offs_t offs, extFloat80_t &data);
bool read_chunk(offs_t address, int chunknum, u64 &chunkdata);
+ void generate_row(debug_view_char *destmin, debug_view_char *destmax, debug_view_char *destrow, offs_t address);
// internal state
debug_view_expression m_expression; // expression describing the start address
u32 m_chunks_per_row; // number of chunks displayed per line
u8 m_bytes_per_chunk; // bytes per chunk
u8 m_steps_per_chunk; // bytes per chunk
- int m_data_format; // 1-8 current values 9 32bit floating point
+ data_format m_data_format; // 1-8 current values 9 32bit floating point
bool m_reverse_view; // reverse-endian view?
bool m_ascii_view; // display ASCII characters?
bool m_no_translation; // don't run addresses through the cpu translation hook
bool m_edit_enabled; // can modify contents ?
+ u8 m_shift_bits; // number of bits for each character/cursor position
+ u8 m_address_radix; // numerical radix for address column and expressions
offs_t m_maxaddr; // (derived) maximum address to display
u32 m_bytes_per_row; // (derived) number of bytes displayed per line
u32 m_byte_offset; // (derived) offset of starting visible byte
@@ -129,10 +159,11 @@ private:
struct memory_view_pos
{
- u8 m_spacing; /* spacing between each entry */
- u8 m_shift[24]; /* shift for each character */
+ u8 m_bytes; // bytes per entry
+ u8 m_spacing; // spacing between each entry
+ u8 m_shift[28]; // shift for each character
};
- static const memory_view_pos s_memory_pos_table[12]; // table for rendering at different data formats
+ static const memory_view_pos s_memory_pos_table[16]; // table for rendering at different data formats
// constants
static constexpr int MEM_MAX_LINE_WIDTH = 1024;
diff --git a/src/emu/debug/dvrpoints.cpp b/src/emu/debug/dvrpoints.cpp
new file mode 100644
index 00000000000..a36f42bd1d0
--- /dev/null
+++ b/src/emu/debug/dvrpoints.cpp
@@ -0,0 +1,298 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/*********************************************************************
+
+ dvrpoints.cpp
+
+ Registerpoint debugger view.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "dvrpoints.h"
+
+#include "debugcpu.h"
+#include "points.h"
+
+#include <algorithm>
+#include <iomanip>
+
+
+namespace {
+
+bool cIndexAscending(std::pair<device_t *, debug_registerpoint const *> const &a, std::pair<device_t *, debug_registerpoint const *> const &b)
+{
+ return a.second->index() < b.second->index();
+}
+
+bool cIndexDescending(std::pair<device_t *, debug_registerpoint const *> const &a, std::pair<device_t *, debug_registerpoint const *> const &b)
+{
+ return a.second->index() > b.second->index();
+}
+
+bool cEnabledAscending(std::pair<device_t *, debug_registerpoint const *> const &a, std::pair<device_t *, debug_registerpoint const *> const &b)
+{
+ return !a.second->enabled() && b.second->enabled();
+}
+
+bool cEnabledDescending(std::pair<device_t *, debug_registerpoint const *> const &a, std::pair<device_t *, debug_registerpoint const *> const &b)
+{
+ return cEnabledAscending(b, a);
+}
+
+bool cCpuAscending(std::pair<device_t *, debug_registerpoint const *> const &a, std::pair<device_t *, debug_registerpoint const *> const &b)
+{
+ return strcmp(a.first->tag(), b.first->tag()) < 0;
+}
+
+bool cCpuDescending(std::pair<device_t *, debug_registerpoint const *> const &a, std::pair<device_t *, debug_registerpoint const *> const &b)
+{
+ return cCpuAscending(b, a);
+}
+
+bool cConditionAscending(std::pair<device_t *, debug_registerpoint const *> const &a, std::pair<device_t *, debug_registerpoint const *> const &b)
+{
+ return strcmp(a.second->condition(), b.second->condition()) < 0;
+}
+
+bool cConditionDescending(std::pair<device_t *, debug_registerpoint const *> const &a, std::pair<device_t *, debug_registerpoint const *> const &b)
+{
+ return cConditionAscending(b, a);
+}
+
+bool cActionAscending(std::pair<device_t *, debug_registerpoint const *> const &a, std::pair<device_t *, debug_registerpoint const *> const &b)
+{
+ return a.second->action() < b.second->action();
+}
+
+bool cActionDescending(std::pair<device_t *, debug_registerpoint const *> const &a, std::pair<device_t *, debug_registerpoint const *> const &b)
+{
+ return cActionAscending(b, a);
+}
+
+
+constexpr int TABLE_BREAKS[] = { 5, 9, 31, 49, 66 };
+
+} // anonymous namespace
+
+
+//**************************************************************************
+// DEBUG VIEW REGISTER POINTS
+//**************************************************************************
+
+
+//-------------------------------------------------
+// debug_view_registerpoints - constructor
+//-------------------------------------------------
+
+debug_view_registerpoints::debug_view_registerpoints(running_machine &machine, debug_view_osd_update_func osdupdate, void *osdprivate)
+ : debug_view(machine, DVT_REGISTER_POINTS, osdupdate, osdprivate)
+ , m_sort_type(cIndexAscending)
+{
+ // fail if no available sources
+ enumerate_sources();
+ if (m_source_list.empty())
+ throw std::bad_alloc();
+}
+
+
+//-------------------------------------------------
+// ~debug_view_registerpoints - destructor
+//-------------------------------------------------
+
+debug_view_registerpoints::~debug_view_registerpoints()
+{
+}
+
+
+//-------------------------------------------------
+// enumerate_sources - enumerate all possible
+// sources for a disassembly view
+//-------------------------------------------------
+
+void debug_view_registerpoints::enumerate_sources()
+{
+ // start with an empty list
+ m_source_list.clear();
+
+ // iterate over devices with disassembly interfaces
+ for (device_disasm_interface &dasm : disasm_interface_enumerator(machine().root_device()))
+ {
+ m_source_list.emplace_back(
+ std::make_unique<debug_view_source>(
+ util::string_format("%s '%s'", dasm.device().name(), dasm.device().tag()),
+ &dasm.device()));
+ }
+
+ // reset the source to a known good entry
+ if (!m_source_list.empty())
+ set_source(*m_source_list[0]);
+}
+
+
+//-------------------------------------------------
+// view_click - handle a mouse click within the
+// current view
+//-------------------------------------------------
+
+void debug_view_registerpoints::view_click(const int button, const debug_view_xy& pos)
+{
+ bool clickedTopRow = (m_topleft.y == pos.y);
+
+ if (clickedTopRow)
+ {
+ if (pos.x < TABLE_BREAKS[0])
+ m_sort_type = (m_sort_type == &cIndexAscending) ? &cIndexDescending : &cIndexAscending;
+ else if (pos.x < TABLE_BREAKS[1])
+ m_sort_type = (m_sort_type == &cEnabledAscending) ? &cEnabledDescending : &cEnabledAscending;
+ else if (pos.x < TABLE_BREAKS[2])
+ m_sort_type = (m_sort_type == &cCpuAscending) ? &cCpuDescending : &cCpuAscending;
+ else if (pos.x < TABLE_BREAKS[3])
+ m_sort_type = (m_sort_type == &cConditionAscending) ? &cConditionDescending : &cConditionAscending;
+ else if (pos.x < TABLE_BREAKS[4])
+ m_sort_type = (m_sort_type == &cActionAscending) ? &cActionDescending : &cActionAscending;
+ }
+ else
+ {
+ // Gather a sorted list of all the breakpoints for all the CPUs
+ gather_registerpoints();
+
+ int rpIndex = pos.y - 1;
+ if ((rpIndex >= m_buffer.size()) || (rpIndex < 0))
+ return;
+
+ // Enable / disable
+ m_buffer[rpIndex].first->debug()->registerpoint_enable(
+ m_buffer[rpIndex].second->index(),
+ !m_buffer[rpIndex].second->enabled());
+ }
+
+ begin_update();
+ m_update_pending = true;
+ end_update();
+}
+
+
+void debug_view_registerpoints::pad_ostream_to_length(std::ostream& str, int len)
+{
+ auto const current = str.tellp();
+ if (current < decltype(current)(len))
+ str << std::setw(decltype(current)(len) - current) << "";
+}
+
+
+void debug_view_registerpoints::gather_registerpoints()
+{
+ m_buffer.resize(0);
+ for (auto &source : m_source_list)
+ {
+ // Collect
+ device_debug &debugInterface = *source->device()->debug();
+ for (const auto &rp : debugInterface.registerpoint_list())
+ m_buffer.emplace_back(source->device(), &rp);
+ }
+
+ // And now for the sort
+ if (!m_buffer.empty())
+ std::stable_sort(m_buffer.begin(), m_buffer.end(), m_sort_type);
+}
+
+
+//-------------------------------------------------
+// view_update - update the contents of the
+// registerpoints view
+//-------------------------------------------------
+
+void debug_view_registerpoints::view_update()
+{
+ // Gather a list of all the registerpoints for all the CPUs
+ gather_registerpoints();
+
+ // Set the view region so the scroll bars update
+ m_total.x = TABLE_BREAKS[std::size(TABLE_BREAKS) - 1];
+ m_total.y = m_buffer.size() + 1;
+ if (m_total.y < 10)
+ m_total.y = 10;
+
+ // Draw
+ debug_view_char *dest = &m_viewdata[0];
+ util::ovectorstream linebuf;
+ linebuf.reserve(std::size(TABLE_BREAKS) - 1);
+
+ // Header
+ if (m_visible.y > 0)
+ {
+ linebuf.clear();
+ linebuf.rdbuf()->clear();
+ linebuf << "ID";
+ if (m_sort_type == &cIndexAscending) linebuf.put('\\');
+ else if (m_sort_type == &cIndexDescending) linebuf.put('/');
+ pad_ostream_to_length(linebuf, TABLE_BREAKS[0]);
+ linebuf << "En";
+ if (m_sort_type == &cEnabledAscending) linebuf.put('\\');
+ else if (m_sort_type == &cEnabledDescending) linebuf.put('/');
+ pad_ostream_to_length(linebuf, TABLE_BREAKS[1]);
+ linebuf << "CPU";
+ if (m_sort_type == &cCpuAscending) linebuf.put('\\');
+ else if (m_sort_type == &cCpuDescending) linebuf.put('/');
+ pad_ostream_to_length(linebuf, TABLE_BREAKS[2]);
+ linebuf << "Condition";
+ if (m_sort_type == &cConditionAscending) linebuf.put('\\');
+ else if (m_sort_type == &cConditionDescending) linebuf.put('/');
+ pad_ostream_to_length(linebuf, TABLE_BREAKS[3]);
+ linebuf << "Action";
+ if (m_sort_type == &cActionAscending) linebuf.put('\\');
+ else if (m_sort_type == &cActionDescending) linebuf.put('/');
+ pad_ostream_to_length(linebuf, TABLE_BREAKS[4]);
+
+ auto const &text(linebuf.vec());
+ for (u32 i = m_topleft.x; i < (m_topleft.x + m_visible.x); i++, dest++)
+ {
+ dest->byte = (i < text.size()) ? text[i] : ' ';
+ dest->attrib = DCA_ANCILLARY;
+ }
+ }
+
+ for (int row = 1; row < m_visible.y; row++)
+ {
+ // Breakpoints
+ int rpi = row + m_topleft.y - 1;
+ if ((rpi < m_buffer.size()) && (rpi >= 0))
+ {
+ point_pair const &rpp = m_buffer[rpi];
+
+ linebuf.clear();
+ linebuf.rdbuf()->clear();
+ util::stream_format(linebuf, "%2X", rpp.second->index());
+ pad_ostream_to_length(linebuf, TABLE_BREAKS[0]);
+ linebuf.put(rpp.second->enabled() ? 'X' : 'O');
+ pad_ostream_to_length(linebuf, TABLE_BREAKS[1]);
+ linebuf << rpp.first->tag();
+ pad_ostream_to_length(linebuf, TABLE_BREAKS[2]);
+ linebuf << rpp.second->condition();
+ pad_ostream_to_length(linebuf, TABLE_BREAKS[3]);
+ linebuf << rpp.second->action();
+ pad_ostream_to_length(linebuf, TABLE_BREAKS[4]);
+
+ auto const &text(linebuf.vec());
+ for (u32 i = m_topleft.x; i < (m_topleft.x + m_visible.x); i++, dest++)
+ {
+ dest->byte = (i < text.size()) ? text[i] : ' ';
+ dest->attrib = DCA_NORMAL;
+
+ // Color disabled breakpoints red
+ if ((i >= TABLE_BREAKS[0]) && (i < TABLE_BREAKS[1]) && !rpp.second->enabled())
+ dest->attrib |= DCA_CHANGED;
+ }
+ }
+ else
+ {
+ // Fill the remaining vertical space
+ for (u32 i = m_topleft.x; i < (m_topleft.x + m_visible.x); i++, dest++)
+ {
+ dest->byte = ' ';
+ dest->attrib = DCA_NORMAL;
+ }
+ }
+ }
+}
diff --git a/src/emu/debug/dvrpoints.h b/src/emu/debug/dvrpoints.h
new file mode 100644
index 00000000000..31b821d7fae
--- /dev/null
+++ b/src/emu/debug/dvrpoints.h
@@ -0,0 +1,52 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/*********************************************************************
+
+ dvrpoints.h
+
+ Registerpoint debugger view.
+
+***************************************************************************/
+#ifndef MAME_EMU_DEBUG_DVRPOINTS_H
+#define MAME_EMU_DEBUG_DVRPOINTS_H
+
+#pragma once
+
+#include "debugvw.h"
+
+#include <utility>
+#include <vector>
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// debug view for breakpoints
+class debug_view_registerpoints : public debug_view
+{
+ friend class debug_view_manager;
+
+ // construction/destruction
+ debug_view_registerpoints(running_machine &machine, debug_view_osd_update_func osdupdate, void *osdprivate);
+ virtual ~debug_view_registerpoints();
+
+protected:
+ // view overrides
+ virtual void view_update() override;
+ virtual void view_click(int button, debug_view_xy const &pos) override;
+
+private:
+ using point_pair = std::pair<device_t *, debug_registerpoint const *>;
+
+ // internal helpers
+ void enumerate_sources();
+ void pad_ostream_to_length(std::ostream& str, int len);
+ void gather_registerpoints();
+
+ // internal state
+ bool (*m_sort_type)(point_pair const &, point_pair const &);
+ std::vector<point_pair> m_buffer;
+};
+
+#endif // MAME_EMU_DEBUG_DVBPOINTS_H
diff --git a/src/emu/debug/dvstate.cpp b/src/emu/debug/dvstate.cpp
index e0004f9bb60..931ae51111b 100644
--- a/src/emu/debug/dvstate.cpp
+++ b/src/emu/debug/dvstate.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Aaron Giles
/*********************************************************************
- dvstate.c
+ dvstate.cpp
State debugger view.
@@ -16,13 +16,6 @@
#include "screen.h"
-const int debug_view_state::REG_DIVIDER;
-const int debug_view_state::REG_CYCLES;
-const int debug_view_state::REG_BEAMX;
-const int debug_view_state::REG_BEAMY;
-const int debug_view_state::REG_FRAME;
-
-
//**************************************************************************
// DEBUG VIEW STATE SOURCE
//**************************************************************************
@@ -31,8 +24,8 @@ const int debug_view_state::REG_FRAME;
// debug_view_state_source - constructor
//-------------------------------------------------
-debug_view_state_source::debug_view_state_source(const char *name, device_t &device)
- : debug_view_source(name, &device)
+debug_view_state_source::debug_view_state_source(std::string &&name, device_t &device)
+ : debug_view_source(std::move(name), &device)
, m_stateintf(dynamic_cast<device_state_interface *>(&device))
, m_execintf(dynamic_cast<device_execute_interface *>(&device))
{
@@ -55,7 +48,7 @@ debug_view_state::debug_view_state(running_machine &machine, debug_view_osd_upda
{
// fail if no available sources
enumerate_sources();
- if (m_source_list.count() == 0)
+ if (m_source_list.empty())
throw std::bad_alloc();
}
@@ -78,18 +71,20 @@ debug_view_state::~debug_view_state()
void debug_view_state::enumerate_sources()
{
// start with an empty list
- m_source_list.reset();
+ m_source_list.clear();
// iterate over devices that have state interfaces
- std::string name;
- for (device_state_interface &state : state_interface_iterator(machine().root_device()))
+ for (device_state_interface &state : state_interface_enumerator(machine().root_device()))
{
- name = string_format("%s '%s'", state.device().name(), state.device().tag());
- m_source_list.append(*global_alloc(debug_view_state_source(name.c_str(), state.device())));
+ m_source_list.emplace_back(
+ std::make_unique<debug_view_state_source>(
+ util::string_format("%s '%s'", state.device().name(), state.device().tag()),
+ state.device()));
}
// reset the source to a known good entry
- set_source(*m_source_list.first());
+ if (!m_source_list.empty())
+ set_source(*m_source_list[0]);
}
@@ -119,8 +114,8 @@ void debug_view_state::recompute()
// add a cycles entry: cycles:99999999
m_state_list.emplace_back(REG_CYCLES, "cycles", 8);
- screen_device_iterator screen_iterator = screen_device_iterator(machine().root_device());
- screen_device_iterator::auto_iterator iter = screen_iterator.begin();
+ screen_device_enumerator screen_iterator(machine().root_device());
+ screen_device_enumerator::iterator iter = screen_iterator.begin();
const int screen_count = screen_iterator.count();
if (screen_count == 1)
@@ -145,7 +140,9 @@ void debug_view_state::recompute()
}
// add a flags entry: flags:xxxxxxxx
- m_state_list.emplace_back(STATE_GENFLAGS, "flags", source.m_stateintf->state_string_max_length(STATE_GENFLAGS));
+ const device_state_entry *flags = source.m_stateintf->state_find_entry(STATE_GENFLAGS);
+ if (flags != nullptr)
+ m_state_list.emplace_back(STATE_GENFLAGS, "flags", flags->max_length());
// add a divider entry
m_state_list.emplace_back(REG_DIVIDER, "", 0);
@@ -156,7 +153,7 @@ void debug_view_state::recompute()
if (entry->divider())
m_state_list.emplace_back(REG_DIVIDER, "", 0);
else if (entry->visible())
- m_state_list.emplace_back(entry->index(), entry->symbol(), source.m_stateintf->state_string_max_length(entry->index()));
+ m_state_list.emplace_back(entry->index(), entry->symbol(), entry->max_length());
}
// count the entries and determine the maximum tag and value sizes
@@ -239,25 +236,28 @@ void debug_view_state::view_update()
case REG_BEAMX_S0: case REG_BEAMX_S1: case REG_BEAMX_S2: case REG_BEAMX_S3:
case REG_BEAMX_S4: case REG_BEAMX_S5: case REG_BEAMX_S6: case REG_BEAMX_S7:
- curitem.update(screen_device_iterator(machine().root_device()).byindex(-(curitem.index() - REG_BEAMX_S0))->hpos(), cycles_changed);
+ curitem.update(screen_device_enumerator(machine().root_device()).byindex(-(curitem.index() - REG_BEAMX_S0))->hpos(), cycles_changed);
valstr = string_format("%4d", curitem.value());
break;
case REG_BEAMY_S0: case REG_BEAMY_S1: case REG_BEAMY_S2: case REG_BEAMY_S3:
case REG_BEAMY_S4: case REG_BEAMY_S5: case REG_BEAMY_S6: case REG_BEAMY_S7:
- curitem.update(screen_device_iterator(machine().root_device()).byindex(-(curitem.index() - REG_BEAMY_S0))->vpos(), cycles_changed);
+ curitem.update(screen_device_enumerator(machine().root_device()).byindex(-(curitem.index() - REG_BEAMY_S0))->vpos(), cycles_changed);
valstr = string_format("%4d", curitem.value());
break;
case REG_FRAME_S0: case REG_FRAME_S1: case REG_FRAME_S2: case REG_FRAME_S3:
case REG_FRAME_S4: case REG_FRAME_S5: case REG_FRAME_S6: case REG_FRAME_S7:
- curitem.update(screen_device_iterator(machine().root_device()).byindex(-(curitem.index() - REG_FRAME_S0))->frame_number(), cycles_changed);
+ curitem.update(screen_device_enumerator(machine().root_device()).byindex(-(curitem.index() - REG_FRAME_S0))->frame_number(), cycles_changed);
valstr = string_format("%-6d", curitem.value());
break;
default:
curitem.update(source.m_stateintf->state_int(curitem.index()), cycles_changed);
valstr = source.m_stateintf->state_string(curitem.index());
+ // state_string may not always provide the maximum number of characters with some formats
+ valstr.resize(curitem.value_length(), ' ');
+ break;
}
// if this row is visible, add it to the buffer
diff --git a/src/emu/debug/dvstate.h b/src/emu/debug/dvstate.h
index 8239a53f5db..63c0eb38822 100644
--- a/src/emu/debug/dvstate.h
+++ b/src/emu/debug/dvstate.h
@@ -25,8 +25,10 @@ class debug_view_state_source : public debug_view_source
{
friend class debug_view_state;
+public:
// construction/destruction
- debug_view_state_source(const char *name, device_t &device);
+ debug_view_state_source(std::string &&name, device_t &device);
+
private:
// internal state
device_state_interface *m_stateintf; // state interface
diff --git a/src/emu/debug/dvtext.cpp b/src/emu/debug/dvtext.cpp
index e55a6b278e8..6a4d2893562 100644
--- a/src/emu/debug/dvtext.cpp
+++ b/src/emu/debug/dvtext.cpp
@@ -2,17 +2,19 @@
// copyright-holders:Aaron Giles
/*********************************************************************
- dvtext.c
+ dvtext.cpp
Debugger simple text-based views.
***************************************************************************/
#include "emu.h"
-#include "debugvw.h"
#include "dvtext.h"
+
#include "debugcon.h"
#include "debugger.h"
+#include "debugvw.h"
+
//**************************************************************************
// DEBUG VIEW TEXTBUF
@@ -22,11 +24,16 @@
// debug_view_textbuf - constructor
//-------------------------------------------------
-debug_view_textbuf::debug_view_textbuf(running_machine &machine, debug_view_type type, debug_view_osd_update_func osdupdate, void *osdprivate, text_buffer &textbuf)
- : debug_view(machine, type, osdupdate, osdprivate),
- m_textbuf(textbuf),
- m_at_bottom(true),
- m_topseq(0)
+debug_view_textbuf::debug_view_textbuf(
+ running_machine &machine,
+ debug_view_type type,
+ debug_view_osd_update_func osdupdate,
+ void *osdprivate,
+ text_buffer &textbuf)
+ : debug_view(machine, type, osdupdate, osdprivate)
+ , m_textbuf(textbuf)
+ , m_at_bottom(true)
+ , m_topseq(0)
{
}
@@ -47,7 +54,7 @@ debug_view_textbuf::~debug_view_textbuf()
void debug_view_textbuf::clear()
{
begin_update();
- text_buffer_clear(&m_textbuf);
+ text_buffer_clear(m_textbuf);
m_at_bottom = true;
m_topseq = 0;
end_update();
@@ -61,8 +68,8 @@ void debug_view_textbuf::clear()
void debug_view_textbuf::view_update()
{
// update the console info
- m_total.x = text_buffer_max_width(&m_textbuf);
- m_total.y = text_buffer_num_lines(&m_textbuf);
+ m_total.x = text_buffer_max_width(m_textbuf);
+ m_total.y = text_buffer_num_lines(m_textbuf);
if (m_total.x < 80)
m_total.x = 80;
@@ -71,24 +78,24 @@ void debug_view_textbuf::view_update()
if (!m_at_bottom)
{
curseq = m_topseq;
- if (!text_buffer_get_seqnum_line(&m_textbuf, curseq))
+ if (!text_buffer_get_seqnum_line(m_textbuf, curseq))
m_at_bottom = true;
}
if (m_at_bottom)
{
- curseq = text_buffer_line_index_to_seqnum(&m_textbuf, m_total.y - 1);
+ curseq = text_buffer_line_index_to_seqnum(m_textbuf, m_total.y - 1);
if (m_total.y < m_visible.y)
curseq -= m_total.y - 1;
else
curseq -= m_visible.y - 1;
}
- m_topleft.y = curseq - text_buffer_line_index_to_seqnum(&m_textbuf, 0);
+ m_topleft.y = curseq - text_buffer_line_index_to_seqnum(m_textbuf, 0);
// loop over visible rows
debug_view_char *dest = &m_viewdata[0];
for (u32 row = 0; row < m_visible.y; row++)
{
- const char *line = text_buffer_get_seqnum_line(&m_textbuf, curseq++);
+ const char *line = text_buffer_get_seqnum_line(m_textbuf, curseq++);
u32 col = 0;
// if this visible row is valid, add it to the buffer
@@ -133,7 +140,7 @@ void debug_view_textbuf::view_notify(debug_view_notification type)
/* otherwise, track the seqence number of the top line */
if (!m_at_bottom)
- m_topseq = text_buffer_line_index_to_seqnum(&m_textbuf, m_topleft.y);
+ m_topseq = text_buffer_line_index_to_seqnum(m_textbuf, m_topleft.y);
}
}
@@ -148,7 +155,7 @@ void debug_view_textbuf::view_notify(debug_view_notification type)
//-------------------------------------------------
debug_view_console::debug_view_console(running_machine &machine, debug_view_osd_update_func osdupdate, void *osdprivate)
- : debug_view_textbuf(machine, DVT_CONSOLE, osdupdate, osdprivate, *machine.debugger().console().get_console_textbuf())
+ : debug_view_textbuf(machine, DVT_CONSOLE, osdupdate, osdprivate, machine.debugger().console().get_console_textbuf())
{
}
@@ -163,6 +170,6 @@ debug_view_console::debug_view_console(running_machine &machine, debug_view_osd_
//-------------------------------------------------
debug_view_log::debug_view_log(running_machine &machine, debug_view_osd_update_func osdupdate, void *osdprivate)
- : debug_view_textbuf(machine, DVT_LOG, osdupdate, osdprivate, *machine.debugger().console().get_errorlog_textbuf())
+ : debug_view_textbuf(machine, DVT_LOG, osdupdate, osdprivate, machine.debugger().console().get_errorlog_textbuf())
{
}
diff --git a/src/emu/debug/dvtext.h b/src/emu/debug/dvtext.h
index 14a323794db..e6b65c137b3 100644
--- a/src/emu/debug/dvtext.h
+++ b/src/emu/debug/dvtext.h
@@ -31,7 +31,12 @@ public:
protected:
// construction/destruction
- debug_view_textbuf(running_machine &machine, debug_view_type type, debug_view_osd_update_func osdupdate, void *osdprivate, text_buffer &textbuf);
+ debug_view_textbuf(
+ running_machine &machine,
+ debug_view_type type,
+ debug_view_osd_update_func osdupdate,
+ void *osdprivate,
+ text_buffer &textbuf);
virtual ~debug_view_textbuf();
protected:
@@ -41,9 +46,9 @@ protected:
private:
// internal state
- text_buffer & m_textbuf; /* pointer to the text buffer */
- bool m_at_bottom; /* are we tracking new stuff being added? */
- u32 m_topseq; /* sequence number of the top line */
+ text_buffer & m_textbuf; // pointer to the text buffer
+ bool m_at_bottom; // are we tracking new stuff being added?
+ u32 m_topseq; // sequence number of the top line
};
diff --git a/src/emu/debug/dvwpoints.cpp b/src/emu/debug/dvwpoints.cpp
index b0584b5d61c..116904232a1 100644
--- a/src/emu/debug/dvwpoints.cpp
+++ b/src/emu/debug/dvwpoints.cpp
@@ -1,8 +1,8 @@
// license:BSD-3-Clause
-// copyright-holders:Aaron Giles
+// copyright-holders:Andrew Gardner, Vas Crabb
/*********************************************************************
- dvwpoints.c
+ dvwpoints.cpp
Watchpoint debugger view.
@@ -11,87 +11,90 @@
#include "emu.h"
#include "dvwpoints.h"
+#include "debugcpu.h"
+#include "points.h"
+
#include <algorithm>
#include <iomanip>
-static bool cIndexAscending(const device_debug::watchpoint *a, const device_debug::watchpoint *b)
+static bool cIndexAscending(const debug_watchpoint *a, const debug_watchpoint *b)
{
return a->index() < b->index();
}
-static bool cIndexDescending(const device_debug::watchpoint *a, const device_debug::watchpoint *b)
+static bool cIndexDescending(const debug_watchpoint *a, const debug_watchpoint *b)
{
return cIndexAscending(b, a);
}
-static bool cEnabledAscending(const device_debug::watchpoint *a, const device_debug::watchpoint *b)
+static bool cEnabledAscending(const debug_watchpoint *a, const debug_watchpoint *b)
{
return !a->enabled() && b->enabled();
}
-static bool cEnabledDescending(const device_debug::watchpoint *a, const device_debug::watchpoint *b)
+static bool cEnabledDescending(const debug_watchpoint *a, const debug_watchpoint *b)
{
return cEnabledAscending(b, a);
}
-static bool cCpuAscending(const device_debug::watchpoint *a, const device_debug::watchpoint *b)
+static bool cCpuAscending(const debug_watchpoint *a, const debug_watchpoint *b)
{
return strcmp(a->debugInterface()->device().tag(), b->debugInterface()->device().tag()) < 0;
}
-static bool cCpuDescending(const device_debug::watchpoint *a, const device_debug::watchpoint *b)
+static bool cCpuDescending(const debug_watchpoint *a, const debug_watchpoint *b)
{
return cCpuAscending(b, a);
}
-static bool cSpaceAscending(const device_debug::watchpoint *a, const device_debug::watchpoint *b)
+static bool cSpaceAscending(const debug_watchpoint *a, const debug_watchpoint *b)
{
return strcmp(a->space().name(), b->space().name()) < 0;
}
-static bool cSpaceDescending(const device_debug::watchpoint *a, const device_debug::watchpoint *b)
+static bool cSpaceDescending(const debug_watchpoint *a, const debug_watchpoint *b)
{
return cSpaceAscending(b, a);
}
-static bool cAddressAscending(const device_debug::watchpoint *a, const device_debug::watchpoint *b)
+static bool cAddressAscending(const debug_watchpoint *a, const debug_watchpoint *b)
{
return a->address() < b->address();
}
-static bool cAddressDescending(const device_debug::watchpoint *a, const device_debug::watchpoint *b)
+static bool cAddressDescending(const debug_watchpoint *a, const debug_watchpoint *b)
{
return cAddressAscending(b, a);
}
-static bool cTypeAscending(const device_debug::watchpoint *a, const device_debug::watchpoint *b)
+static bool cTypeAscending(const debug_watchpoint *a, const debug_watchpoint *b)
{
return int(a->type()) < int(b->type());
}
-static bool cTypeDescending(const device_debug::watchpoint *a, const device_debug::watchpoint *b)
+static bool cTypeDescending(const debug_watchpoint *a, const debug_watchpoint *b)
{
return cTypeAscending(b, a);
}
-static bool cConditionAscending(const device_debug::watchpoint *a, const device_debug::watchpoint *b)
+static bool cConditionAscending(const debug_watchpoint *a, const debug_watchpoint *b)
{
return strcmp(a->condition(), b->condition()) < 0;
}
-static bool cConditionDescending(const device_debug::watchpoint *a, const device_debug::watchpoint *b)
+static bool cConditionDescending(const debug_watchpoint *a, const debug_watchpoint *b)
{
return cConditionAscending(b, a);
}
-static bool cActionAscending(const device_debug::watchpoint *a, const device_debug::watchpoint *b)
+static bool cActionAscending(const debug_watchpoint *a, const debug_watchpoint *b)
{
return a->action() < b->action();
}
-static bool cActionDescending(const device_debug::watchpoint *a, const device_debug::watchpoint *b)
+static bool cActionDescending(const debug_watchpoint *a, const debug_watchpoint *b)
{
return cActionAscending(b, a);
}
@@ -113,7 +116,7 @@ debug_view_watchpoints::debug_view_watchpoints(running_machine &machine, debug_v
{
// fail if no available sources
enumerate_sources();
- if (m_source_list.count() == 0)
+ if (m_source_list.empty())
throw std::bad_alloc();
}
@@ -135,18 +138,20 @@ debug_view_watchpoints::~debug_view_watchpoints()
void debug_view_watchpoints::enumerate_sources()
{
// start with an empty list
- m_source_list.reset();
+ m_source_list.clear();
// iterate over devices with disassembly interfaces
- for (device_disasm_interface &dasm : disasm_interface_iterator(machine().root_device()))
+ for (device_disasm_interface &dasm : disasm_interface_enumerator(machine().root_device()))
{
- std::string name;
- name = string_format("%s '%s'", dasm.device().name(), dasm.device().tag());
- m_source_list.append(*global_alloc(debug_view_source(name.c_str(), &dasm.device())));
+ m_source_list.emplace_back(
+ std::make_unique<debug_view_source>(
+ util::string_format("%s '%s'", dasm.device().name(), dasm.device().tag()),
+ &dasm.device()));
}
// reset the source to a known good entry
- set_source(*m_source_list.first());
+ if (!m_source_list.empty())
+ set_source(*m_source_list[0]);
}
@@ -208,10 +213,10 @@ void debug_view_watchpoints::pad_ostream_to_length(std::ostream& str, int len)
void debug_view_watchpoints::gather_watchpoints()
{
m_buffer.resize(0);
- for (const debug_view_source &source : m_source_list)
+ for (auto &source : m_source_list)
{
// Collect
- device_debug &debugInterface = *source.device()->debug();
+ device_debug &debugInterface = *source->device()->debug();
for (int spacenum = 0; spacenum < debugInterface.watchpoint_space_count(); ++spacenum)
{
for (const auto &wp : debugInterface.watchpoint_vector(spacenum))
@@ -236,7 +241,7 @@ void debug_view_watchpoints::view_update()
gather_watchpoints();
// Set the view region so the scroll bars update
- m_total.x = tableBreaks[ARRAY_LENGTH(tableBreaks) - 1];
+ m_total.x = tableBreaks[std::size(tableBreaks) - 1];
m_total.y = m_buffer.size() + 1;
if (m_total.y < 10)
m_total.y = 10;
@@ -244,7 +249,7 @@ void debug_view_watchpoints::view_update()
// Draw
debug_view_char *dest = &m_viewdata[0];
util::ovectorstream linebuf;
- linebuf.reserve(ARRAY_LENGTH(tableBreaks) - 1);
+ linebuf.reserve(std::size(tableBreaks) - 1);
// Header
if (m_visible.y > 0)
@@ -299,7 +304,7 @@ void debug_view_watchpoints::view_update()
if ((wpi < m_buffer.size()) && wpi >= 0)
{
static char const *const types[] = { "unkn ", "read ", "write", "r/w " };
- device_debug::watchpoint *const wp = m_buffer[wpi];
+ debug_watchpoint *const wp = m_buffer[wpi];
linebuf.clear();
linebuf.rdbuf()->clear();
diff --git a/src/emu/debug/dvwpoints.h b/src/emu/debug/dvwpoints.h
index 26b7c676e0e..df1145742b6 100644
--- a/src/emu/debug/dvwpoints.h
+++ b/src/emu/debug/dvwpoints.h
@@ -1,5 +1,5 @@
// license:BSD-3-Clause
-// copyright-holders:Aaron Giles
+// copyright-holders:Andrew Gardner, Vas Crabb
/*********************************************************************
dvwpoints.h
@@ -7,14 +7,12 @@
Watchpoint debugger view.
***************************************************************************/
-
#ifndef MAME_EMU_DEBUG_DVWPOINTS_H
#define MAME_EMU_DEBUG_DVWPOINTS_H
#pragma once
#include "debugvw.h"
-#include "debugcpu.h"
//**************************************************************************
@@ -43,8 +41,8 @@ private:
// internal state
- bool (*m_sortType)(const device_debug::watchpoint *, const device_debug::watchpoint *);
- std::vector<device_debug::watchpoint *> m_buffer;
+ bool (*m_sortType)(const debug_watchpoint *, const debug_watchpoint *);
+ std::vector<debug_watchpoint *> m_buffer;
};
#endif // MAME_EMU_DEBUG_DVWPOINTS_H
diff --git a/src/emu/debug/express.cpp b/src/emu/debug/express.cpp
index bcd9269db30..9ab75b7b655 100644
--- a/src/emu/debug/express.cpp
+++ b/src/emu/debug/express.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Aaron Giles
/***************************************************************************
- express.c
+ express.cpp
Generic expressions engine.
@@ -31,7 +31,9 @@
#include "emu.h"
#include "express.h"
-#include <ctype.h>
+
+#include "corestr.h"
+#include <cctype>
@@ -39,19 +41,17 @@
DEBUGGING
***************************************************************************/
-#define DEBUG_TOKENS 0
+#define LOG_OUTPUT_FUNC osd_printf_info
+#define VERBOSE 0
+#include "logmacro.h"
+namespace {
/***************************************************************************
CONSTANTS
***************************************************************************/
-#ifndef DEFAULT_BASE
-#define DEFAULT_BASE 16 // hex unless otherwise specified
-#endif
-
-
// token.value values if token.is_operator()
enum
{
@@ -104,7 +104,7 @@ enum
//**************************************************************************
-// TYPE DEFINITIONS
+// REGISTER SYMBOL ENTRY
//**************************************************************************
// a symbol entry representing a register, with read/write callbacks
@@ -117,8 +117,8 @@ public:
integer_symbol_entry(symbol_table &table, const char *name, symbol_table::getter_func getter, symbol_table::setter_func setter, const std::string &format);
// symbol access
- virtual bool is_lval() const override;
- virtual u64 value() const override;
+ virtual bool is_lval() const override { return m_setter != nullptr; }
+ virtual u64 value() const override { return m_getter(); }
virtual void set_value(u64 newvalue) override;
private:
@@ -129,6 +129,61 @@ private:
};
+//-------------------------------------------------
+// integer_symbol_entry - constructor
+//-------------------------------------------------
+
+integer_symbol_entry::integer_symbol_entry(symbol_table &table, const char *name, symbol_table::read_write rw, u64 *ptr)
+ : symbol_entry(table, SMT_INTEGER, name, ""),
+ m_getter(ptr
+ ? symbol_table::getter_func([ptr] () { return *ptr; })
+ : symbol_table::getter_func([this] () { return m_value; })),
+ m_setter((rw == symbol_table::READ_ONLY)
+ ? symbol_table::setter_func(nullptr)
+ : ptr
+ ? symbol_table::setter_func([ptr] (u64 value) { *ptr = value; })
+ : symbol_table::setter_func([this] (u64 value) { m_value = value; })),
+ m_value(0)
+{
+}
+
+
+integer_symbol_entry::integer_symbol_entry(symbol_table &table, const char *name, u64 constval)
+ : symbol_entry(table, SMT_INTEGER, name, ""),
+ m_getter([this] () { return m_value; }),
+ m_setter(nullptr),
+ m_value(constval)
+{
+}
+
+
+integer_symbol_entry::integer_symbol_entry(symbol_table &table, const char *name, symbol_table::getter_func getter, symbol_table::setter_func setter, const std::string &format)
+ : symbol_entry(table, SMT_INTEGER, name, format),
+ m_getter(std::move(getter)),
+ m_setter(std::move(setter)),
+ m_value(0)
+{
+}
+
+
+//-------------------------------------------------
+// set_value - set the value of this symbol
+//-------------------------------------------------
+
+void integer_symbol_entry::set_value(u64 newvalue)
+{
+ if (m_setter != nullptr)
+ m_setter(newvalue);
+ else
+ throw emu_fatalerror("Symbol '%s' is read-only", m_name);
+}
+
+
+
+//**************************************************************************
+// FUNCTION SYMBOL ENTRY
+//**************************************************************************
+
// a symbol entry representing a function
class function_symbol_entry : public symbol_entry
{
@@ -136,8 +191,12 @@ public:
// construction/destruction
function_symbol_entry(symbol_table &table, const char *name, int minparams, int maxparams, symbol_table::execute_func execute);
+ // getters
+ u16 minparams() const { return m_minparams; }
+ u16 maxparams() const { return m_maxparams; }
+
// symbol access
- virtual bool is_lval() const override;
+ virtual bool is_lval() const override { return false; }
virtual u64 value() const override;
virtual void set_value(u64 newvalue) override;
@@ -152,6 +211,82 @@ private:
};
+//-------------------------------------------------
+// function_symbol_entry - constructor
+//-------------------------------------------------
+
+function_symbol_entry::function_symbol_entry(symbol_table &table, const char *name, int minparams, int maxparams, symbol_table::execute_func execute)
+ : symbol_entry(table, SMT_FUNCTION, name, ""),
+ m_minparams(minparams),
+ m_maxparams(maxparams),
+ m_execute(std::move(execute))
+{
+}
+
+
+//-------------------------------------------------
+// value - return the value of this symbol
+//-------------------------------------------------
+
+u64 function_symbol_entry::value() const
+{
+ throw emu_fatalerror("Symbol '%s' is a function and cannot be used in this context", m_name);
+}
+
+
+//-------------------------------------------------
+// set_value - set the value of this symbol
+//-------------------------------------------------
+
+void function_symbol_entry::set_value(u64 newvalue)
+{
+ throw emu_fatalerror("Symbol '%s' is a function and cannot be written", m_name);
+}
+
+
+//-------------------------------------------------
+// execute - execute the function
+//-------------------------------------------------
+
+u64 function_symbol_entry::execute(int numparams, const u64 *paramlist)
+{
+ if (numparams < m_minparams)
+ throw emu_fatalerror("Function '%s' requires at least %d parameters", m_name, m_minparams);
+ if (numparams > m_maxparams)
+ throw emu_fatalerror("Function '%s' accepts no more than %d parameters", m_name, m_maxparams);
+ return m_execute(numparams, paramlist);
+}
+
+
+
+/***************************************************************************
+ INLINE FUNCTIONS
+***************************************************************************/
+
+inline std::pair<device_t &, char const *> get_device_search(running_machine &machine, device_memory_interface *memintf, char const *tag)
+{
+ if (tag)
+ {
+ if (('.' == tag[0]) && (!tag[1] || (':' == tag[1]) || ('^' == tag[1])))
+ return std::pair<device_t &, char const *>(memintf ? memintf->device() : machine.root_device(), tag + ((':' == tag[1]) ? 2 : 1));
+ else if (('^' == tag[0]) && memintf)
+ return std::pair<device_t &, char const *>(memintf->device(), tag);
+ else
+ return std::pair<device_t &, char const *>(machine.root_device(), tag);
+ }
+ else if (memintf)
+ {
+ return std::pair<device_t &, char const *>(memintf->device(), "");
+ }
+ else
+ {
+ return std::pair<device_t &, char const *>(machine.root_device(), "");
+ }
+}
+
+} // anonymous namespace
+
+
//**************************************************************************
// EXPRESSION ERROR
@@ -162,7 +297,7 @@ private:
// given expression error
//-------------------------------------------------
-const char *expression_error::code_string() const
+std::string expression_error::code_string() const
{
switch (m_code)
{
@@ -178,6 +313,8 @@ const char *expression_error::code_string() const
case DIVIDE_BY_ZERO: return "divide by zero";
case OUT_OF_MEMORY: return "out of memory";
case INVALID_PARAM_COUNT: return "invalid number of parameters";
+ case TOO_FEW_PARAMS: return util::string_format("too few parameters (at least %d required)", m_num);
+ case TOO_MANY_PARAMS: return util::string_format("too many parameters (no more than %d accepted)", m_num);
case UNBALANCED_QUOTES: return "unbalanced quotes";
case TOO_MANY_STRINGS: return "too many strings";
case INVALID_MEMORY_SIZE: return "invalid memory size (b/w/d/q expected)";
@@ -200,8 +337,7 @@ const char *expression_error::code_string() const
//-------------------------------------------------
symbol_entry::symbol_entry(symbol_table &table, symbol_type type, const char *name, const std::string &format)
- : m_next(nullptr),
- m_table(table),
+ : m_table(table),
m_type(type),
m_name(name),
m_format(format)
@@ -220,312 +356,652 @@ symbol_entry::~symbol_entry()
//**************************************************************************
-// REGISTER SYMBOL ENTRY
+// SYMBOL TABLE
//**************************************************************************
//-------------------------------------------------
-// integer_symbol_entry - constructor
+// symbol_table - constructor
//-------------------------------------------------
-integer_symbol_entry::integer_symbol_entry(symbol_table &table, const char *name, symbol_table::read_write rw, u64 *ptr)
- : symbol_entry(table, SMT_INTEGER, name, ""),
- m_getter(ptr
- ? symbol_table::getter_func([ptr] (symbol_table &table) { return *ptr; })
- : symbol_table::getter_func([this] (symbol_table &table) { return m_value; })),
- m_setter((rw == symbol_table::READ_ONLY)
- ? symbol_table::setter_func(nullptr)
- : ptr
- ? symbol_table::setter_func([ptr] (symbol_table &table, u64 value) { *ptr = value; })
- : symbol_table::setter_func([this] (symbol_table &table, u64 value) { m_value = value; })),
- m_value(0)
-{
-}
-
-
-integer_symbol_entry::integer_symbol_entry(symbol_table &table, const char *name, u64 constval)
- : symbol_entry(table, SMT_INTEGER, name, ""),
- m_getter([this] (symbol_table &table) { return m_value; }),
- m_setter(nullptr),
- m_value(constval)
-{
-}
-
-
-integer_symbol_entry::integer_symbol_entry(symbol_table &table, const char *name, symbol_table::getter_func getter, symbol_table::setter_func setter, const std::string &format)
- : symbol_entry(table, SMT_INTEGER, name, format),
- m_getter(getter),
- m_setter(setter),
- m_value(0)
+symbol_table::symbol_table(running_machine &machine, symbol_table *parent, device_t *device)
+ : m_machine(machine)
+ , m_parent(parent)
+ , m_memintf(dynamic_cast<device_memory_interface *>(device))
+ , m_memory_modified(nullptr)
{
}
//-------------------------------------------------
-// is_lval - is this symbol allowable as an lval?
+// set_memory_modified_func - install notifier
+// for when memory is modified in debugger
//-------------------------------------------------
-bool integer_symbol_entry::is_lval() const
+void symbol_table::set_memory_modified_func(memory_modified_func modified)
{
- return (m_setter != nullptr);
+ m_memory_modified = std::move(modified);
}
//-------------------------------------------------
-// value - return the value of this symbol
+// add - add a new u64 pointer symbol
//-------------------------------------------------
-u64 integer_symbol_entry::value() const
+symbol_entry &symbol_table::add(const char *name, read_write rw, u64 *ptr)
{
- return m_getter(m_table);
+ m_symlist.erase(name);
+ return *m_symlist.emplace(name, std::make_unique<integer_symbol_entry>(*this, name, rw, ptr)).first->second;
}
//-------------------------------------------------
-// set_value - set the value of this symbol
+// add - add a new value symbol
//-------------------------------------------------
-void integer_symbol_entry::set_value(u64 newvalue)
+symbol_entry &symbol_table::add(const char *name, u64 value)
{
- if (m_setter != nullptr)
- m_setter(m_table, newvalue);
- else
- throw emu_fatalerror("Symbol '%s' is read-only", m_name.c_str());
+ m_symlist.erase(name);
+ return *m_symlist.emplace(name, std::make_unique<integer_symbol_entry>(*this, name, value)).first->second;
}
-
-//**************************************************************************
-// FUNCTION SYMBOL ENTRY
-//**************************************************************************
-
//-------------------------------------------------
-// function_symbol_entry - constructor
+// add - add a new register symbol
//-------------------------------------------------
-function_symbol_entry::function_symbol_entry(symbol_table &table, const char *name, int minparams, int maxparams, symbol_table::execute_func execute)
- : symbol_entry(table, SMT_FUNCTION, name, ""),
- m_minparams(minparams),
- m_maxparams(maxparams),
- m_execute(execute)
+symbol_entry &symbol_table::add(const char *name, getter_func getter, setter_func setter, const std::string &format_string)
{
+ m_symlist.erase(name);
+ return *m_symlist.emplace(name, std::make_unique<integer_symbol_entry>(*this, name, getter, setter, format_string)).first->second;
}
//-------------------------------------------------
-// is_lval - is this symbol allowable as an lval?
+// add - add a new function symbol
//-------------------------------------------------
-bool function_symbol_entry::is_lval() const
+symbol_entry &symbol_table::add(const char *name, int minparams, int maxparams, execute_func execute)
{
- return false;
+ m_symlist.erase(name);
+ return *m_symlist.emplace(name, std::make_unique<function_symbol_entry>(*this, name, minparams, maxparams, execute)).first->second;
}
//-------------------------------------------------
-// value - return the value of this symbol
+// find_deep - do a deep search for a symbol,
+// looking in the parent if needed
//-------------------------------------------------
-u64 function_symbol_entry::value() const
+symbol_entry *symbol_table::find_deep(const char *symbol)
{
- throw emu_fatalerror("Symbol '%s' is a function and cannot be used in this context", m_name.c_str());
+ // walk up the table hierarchy to find the owner
+ for (symbol_table *symtable = this; symtable != nullptr; symtable = symtable->m_parent)
+ {
+ symbol_entry *entry = symtable->find(symbol);
+ if (entry != nullptr)
+ return entry;
+ }
+ return nullptr;
}
//-------------------------------------------------
-// set_value - set the value of this symbol
+// value - return the value of a symbol
//-------------------------------------------------
-void function_symbol_entry::set_value(u64 newvalue)
+u64 symbol_table::value(const char *symbol)
{
- throw emu_fatalerror("Symbol '%s' is a function and cannot be written", m_name.c_str());
+ symbol_entry *entry = find_deep(symbol);
+ return (entry != nullptr) ? entry->value() : 0;
}
//-------------------------------------------------
-// execute - execute the function
+// set_value - set the value of a symbol
//-------------------------------------------------
-u64 function_symbol_entry::execute(int numparams, const u64 *paramlist)
+void symbol_table::set_value(const char *symbol, u64 value)
{
- if (numparams < m_minparams)
- throw emu_fatalerror("Function '%s' requires at least %d parameters", m_name.c_str(), m_minparams);
- if (numparams > m_maxparams)
- throw emu_fatalerror("Function '%s' accepts no more than %d parameters", m_name.c_str(), m_maxparams);
- return m_execute(m_table, numparams, paramlist);
+ symbol_entry *entry = find_deep(symbol);
+ if (entry != nullptr)
+ entry->set_value(value);
}
//**************************************************************************
-// SYMBOL TABLE
+// EXPRESSION MEMORY HANDLERS
//**************************************************************************
//-------------------------------------------------
-// symbol_table - constructor
+// read_memory - return 1,2,4 or 8 bytes
+// from the specified memory space
//-------------------------------------------------
-symbol_table::symbol_table(void *globalref, symbol_table *parent)
- : m_parent(parent),
- m_globalref(globalref),
- m_memory_param(nullptr),
- m_memory_valid(nullptr),
- m_memory_read(nullptr),
- m_memory_write(nullptr)
+u64 symbol_table::read_memory(address_space &space, offs_t address, int size, bool apply_translation)
{
-}
+ u64 result = ~u64(0) >> (64 - 8*size);
+ address_space *tspace = &space;
-//-------------------------------------------------
-// add - add a new u64 pointer symbol
-//-------------------------------------------------
+ if (apply_translation)
+ {
+ // mask against the logical byte mask
+ address &= space.logaddrmask();
-void symbol_table::configure_memory(void *param, valid_func valid, read_func read, write_func write)
-{
- m_memory_param = param;
- m_memory_valid = valid;
- m_memory_read = read;
- m_memory_write = write;
+ // translate if necessary; if not mapped, return 0xffffffffffffffff
+ if (!space.device().memory().translate(space.spacenum(), device_memory_interface::TR_READ, address, tspace))
+ return result;
+ }
+
+ // otherwise, call the reading function for the translated address
+ switch (size)
+ {
+ case 1: result = tspace->read_byte(address); break;
+ case 2: result = tspace->read_word_unaligned(address); break;
+ case 4: result = tspace->read_dword_unaligned(address); break;
+ case 8: result = tspace->read_qword_unaligned(address); break;
+ }
+ return result;
}
//-------------------------------------------------
-// add - add a new u64 pointer symbol
+// write_memory - write 1,2,4 or 8 bytes to the
+// specified memory space
//-------------------------------------------------
-void symbol_table::add(const char *name, read_write rw, u64 *ptr)
+void symbol_table::write_memory(address_space &space, offs_t address, u64 data, int size, bool apply_translation)
{
- m_symlist.erase(name);
- m_symlist.emplace(name, std::make_unique<integer_symbol_entry>(*this, name, rw, ptr));
+ address_space *tspace = &space;
+
+ if (apply_translation)
+ {
+ // mask against the logical byte mask
+ address &= space.logaddrmask();
+
+ // translate if necessary; if not mapped, we're done
+ if (!space.device().memory().translate(space.spacenum(), device_memory_interface::TR_WRITE, address, tspace))
+ return;
+ }
+
+ // otherwise, call the writing function for the translated address
+ switch (size)
+ {
+ case 1: tspace->write_byte(address, data); break;
+ case 2: tspace->write_word_unaligned(address, data); break;
+ case 4: tspace->write_dword_unaligned(address, data); break;
+ case 8: tspace->write_qword_unaligned(address, data); break;
+ }
+
+ notify_memory_modified();
}
//-------------------------------------------------
-// add - add a new value symbol
+// expression_get_space - return a space
+// based on a case insensitive tag search
//-------------------------------------------------
-void symbol_table::add(const char *name, u64 value)
+expression_error symbol_table::expression_get_space(const char *tag, int &spacenum, device_memory_interface *&memory)
{
- m_symlist.erase(name);
- m_symlist.emplace(name, std::make_unique<integer_symbol_entry>(*this, name, value));
+ device_t *device = nullptr;
+ std::string spacename;
+ if (tag)
+ {
+ // convert to lowercase then lookup the name (tags are enforced to be all lower case)
+ auto base = get_device_search(m_machine, m_memintf, tag);
+ device = base.first.subdevice(strmakelower(base.second));
+
+ // if that failed, treat the last component as an address space
+ if (!device)
+ {
+ std::string_view t = base.second;
+ auto const delimiter = t.find_last_of(":^");
+ bool const found = std::string_view::npos != delimiter;
+ if (!found || (':' == t[delimiter]))
+ {
+ spacename = strmakelower(t.substr(found ? (delimiter + 1) : 0));
+ t = t.substr(0, !found ? 0 : !delimiter ? 1 : delimiter);
+ if (!t.empty())
+ device = base.first.subdevice(strmakelower(t));
+ else
+ device = m_memintf ? &m_memintf->device() : &m_machine.root_device();
+ }
+ }
+ }
+ else if (m_memintf)
+ {
+ device = &m_memintf->device();
+ }
+
+ // if still no device, report error
+ if (!device)
+ {
+ memory = nullptr;
+ return expression_error::INVALID_MEMORY_NAME;
+ }
+
+ // ensure device has memory interface, and check for space if search not required
+ if (!device->interface(memory) || (spacename.empty() && (0 <= spacenum) && !memory->has_space(spacenum)))
+ {
+ memory = nullptr;
+ return expression_error::NO_SUCH_MEMORY_SPACE;
+ }
+
+ // search not required
+ if (spacename.empty() && (0 <= spacenum))
+ return expression_error::NONE;
+
+ // find space by name or take first populated space if required
+ for (int i = 0; memory->max_space_count() > i; ++i)
+ {
+ if (memory->has_space(i) && (spacename.empty() || (memory->space(i).name() == spacename)))
+ {
+ spacenum = i;
+ return expression_error::NONE;
+ }
+ }
+
+ // space not found
+ memory = nullptr;
+ return expression_error::NO_SUCH_MEMORY_SPACE;
}
//-------------------------------------------------
-// add - add a new register symbol
+// notify_memory_modified - notify that memory
+// has been changed
//-------------------------------------------------
-void symbol_table::add(const char *name, getter_func getter, setter_func setter, const std::string &format_string)
+void symbol_table::notify_memory_modified()
{
- m_symlist.erase(name);
- m_symlist.emplace(name, std::make_unique<integer_symbol_entry>(*this, name, getter, setter, format_string));
+ // walk up the table hierarchy to find the owner
+ for (symbol_table *symtable = this; symtable != nullptr; symtable = symtable->m_parent)
+ if (symtable->m_memory_modified)
+ symtable->m_memory_modified();
}
//-------------------------------------------------
-// add - add a new function symbol
+// memory_value - read 1,2,4 or 8 bytes at the
+// given offset in the given address space
//-------------------------------------------------
-void symbol_table::add(const char *name, int minparams, int maxparams, execute_func execute)
+u64 symbol_table::memory_value(const char *name, expression_space spacenum, u32 address, int size, bool disable_se)
{
- m_symlist.erase(name);
- m_symlist.emplace(name, std::make_unique<function_symbol_entry>(*this, name, minparams, maxparams, execute));
+ device_memory_interface *memory = m_memintf;
+
+ bool logical = true;
+ int space = -1;
+ switch (spacenum)
+ {
+ case EXPSPACE_PROGRAM_PHYSICAL:
+ case EXPSPACE_DATA_PHYSICAL:
+ case EXPSPACE_IO_PHYSICAL:
+ case EXPSPACE_OPCODE_PHYSICAL:
+ spacenum = expression_space(spacenum - (EXPSPACE_PROGRAM_PHYSICAL - EXPSPACE_PROGRAM_LOGICAL));
+ logical = false;
+ [[fallthrough]];
+ case EXPSPACE_PROGRAM_LOGICAL:
+ case EXPSPACE_DATA_LOGICAL:
+ case EXPSPACE_IO_LOGICAL:
+ case EXPSPACE_OPCODE_LOGICAL:
+ space = AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_LOGICAL);
+ expression_get_space(name, space, memory);
+ if (memory)
+ {
+ auto dis = m_machine.disable_side_effects(disable_se);
+ return read_memory(memory->space(space), address, size, logical);
+ }
+ break;
+
+ case EXPSPACE_PRGDIRECT:
+ case EXPSPACE_OPDIRECT:
+ space = (spacenum == EXPSPACE_OPDIRECT) ? AS_OPCODES : AS_PROGRAM;
+ expression_get_space(name, space, memory);
+ if (memory)
+ {
+ auto dis = m_machine.disable_side_effects(disable_se);
+ return read_program_direct(memory->space(space), (spacenum == EXPSPACE_OPDIRECT) ? 1 : 0, address, size);
+ }
+ break;
+
+ case EXPSPACE_REGION:
+ if (name)
+ return read_memory_region(name, address, size);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
}
//-------------------------------------------------
-// find_deep - do a deep search for a symbol,
-// looking in the parent if needed
+// read_program_direct - read memory directly
+// from an opcode or RAM pointer
//-------------------------------------------------
-symbol_entry *symbol_table::find_deep(const char *symbol)
+u64 symbol_table::read_program_direct(address_space &space, int opcode, offs_t address, int size)
{
- // walk up the table hierarchy to find the owner
- for (symbol_table *symtable = this; symtable != nullptr; symtable = symtable->m_parent)
+ 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)
{
- symbol_entry *entry = symtable->find(symbol);
- if (entry != nullptr)
- return entry;
+ int halfsize = size / 2;
+
+ // read each half, from lower address to upper address
+ u64 r0 = read_program_direct(space, opcode | 2, address + 0, halfsize);
+ u64 r1 = 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));
}
- return nullptr;
+
+ // 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;
}
//-------------------------------------------------
-// value - return the value of a symbol
+// read_memory_region - read memory from a
+// memory region
//-------------------------------------------------
-u64 symbol_table::value(const char *symbol)
+u64 symbol_table::read_memory_region(const char *rgntag, offs_t address, int size)
{
- symbol_entry *entry = find_deep(symbol);
- return (entry != nullptr) ? entry->value() : 0;
+ auto search = get_device_search(m_machine, m_memintf, rgntag);
+ memory_region *const region = search.first.memregion(search.second);
+ u64 result = ~u64(0) >> (64 - 8*size);
+
+ // make sure we get a valid base before proceeding
+ if (region)
+ {
+ // 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 = read_memory_region(rgntag, address + 0, halfsize);
+ r1 = 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;
}
//-------------------------------------------------
-// set_value - set the value of a symbol
+// set_memory_value - write 1,2,4 or 8 bytes at
+// the given offset in the given address space
//-------------------------------------------------
-void symbol_table::set_value(const char *symbol, u64 value)
+void symbol_table::set_memory_value(const char *name, expression_space spacenum, u32 address, int size, u64 data, bool disable_se)
{
- symbol_entry *entry = find_deep(symbol);
- if (entry != nullptr)
- entry->set_value(value);
+ device_memory_interface *memory = m_memintf;
+
+ bool logical = true;
+ int space = -1;
+ switch (spacenum)
+ {
+ case EXPSPACE_PROGRAM_PHYSICAL:
+ case EXPSPACE_DATA_PHYSICAL:
+ case EXPSPACE_IO_PHYSICAL:
+ case EXPSPACE_OPCODE_PHYSICAL:
+ spacenum = expression_space(spacenum - (EXPSPACE_PROGRAM_PHYSICAL - EXPSPACE_PROGRAM_LOGICAL));
+ logical = false;
+ [[fallthrough]];
+ case EXPSPACE_PROGRAM_LOGICAL:
+ case EXPSPACE_DATA_LOGICAL:
+ case EXPSPACE_IO_LOGICAL:
+ case EXPSPACE_OPCODE_LOGICAL:
+ space = AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_LOGICAL);
+ expression_get_space(name, space, memory);
+ if (memory)
+ {
+ auto dis = m_machine.disable_side_effects(disable_se);
+ write_memory(memory->space(space), address, data, size, logical);
+ }
+ break;
+
+ case EXPSPACE_PRGDIRECT:
+ case EXPSPACE_OPDIRECT:
+ space = (spacenum == EXPSPACE_OPDIRECT) ? AS_OPCODES : AS_PROGRAM;
+ expression_get_space(name, space, memory);
+ if (memory)
+ {
+ auto dis = m_machine.disable_side_effects(disable_se);
+ write_program_direct(memory->space(space), (spacenum == EXPSPACE_OPDIRECT) ? 1 : 0, address, size, data);
+ }
+ break;
+
+ case EXPSPACE_REGION:
+ if (name)
+ write_memory_region(name, address, size, data);
+ break;
+
+ default:
+ break;
+ }
}
//-------------------------------------------------
-// memory_valid - return true if the given
-// memory name/space/offset combination is valid
+// write_program_direct - write memory directly
+// to an opcode or RAM pointer
//-------------------------------------------------
-expression_error::error_code symbol_table::memory_valid(const char *name, expression_space space)
+void symbol_table::write_program_direct(address_space &space, int opcode, offs_t address, int size, u64 data)
{
- // walk up the table hierarchy to find the owner
- for (symbol_table *symtable = this; symtable != nullptr; symtable = symtable->m_parent)
- if (symtable->m_memory_valid != nullptr)
+ // 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)
{
- expression_error::error_code err = symtable->m_memory_valid(symtable->m_memory_param, name, space);
- if (err != expression_error::NO_SUCH_MEMORY_SPACE)
- return err;
+ r0 = data & halfmask;
+ r1 = (data >> (8 * halfsize)) & halfmask;
}
- return expression_error::NO_SUCH_MEMORY_SPACE;
+ else
+ {
+ r0 = (data >> (8 * halfsize)) & halfmask;
+ r1 = data & halfmask;
+ }
+
+ // write each half, from lower address to upper address
+ write_program_direct(space, opcode | 2, address + 0, halfsize, r0);
+ 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;
+ notify_memory_modified();
+ }
+ }
}
//-------------------------------------------------
-// memory_value - return a value read from memory
+// write_memory_region - write memory to a
+// memory region
//-------------------------------------------------
-u64 symbol_table::memory_value(const char *name, expression_space space, u32 offset, int size, bool disable_se)
+void symbol_table::write_memory_region(const char *rgntag, offs_t address, int size, u64 data)
{
- // walk up the table hierarchy to find the owner
- for (symbol_table *symtable = this; symtable != nullptr; symtable = symtable->m_parent)
- if (symtable->m_memory_valid != nullptr)
+ auto search = get_device_search(m_machine, m_memintf, rgntag);
+ memory_region *const region = search.first.memregion(search.second);
+
+ // make sure we get a valid base before proceeding
+ if (region)
+ {
+ // call ourself recursively until we are byte-sized
+ if (size > 1)
{
- expression_error::error_code err = symtable->m_memory_valid(symtable->m_memory_param, name, space);
- if (err != expression_error::NO_SUCH_MEMORY_SPACE && symtable->m_memory_read != nullptr)
- return symtable->m_memory_read(symtable->m_memory_param, name, space, offset, size, disable_se);
- return 0;
+ 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
+ write_memory_region(rgntag, address + 0, halfsize, r0);
+ write_memory_region(rgntag, address + halfsize, halfsize, r1);
}
- return 0;
+
+ // 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;
+ }
+ notify_memory_modified();
+ }
+ }
}
//-------------------------------------------------
-// set_memory_value - write a value to memory
+// memory_valid - return true if the given
+// memory name/space/offset combination is valid
//-------------------------------------------------
-void symbol_table::set_memory_value(const char *name, expression_space space, u32 offset, int size, u64 value, bool disable_se)
+expression_error::error_code symbol_table::memory_valid(const char *name, expression_space space)
{
- // walk up the table hierarchy to find the owner
- for (symbol_table *symtable = this; symtable != nullptr; symtable = symtable->m_parent)
- if (symtable->m_memory_valid != nullptr)
+ device_memory_interface *memory = m_memintf;
+
+ int spaceno = -1;
+ switch (space)
+ {
+ case EXPSPACE_PROGRAM_LOGICAL:
+ case EXPSPACE_DATA_LOGICAL:
+ case EXPSPACE_IO_LOGICAL:
+ case EXPSPACE_OPCODE_LOGICAL:
+ spaceno = AS_PROGRAM + (space - EXPSPACE_PROGRAM_LOGICAL);
+ return expression_get_space(name, spaceno, memory);
+
+ case EXPSPACE_PROGRAM_PHYSICAL:
+ case EXPSPACE_DATA_PHYSICAL:
+ case EXPSPACE_IO_PHYSICAL:
+ case EXPSPACE_OPCODE_PHYSICAL:
+ spaceno = AS_PROGRAM + (space - EXPSPACE_PROGRAM_PHYSICAL);
+ return expression_get_space(name, spaceno, memory);
+
+ case EXPSPACE_PRGDIRECT:
+ case EXPSPACE_OPDIRECT:
+ spaceno = (space == EXPSPACE_OPDIRECT) ? AS_OPCODES : AS_PROGRAM;
+ return expression_get_space(name, spaceno, memory);
+
+ case EXPSPACE_REGION:
+ if (!name)
{
- expression_error::error_code err = symtable->m_memory_valid(symtable->m_memory_param, name, space);
- if (err != expression_error::NO_SUCH_MEMORY_SPACE && symtable->m_memory_write != nullptr)
- symtable->m_memory_write(symtable->m_memory_param, name, space, offset, size, value, disable_se);
- return;
+ return expression_error::MISSING_MEMORY_NAME;
}
+ else
+ {
+ auto search = get_device_search(m_machine, m_memintf, name);
+ memory_region *const region = search.first.memregion(search.second);
+ if (!region || !region->base())
+ return expression_error::INVALID_MEMORY_NAME;
+ }
+ break;
+
+ default:
+ return expression_error::NO_SUCH_MEMORY_SPACE;
+ }
+
+ return expression_error::NONE;
}
@@ -538,16 +1014,33 @@ void symbol_table::set_memory_value(const char *name, expression_space space, u3
// parsed_expression - constructor
//-------------------------------------------------
-parsed_expression::parsed_expression(symbol_table *symtable, const char *expression, u64 *result)
+parsed_expression::parsed_expression(symbol_table &symtable)
: m_symtable(symtable)
+ , m_default_base(16)
{
- // if we got an expression parse it
- if (expression != nullptr)
- parse(expression);
+}
+
+parsed_expression::parsed_expression(symbol_table &symtable, std::string_view expression, int default_base)
+ : m_symtable(symtable)
+ , m_default_base(default_base)
+{
+ assert(default_base == 8 || default_base == 10 || default_base == 16);
+
+ parse(expression);
+}
+
- // if we get a result pointer, execute it
- if (result != nullptr)
- *result = execute();
+//-------------------------------------------------
+// parsed_expression - copy constructor
+//-------------------------------------------------
+
+parsed_expression::parsed_expression(const parsed_expression &src)
+ : m_symtable(src.m_symtable)
+ , m_default_base(src.m_default_base)
+ , m_original_string(src.m_original_string)
+{
+ if (!m_original_string.empty())
+ parse_string_into_tokens();
}
@@ -555,12 +1048,12 @@ parsed_expression::parsed_expression(symbol_table *symtable, const char *express
// parse - parse an expression into tokens
//-------------------------------------------------
-void parsed_expression::parse(const char *expression)
+void parsed_expression::parse(std::string_view expression)
{
// copy the string and reset our parsing state
m_original_string.assign(expression);
- m_tokenlist.reset();
- m_stringlist.reset();
+ m_tokenlist.clear();
+ m_stringlist.clear();
// first parse the tokens into the token array in order
parse_string_into_tokens();
@@ -577,6 +1070,7 @@ void parsed_expression::parse(const char *expression)
void parsed_expression::copy(const parsed_expression &src)
{
m_symtable = src.m_symtable;
+ m_default_base = src.m_default_base;
m_original_string.assign(src.m_original_string);
if (!m_original_string.empty())
parse_string_into_tokens();
@@ -588,89 +1082,72 @@ void parsed_expression::copy(const parsed_expression &src)
// human readable token representation
//-------------------------------------------------
-void parsed_expression::print_tokens(FILE *out)
+void parsed_expression::print_tokens()
{
-#if DEBUG_TOKENS
- osd_printf_debug("----\n");
- for (parse_token *token = m_tokens.first(); token != nullptr; token = token->next())
+ LOG("----\n");
+ for (parse_token &token : m_tokenlist)
{
- switch (token->type)
+ if (token.is_number())
+ LOG("NUMBER: %016X\n", token.value());
+ else if (token.is_string())
+ LOG("STRING: ""%s""\n", token.string());
+ else if (token.is_symbol())
+ LOG("SYMBOL: %s%s%s\n", token.symbol().name(), token.symbol().is_function() ? "()" : "", token.symbol().is_lval() ? " &" : "");
+ else if (token.is_operator())
{
- default:
- case parse_token::INVALID:
- fprintf(out, "INVALID\n");
- break;
-
- case parse_token::END:
- fprintf(out, "END\n");
- break;
-
- case parse_token::NUMBER:
- fprintf(out, "NUMBER: %08X%08X\n", (u32)(token->value.i >> 32), u32(token->value.i));
- break;
-
- case parse_token::STRING:
- fprintf(out, "STRING: ""%s""\n", token->string);
- break;
-
- case parse_token::SYMBOL:
- fprintf(out, "SYMBOL: %08X%08X\n", u32(token->value.i >> 32), u32(token->value.i));
- break;
-
- case parse_token::OPERATOR:
- switch (token->value.i)
- {
- case TVL_LPAREN: fprintf(out, "(\n"); break;
- case TVL_RPAREN: fprintf(out, ")\n"); break;
- case TVL_PLUSPLUS: fprintf(out, "++ (unspecified)\n"); break;
- case TVL_MINUSMINUS: fprintf(out, "-- (unspecified)\n"); break;
- case TVL_PREINCREMENT: fprintf(out, "++ (prefix)\n"); break;
- case TVL_PREDECREMENT: fprintf(out, "-- (prefix)\n"); break;
- case TVL_POSTINCREMENT: fprintf(out, "++ (postfix)\n"); break;
- case TVL_POSTDECREMENT: fprintf(out, "-- (postfix)\n"); break;
- case TVL_COMPLEMENT: fprintf(out, "!\n"); break;
- case TVL_NOT: fprintf(out, "~\n"); break;
- case TVL_UPLUS: fprintf(out, "+ (unary)\n"); break;
- case TVL_UMINUS: fprintf(out, "- (unary)\n"); break;
- case TVL_MULTIPLY: fprintf(out, "*\n"); break;
- case TVL_DIVIDE: fprintf(out, "/\n"); break;
- case TVL_MODULO: fprintf(out, "%%\n"); break;
- case TVL_ADD: fprintf(out, "+\n"); break;
- case TVL_SUBTRACT: fprintf(out, "-\n"); break;
- case TVL_LSHIFT: fprintf(out, "<<\n"); break;
- case TVL_RSHIFT: fprintf(out, ">>\n"); break;
- case TVL_LESS: fprintf(out, "<\n"); break;
- case TVL_LESSOREQUAL: fprintf(out, "<=\n"); break;
- case TVL_GREATER: fprintf(out, ">\n"); break;
- case TVL_GREATEROREQUAL:fprintf(out, ">=\n"); break;
- case TVL_EQUAL: fprintf(out, "==\n"); break;
- case TVL_NOTEQUAL: fprintf(out, "!=\n"); break;
- case TVL_BAND: fprintf(out, "&\n"); break;
- case TVL_BXOR: fprintf(out, "^\n"); break;
- case TVL_BOR: fprintf(out, "|\n"); break;
- case TVL_LAND: fprintf(out, "&&\n"); break;
- case TVL_LOR: fprintf(out, "||\n"); break;
- case TVL_ASSIGN: fprintf(out, "=\n"); break;
- case TVL_ASSIGNMULTIPLY:fprintf(out, "*=\n"); break;
- case TVL_ASSIGNDIVIDE: fprintf(out, "/=\n"); break;
- case TVL_ASSIGNMODULO: fprintf(out, "%%=\n"); break;
- case TVL_ASSIGNADD: fprintf(out, "+=\n"); break;
- case TVL_ASSIGNSUBTRACT:fprintf(out, "-=\n"); break;
- case TVL_ASSIGNLSHIFT: fprintf(out, "<<=\n"); break;
- case TVL_ASSIGNRSHIFT: fprintf(out, ">>=\n"); break;
- case TVL_ASSIGNBAND: fprintf(out, "&=\n"); break;
- case TVL_ASSIGNBXOR: fprintf(out, "^=\n"); break;
- case TVL_ASSIGNBOR: fprintf(out, "|=\n"); break;
- case TVL_COMMA: fprintf(out, ",\n"); break;
- case TVL_MEMORYAT: fprintf(out, token.memory_size_effect() ? "mem!\n" : "mem@\n");break;
- case TVL_EXECUTEFUNC: fprintf(out, "execute\n"); break;
- default: fprintf(out, "INVALID OPERATOR\n"); break;
- }
- break;
+ switch (token.optype())
+ {
+ case TVL_LPAREN: LOG("(\n"); break;
+ case TVL_RPAREN: LOG(")\n"); break;
+ case TVL_PLUSPLUS: LOG("++ (unspecified)\n"); break;
+ case TVL_MINUSMINUS: LOG("-- (unspecified)\n"); break;
+ case TVL_PREINCREMENT: LOG("++ (prefix)\n"); break;
+ case TVL_PREDECREMENT: LOG("-- (prefix)\n"); break;
+ case TVL_POSTINCREMENT: LOG("++ (postfix)\n"); break;
+ case TVL_POSTDECREMENT: LOG("-- (postfix)\n"); break;
+ case TVL_COMPLEMENT: LOG("!\n"); break;
+ case TVL_NOT: LOG("~\n"); break;
+ case TVL_UPLUS: LOG("+ (unary)\n"); break;
+ case TVL_UMINUS: LOG("- (unary)\n"); break;
+ case TVL_MULTIPLY: LOG("*\n"); break;
+ case TVL_DIVIDE: LOG("/\n"); break;
+ case TVL_MODULO: LOG("%%\n"); break;
+ case TVL_ADD: LOG("+\n"); break;
+ case TVL_SUBTRACT: LOG("-\n"); break;
+ case TVL_LSHIFT: LOG("<<\n"); break;
+ case TVL_RSHIFT: LOG(">>\n"); break;
+ case TVL_LESS: LOG("<\n"); break;
+ case TVL_LESSOREQUAL: LOG("<=\n"); break;
+ case TVL_GREATER: LOG(">\n"); break;
+ case TVL_GREATEROREQUAL:LOG(">=\n"); break;
+ case TVL_EQUAL: LOG("==\n"); break;
+ case TVL_NOTEQUAL: LOG("!=\n"); break;
+ case TVL_BAND: LOG("&\n"); break;
+ case TVL_BXOR: LOG("^\n"); break;
+ case TVL_BOR: LOG("|\n"); break;
+ case TVL_LAND: LOG("&&\n"); break;
+ case TVL_LOR: LOG("||\n"); break;
+ case TVL_ASSIGN: LOG("=\n"); break;
+ case TVL_ASSIGNMULTIPLY:LOG("*=\n"); break;
+ case TVL_ASSIGNDIVIDE: LOG("/=\n"); break;
+ case TVL_ASSIGNMODULO: LOG("%%=\n"); break;
+ case TVL_ASSIGNADD: LOG("+=\n"); break;
+ case TVL_ASSIGNSUBTRACT:LOG("-=\n"); break;
+ case TVL_ASSIGNLSHIFT: LOG("<<=\n"); break;
+ case TVL_ASSIGNRSHIFT: LOG(">>=\n"); break;
+ case TVL_ASSIGNBAND: LOG("&=\n"); break;
+ case TVL_ASSIGNBXOR: LOG("^=\n"); break;
+ case TVL_ASSIGNBOR: LOG("|=\n"); break;
+ case TVL_COMMA: LOG(",\n"); break;
+ case TVL_MEMORYAT: LOG(token.memory_side_effects() ? "mem!\n" : "mem@\n");break;
+ case TVL_EXECUTEFUNC: LOG("execute\n"); break;
+ default: LOG("INVALID OPERATOR\n"); break;
+ }
}
+ else
+ LOG("INVALID\n");
}
- osd_printf_debug("----\n");
-#endif
+ LOG("----\n");
}
@@ -693,7 +1170,8 @@ void parsed_expression::parse_string_into_tokens()
break;
// initialize the current token object
- parse_token &token = m_tokenlist.append(*global_alloc(parse_token(string - stringstart)));
+ m_tokenlist.emplace_back(string - stringstart);
+ parse_token &token = m_tokenlist.back();
// switch off the first character
switch (tolower(u8(string[0])))
@@ -854,12 +1332,15 @@ void parsed_expression::parse_symbol_or_number(parse_token &token, const char *&
// check for memory @ and ! operators
if (string[0] == '@' || string[0] == '!')
{
- try {
+ try
+ {
bool disable_se = string[0] == '@';
parse_memory_operator(token, buffer.c_str(), disable_se);
string += 1;
return;
- } catch(const expression_error &) {
+ }
+ catch (const expression_error &)
+ {
// Try some other operator instead
}
}
@@ -943,21 +1424,20 @@ void parsed_expression::parse_symbol_or_number(parse_token &token, const char *&
catch (expression_error const &err)
{
// this is really a hack, but 0B1234 could also hex depending on default base
- if (expression_error::INVALID_NUMBER == err)
- return parse_number(token, buffer.c_str(), DEFAULT_BASE, expression_error::INVALID_NUMBER);
+ if (expression_error::INVALID_NUMBER == err && m_default_base == 16)
+ return parse_number(token, buffer.c_str(), m_default_base, expression_error::INVALID_NUMBER);
else
throw;
}
- // TODO: for octal address spaces, treat 0123 as octal
default:
; // fall through
}
- // fall through
+ [[fallthrough]];
default:
// check for a symbol match
- symbol_entry *symbol = m_symtable->find_deep(buffer.c_str());
+ symbol_entry *symbol = m_symtable.get().find_deep(buffer.c_str());
if (symbol != nullptr)
{
token.configure_symbol(*symbol);
@@ -965,14 +1445,15 @@ void parsed_expression::parse_symbol_or_number(parse_token &token, const char *&
// if this is a function symbol, synthesize an execute function operator
if (symbol->is_function())
{
- parse_token &newtoken = m_tokenlist.append(*global_alloc(parse_token(string - stringstart)));
+ m_tokenlist.emplace_back(string - stringstart);
+ parse_token &newtoken = m_tokenlist.back();
newtoken.configure_operator(TVL_EXECUTEFUNC, 0);
}
return;
}
// attempt to parse as a number in the default base
- parse_number(token, buffer.c_str(), DEFAULT_BASE, expression_error::UNKNOWN_SYMBOL);
+ parse_number(token, buffer.c_str(), m_default_base, expression_error::UNKNOWN_SYMBOL);
}
}
@@ -1072,7 +1553,7 @@ void parsed_expression::parse_quoted_string(parse_token &token, const char *&str
string++;
// make the token
- token.configure_string(m_stringlist.append(*global_alloc(expression_string(buffer.c_str()))));
+ token.configure_string(m_stringlist.emplace(m_stringlist.end(), buffer.c_str())->c_str());
}
@@ -1089,17 +1570,17 @@ void parsed_expression::parse_memory_operator(parse_token &token, const char *st
const char *dot = strrchr(string, '.');
if (dot != nullptr)
{
- namestring = m_stringlist.append(*global_alloc(expression_string(string, dot - string)));
+ namestring = m_stringlist.emplace(m_stringlist.end(), string, dot)->c_str();
string = dot + 1;
}
- // length 3 means logical/physical, then space, then size
int length = (int)strlen(string);
bool physical = false;
int space = 'p';
int size;
if (length == 3)
{
+ // length 3 means logical/physical, then space, then size
if (string[0] != 'l' && string[0] != 'p')
throw expression_error(expression_error::INVALID_MEMORY_SPACE, token.offset() + (string - startstring));
if (string[1] != 'p' && string[1] != 'd' && string[1] != 'i' && string[1] != '3')
@@ -1108,21 +1589,22 @@ void parsed_expression::parse_memory_operator(parse_token &token, const char *st
space = string[1];
size = string[2];
}
-
- // length 2 means space then size
else if (length == 2)
{
+ // length 2 means space then size
space = string[0];
size = string[1];
}
-
- // length 1 means size
else if (length == 1)
+ {
+ // length 1 means size
size = string[0];
-
- // anything else is invalid
+ }
else
+ {
+ // anything else is invalid
throw expression_error(expression_error::INVALID_TOKEN, token.offset());
+ }
// convert the space to flags
expression_space memspace;
@@ -1131,9 +1613,9 @@ void parsed_expression::parse_memory_operator(parse_token &token, const char *st
case 'p': memspace = physical ? EXPSPACE_PROGRAM_PHYSICAL : EXPSPACE_PROGRAM_LOGICAL; break;
case 'd': memspace = physical ? EXPSPACE_DATA_PHYSICAL : EXPSPACE_DATA_LOGICAL; break;
case 'i': memspace = physical ? EXPSPACE_IO_PHYSICAL : EXPSPACE_IO_LOGICAL; break;
- case '3': memspace = physical ? EXPSPACE_SPACE3_PHYSICAL : EXPSPACE_SPACE3_LOGICAL; break;
- case 'o': memspace = EXPSPACE_OPCODE; break;
- case 'r': memspace = EXPSPACE_RAMWRITE; break;
+ case '3': memspace = physical ? EXPSPACE_OPCODE_PHYSICAL : EXPSPACE_OPCODE_LOGICAL; break;
+ case 'r': memspace = EXPSPACE_PRGDIRECT; break;
+ case 'o': memspace = EXPSPACE_OPDIRECT; break;
case 'm': memspace = EXPSPACE_REGION; break;
default: throw expression_error(expression_error::INVALID_MEMORY_SPACE, token.offset() + (string - startstring));
}
@@ -1150,12 +1632,9 @@ void parsed_expression::parse_memory_operator(parse_token &token, const char *st
}
// validate the name
- if (m_symtable != nullptr)
- {
- expression_error::error_code err = m_symtable->memory_valid(namestring, memspace);
- if (err != expression_error::NONE)
- throw expression_error(err, token.offset() + (string - startstring));
- }
+ expression_error::error_code err = m_symtable.get().memory_valid(namestring, memspace);
+ if (err != expression_error::NONE)
+ throw expression_error(err, token.offset() + (string - startstring));
// configure the token
token.configure_operator(TVL_MEMORYAT, 2).set_memory_size(memsize).set_memory_space(memspace).set_memory_source(namestring).set_memory_side_effects(disable_se);
@@ -1167,9 +1646,8 @@ void parsed_expression::parse_memory_operator(parse_token &token, const char *st
// ambiguities based on neighboring tokens
//-------------------------------------------------
-void parsed_expression::normalize_operator(parse_token *prevtoken, parse_token &thistoken)
+void parsed_expression::normalize_operator(parse_token &thistoken, parse_token *prevtoken, parse_token *nexttoken, const std::list<parse_token> &stack, bool was_rparen)
{
- parse_token *nexttoken = thistoken.next();
switch (thistoken.optype())
{
// Determine if an open paren is part of a function or not
@@ -1203,15 +1681,15 @@ void parsed_expression::normalize_operator(parse_token *prevtoken, parse_token &
case TVL_SUBTRACT:
// Assume we're unary if we are the first token, or if the previous token is not
// a symbol, a number, or a right parenthesis
- if (prevtoken == nullptr || (!prevtoken->is_symbol() && !prevtoken->is_number() && !prevtoken->is_operator(TVL_RPAREN)))
+ if (prevtoken == nullptr || (!prevtoken->is_symbol() && !prevtoken->is_number() && !was_rparen))
thistoken.configure_operator(thistoken.is_operator(TVL_ADD) ? TVL_UPLUS : TVL_UMINUS, 2);
break;
// Determine if , refers to a function parameter
case TVL_COMMA:
- for (auto lookback = m_token_stack.rbegin(); lookback != m_token_stack.rend(); ++lookback)
+ for (auto lookback = stack.begin(); lookback != stack.end(); ++lookback)
{
- parse_token &peek = *lookback;
+ const parse_token &peek = *lookback;
// if we hit an execute function operator, or else a left parenthesis that is
// already tagged, then tag us as well
@@ -1233,51 +1711,63 @@ void parsed_expression::normalize_operator(parse_token *prevtoken, parse_token &
void parsed_expression::infix_to_postfix()
{
- simple_list<parse_token> stack;
+ std::list<parse_token> stack;
+ parse_token *prev = nullptr;
+
+ // this flag is used to avoid looking back at a closing parenthesis that was already destroyed
+ bool was_rparen = false;
// loop over all the original tokens
- parse_token *prev = nullptr;
- parse_token *next;
- for (parse_token *token = m_tokenlist.detach_all(); token != nullptr; prev = token, token = next)
+ std::list<parse_token>::iterator next;
+ std::list<parse_token> origlist = std::move(m_tokenlist);
+ m_tokenlist.clear();
+ for (std::list<parse_token>::iterator token = origlist.begin(); token != origlist.end(); token = next)
{
// pre-determine our next token
- next = token->next();
+ next = std::next(token);
// if the character is an operand, append it to the result string
if (token->is_number() || token->is_symbol() || token->is_string())
- m_tokenlist.append(*token);
+ {
+ m_tokenlist.splice(m_tokenlist.end(), origlist, token);
+
+ // remember this as the previous token
+ prev = &*token;
+ was_rparen = false;
+ }
// if this is an operator, process it
else if (token->is_operator())
{
// normalize the operator based on neighbors
- normalize_operator(prev, *token);
+ normalize_operator(*token, prev, next != origlist.end() ? &*next : nullptr, stack, was_rparen);
+ was_rparen = false;
// if the token is an opening parenthesis, push it onto the stack.
if (token->is_operator(TVL_LPAREN))
- stack.prepend(*token);
+ stack.splice(stack.begin(), origlist, token);
// if the token is a closing parenthesis, pop all operators until we
// reach an opening parenthesis and append them to the result string,
- // discaring the open parenthesis
+ // discarding the open parenthesis
else if (token->is_operator(TVL_RPAREN))
{
- // loop until we find our matching opener
- parse_token *popped;
- while ((popped = stack.detach_head()) != nullptr)
- {
- if (popped->is_operator(TVL_LPAREN))
- break;
- m_tokenlist.append(*popped);
- }
+ // find our matching opener
+ std::list<parse_token>::iterator lparen = std::find_if(stack.begin(), stack.end(),
+ [] (const parse_token &token) { return token.is_operator(TVL_LPAREN); }
+ );
// if we didn't find an open paren, it's an error
- if (popped == nullptr)
+ if (lparen == stack.end())
throw expression_error(expression_error::UNBALANCED_PARENS, token->offset());
+ // move the stacked operators to the end of the new list
+ m_tokenlist.splice(m_tokenlist.end(), stack, stack.begin(), lparen);
+
// free ourself and our matching opening parenthesis
- global_free(token);
- global_free(popped);
+ origlist.erase(token);
+ stack.erase(lparen);
+ was_rparen = true;
}
// if the token is an operator, pop operators until we reach an opening parenthesis,
@@ -1288,8 +1778,8 @@ void parsed_expression::infix_to_postfix()
int our_precedence = token->precedence();
// loop until we can't peek at the stack anymore
- parse_token *peek;
- while ((peek = stack.first()) != nullptr)
+ std::list<parse_token>::iterator peek;
+ for (peek = stack.begin(); peek != stack.end(); ++peek)
{
// break if any of the above conditions are true
if (peek->is_operator(TVL_LPAREN))
@@ -1297,28 +1787,29 @@ void parsed_expression::infix_to_postfix()
int stack_precedence = peek->precedence();
if (stack_precedence > our_precedence || (stack_precedence == our_precedence && peek->right_to_left()))
break;
-
- // pop this token
- m_tokenlist.append(*stack.detach_head());
}
+ // move the stacked operands to the end of the new list
+ m_tokenlist.splice(m_tokenlist.end(), stack, stack.begin(), peek);
+
// push the new operator
- stack.prepend(*token);
+ stack.splice(stack.begin(), origlist, token);
}
+
+ if (!was_rparen)
+ prev = &*token;
}
}
- // finish popping the stack
- parse_token *popped;
- while ((popped = stack.detach_head()) != nullptr)
- {
- // it is an error to have a left parenthesis still on the stack
- if (popped->is_operator(TVL_LPAREN))
- throw expression_error(expression_error::UNBALANCED_PARENS, popped->offset());
+ // it is an error to have a left parenthesis still on the stack
+ std::list<parse_token>::iterator lparen = std::find_if(stack.begin(), stack.end(),
+ [] (const parse_token &token) { return token.is_operator(TVL_LPAREN); }
+ );
+ if (lparen != stack.end())
+ throw expression_error(expression_error::UNBALANCED_PARENS, lparen->offset());
- // pop this token
- m_tokenlist.append(*popped);
- }
+ // pop all remaining tokens
+ m_tokenlist.splice(m_tokenlist.end(), stack, stack.begin(), stack.end());
}
@@ -1664,8 +2155,7 @@ u64 parsed_expression::execute_tokens()
//-------------------------------------------------
parsed_expression::parse_token::parse_token(int offset)
- : m_next(nullptr),
- m_type(INVALID),
+ : m_type(INVALID),
m_offset(offset),
m_value(0),
m_flags(0),
@@ -1680,16 +2170,15 @@ parsed_expression::parse_token::parse_token(int offset)
// for a SYMBOL token
//-------------------------------------------------
-u64 parsed_expression::parse_token::get_lval_value(symbol_table *table)
+u64 parsed_expression::parse_token::get_lval_value(symbol_table &table)
{
// get the value of a symbol
if (is_symbol())
return m_symbol->value();
// or get the value from the memory callbacks
- else if (is_memory() && table != nullptr) {
- return table->memory_value(m_string, memory_space(), address(), 1 << memory_size(), memory_side_effects());
- }
+ else if (is_memory())
+ return table.memory_value(m_string, memory_space(), address(), 1 << memory_size(), memory_side_effects());
return 0;
}
@@ -1700,15 +2189,15 @@ u64 parsed_expression::parse_token::get_lval_value(symbol_table *table)
// for a SYMBOL token
//-------------------------------------------------
-inline void parsed_expression::parse_token::set_lval_value(symbol_table *table, u64 value)
+inline void parsed_expression::parse_token::set_lval_value(symbol_table &table, u64 value)
{
// set the value of a symbol
if (is_symbol())
m_symbol->set_value(value);
// or set the value via the memory callbacks
- else if (is_memory() && table != nullptr)
- table->set_memory_value(m_string, memory_space(), address(), 1 << memory_size(), value, memory_side_effects());
+ else if (is_memory())
+ table.set_memory_value(m_string, memory_space(), address(), 1 << memory_size(), value, memory_side_effects());
}
@@ -1733,7 +2222,7 @@ void parsed_expression::execute_function(parse_token &token)
// if it is a function symbol, break out of the loop
if (peek.is_symbol())
{
- symbol = peek.symbol();
+ symbol = &peek.symbol();
if (symbol->is_function())
{
m_token_stack.pop_back();
@@ -1751,8 +2240,14 @@ void parsed_expression::execute_function(parse_token &token)
if (paramcount == MAX_FUNCTION_PARAMS)
throw expression_error(expression_error::INVALID_PARAM_COUNT, token.offset());
- // execute the function and push the result
+ // validate parameters
function_symbol_entry *function = downcast<function_symbol_entry *>(symbol);
+ if (paramcount < function->minparams())
+ throw expression_error(expression_error::TOO_FEW_PARAMS, token.offset(), function->minparams());
+ if (paramcount > function->maxparams())
+ throw expression_error(expression_error::TOO_MANY_PARAMS, token.offset(), function->maxparams());
+
+ // execute the function and push the result
parse_token result(token.offset());
result.configure_number(function->execute(paramcount, &funcparams[MAX_FUNCTION_PARAMS - paramcount]));
push_token(result);
diff --git a/src/emu/debug/express.h b/src/emu/debug/express.h
index ee5a500ad0e..251d8b4365c 100644
--- a/src/emu/debug/express.h
+++ b/src/emu/debug/express.h
@@ -17,6 +17,8 @@
#include <deque>
#include <functional>
+#include <list>
+#include <string_view>
#include <unordered_map>
@@ -32,13 +34,13 @@ enum expression_space
EXPSPACE_PROGRAM_LOGICAL,
EXPSPACE_DATA_LOGICAL,
EXPSPACE_IO_LOGICAL,
- EXPSPACE_SPACE3_LOGICAL,
+ EXPSPACE_OPCODE_LOGICAL,
EXPSPACE_PROGRAM_PHYSICAL,
EXPSPACE_DATA_PHYSICAL,
EXPSPACE_IO_PHYSICAL,
- EXPSPACE_SPACE3_PHYSICAL,
- EXPSPACE_OPCODE,
- EXPSPACE_RAMWRITE,
+ EXPSPACE_OPCODE_PHYSICAL,
+ EXPSPACE_PRGDIRECT,
+ EXPSPACE_OPDIRECT,
EXPSPACE_REGION
};
@@ -48,6 +50,8 @@ enum expression_space
// TYPE DEFINITIONS
//**************************************************************************
+using offs_t = u32;
+
// ======================> expression_error
// an expression_error holds an error code and a string offset
@@ -70,6 +74,8 @@ public:
DIVIDE_BY_ZERO,
OUT_OF_MEMORY,
INVALID_PARAM_COUNT,
+ TOO_FEW_PARAMS,
+ TOO_MANY_PARAMS,
UNBALANCED_QUOTES,
TOO_MANY_STRINGS,
INVALID_MEMORY_SIZE,
@@ -80,22 +86,26 @@ public:
};
// construction/destruction
- expression_error(error_code code, int offset = 0)
- : m_code(code),
- m_offset(offset) { }
+ constexpr expression_error(error_code code, int offset = 0, int num = 0)
+ : m_code(code)
+ , m_offset(offset)
+ , m_num(num)
+ {
+ }
// operators
- operator error_code() const { return m_code; }
+ constexpr operator error_code() const { return m_code; }
// getters
- error_code code() const { return m_code; }
- int offset() const { return m_offset; }
- const char *code_string() const;
+ constexpr error_code code() const { return m_code; }
+ constexpr int offset() const { return m_offset; }
+ std::string code_string() const;
private:
// internal state
error_code m_code;
int m_offset;
+ int m_num;
};
@@ -104,8 +114,6 @@ private:
// symbol_entry describes a symbol in a symbol table
class symbol_entry
{
- friend class simple_list<symbol_entry>;
-
protected:
// symbol types
enum symbol_type
@@ -120,7 +128,6 @@ public:
virtual ~symbol_entry();
// getters
- symbol_entry *next() const { return m_next; }
const char *name() const { return m_name.c_str(); }
const std::string &format() const { return m_format; }
@@ -134,7 +141,6 @@ public:
protected:
// internal state
- symbol_entry * m_next; // link to next entry
symbol_table & m_table; // pointer back to the owning table
symbol_type m_type; // type of symbol
std::string m_name; // name of the symbol
@@ -150,16 +156,14 @@ class symbol_table
{
public:
// callback functions for getting/setting a symbol value
- typedef std::function<u64(symbol_table &table)> getter_func;
- typedef std::function<void(symbol_table &table, u64 value)> setter_func;
+ typedef std::function<u64()> getter_func;
+ typedef std::function<void(u64 value)> setter_func;
// callback functions for function execution
- typedef std::function<u64(symbol_table &table, int numparams, const u64 *paramlist)> execute_func;
+ typedef std::function<u64(int numparams, const u64 *paramlist)> execute_func;
// callback functions for memory reads/writes
- typedef std::function<expression_error::error_code(void *cbparam, const char *name, expression_space space)> valid_func;
- typedef std::function<u64(void *cbparam, const char *name, expression_space space, u32 offset, int size, bool disable_se)> read_func;
- typedef std::function<void(void *cbparam, const char *name, expression_space space, u32 offset, int size, u64 value, bool disable_se)> write_func;
+ typedef std::function<void()> memory_modified_func;
enum read_write
{
@@ -168,21 +172,21 @@ public:
};
// construction/destruction
- symbol_table(void *globalref, symbol_table *parent = nullptr);
+ symbol_table(running_machine &machine, symbol_table *parent = nullptr, device_t *device = nullptr);
// getters
const std::unordered_map<std::string, std::unique_ptr<symbol_entry>> &entries() const { return m_symlist; }
symbol_table *parent() const { return m_parent; }
- void *globalref() const { return m_globalref; }
+ running_machine &machine() { return m_machine; }
// setters
- void configure_memory(void *param, valid_func valid, read_func read, write_func write);
+ void set_memory_modified_func(memory_modified_func modified);
// symbol access
- void add(const char *name, read_write rw, u64 *ptr = nullptr);
- void add(const char *name, u64 constvalue);
- void add(const char *name, getter_func getter, setter_func setter = nullptr, const std::string &format_string = "");
- void add(const char *name, int minparams, int maxparams, execute_func execute);
+ symbol_entry &add(const char *name, read_write rw, u64 *ptr = nullptr);
+ symbol_entry &add(const char *name, u64 constvalue);
+ symbol_entry &add(const char *name, getter_func getter, setter_func setter = nullptr, const std::string &format_string = "");
+ symbol_entry &add(const char *name, int minparams, int maxparams, execute_func execute);
symbol_entry *find(const char *name) const { if (name) { auto search = m_symlist.find(name); if (search != m_symlist.end()) return search->second.get(); else return nullptr; } else return nullptr; }
symbol_entry *find_deep(const char *name);
@@ -194,16 +198,24 @@ public:
expression_error::error_code memory_valid(const char *name, expression_space space);
u64 memory_value(const char *name, expression_space space, u32 offset, int size, bool disable_se);
void set_memory_value(const char *name, expression_space space, u32 offset, int size, u64 value, bool disable_se);
+ u64 read_memory(address_space &space, offs_t address, int size, bool apply_translation);
+ void write_memory(address_space &space, offs_t address, u64 data, int size, bool apply_translation);
private:
+ // memory helpers
+ u64 read_program_direct(address_space &space, int opcode, offs_t address, int size);
+ u64 read_memory_region(const char *rgntag, offs_t address, int size);
+ void write_program_direct(address_space &space, int opcode, offs_t address, int size, u64 data);
+ void write_memory_region(const char *rgntag, offs_t address, int size, u64 data);
+ expression_error expression_get_space(const char *tag, int &spacenum, device_memory_interface *&memory);
+ void notify_memory_modified();
+
// internal state
+ running_machine & m_machine; // reference to the machine
symbol_table * m_parent; // pointer to the parent symbol table
- void * m_globalref; // global reference parameter
std::unordered_map<std::string,std::unique_ptr<symbol_entry>> m_symlist; // list of symbols
- void * m_memory_param; // callback parameter for memory
- valid_func m_memory_valid; // validation callback
- read_func m_memory_read; // read callback
- write_func m_memory_write; // write callback
+ device_memory_interface *const m_memintf; // pointer to the local memory interface (if any)
+ memory_modified_func m_memory_modified; // memory modified callback
};
@@ -215,30 +227,32 @@ class parsed_expression
{
public:
// construction/destruction
- parsed_expression(const parsed_expression &src) { copy(src); }
- parsed_expression(symbol_table *symtable = nullptr, const char *expression = nullptr, u64 *result = nullptr);
+ parsed_expression(symbol_table &symtable);
+ parsed_expression(symbol_table &symtable, std::string_view expression, int default_base = 16);
+ parsed_expression(const parsed_expression &src);
+ parsed_expression(parsed_expression &&src) = default;
// operators
parsed_expression &operator=(const parsed_expression &src) { copy(src); return *this; }
+ parsed_expression &operator=(parsed_expression &&src) = default;
// getters
- bool is_empty() const { return (m_tokenlist.count() == 0); }
+ bool is_empty() const { return m_tokenlist.empty(); }
const char *original_string() const { return m_original_string.c_str(); }
- symbol_table *symbols() const { return m_symtable; }
+ symbol_table &symbols() const { return m_symtable.get(); }
// setters
- void set_symbols(symbol_table *symtable) { m_symtable = symtable; }
+ void set_symbols(symbol_table &symtable) { m_symtable = std::reference_wrapper<symbol_table>(symtable); }
+ void set_default_base(int base) { assert(base == 8 || base == 10 || base == 16); m_default_base = base; }
// execution
- void parse(const char *string);
+ void parse(std::string_view string);
u64 execute() { return execute_tokens(); }
private:
// a single token
class parse_token
{
- friend class simple_list<parse_token>;
-
// operator flags
enum
{
@@ -274,7 +288,6 @@ private:
parse_token(int offset = 0);
// getters
- parse_token *next() const { return m_next; }
int offset() const { return m_offset; }
bool is_number() const { return (m_type == NUMBER); }
bool is_string() const { return (m_type == STRING); }
@@ -285,8 +298,9 @@ private:
bool is_lval() const { return ((m_type == SYMBOL && m_symbol->is_lval()) || m_type == MEMORY); }
u64 value() const { assert(m_type == NUMBER); return m_value; }
+ const char *string() const { assert(m_type == STRING); return m_string; }
u32 address() const { assert(m_type == MEMORY); return m_value; }
- symbol_entry *symbol() const { assert(m_type == SYMBOL); return m_symbol; }
+ symbol_entry &symbol() const { assert(m_type == SYMBOL); return *m_symbol; }
u8 optype() const { assert(m_type == OPERATOR); return (m_flags & TIN_OPTYPE_MASK) >> TIN_OPTYPE_SHIFT; }
u8 precedence() const { assert(m_type == OPERATOR); return (m_flags & TIN_PRECEDENCE_MASK) >> TIN_PRECEDENCE_SHIFT; }
@@ -315,12 +329,11 @@ private:
parse_token &set_memory_source(const char *string) { assert(m_type == OPERATOR || m_type == MEMORY); m_string = string; return *this; }
// access
- u64 get_lval_value(symbol_table *symtable);
- void set_lval_value(symbol_table *symtable, u64 value);
+ u64 get_lval_value(symbol_table &symtable);
+ void set_lval_value(symbol_table &symtable, u64 value);
private:
// internal state
- parse_token * m_next; // next token in list
token_type m_type; // type of token
int m_offset; // offset within the string
u64 m_value; // integral value
@@ -329,30 +342,9 @@ private:
symbol_entry * m_symbol; // symbol pointer
};
- // an expression_string holds an indexed string parsed from the expression
- class expression_string
- {
- friend class simple_list<expression_string>;
-
- public:
- // construction/destruction
- expression_string(const char *string, int length = 0)
- : m_next(nullptr),
- m_string(string, (length == 0) ? strlen(string) : length) { }
-
- // operators
- operator const char *() { return m_string.c_str(); }
- operator const char *() const { return m_string.c_str(); }
-
- private:
- // internal state
- expression_string * m_next; // next string in list
- std::string m_string; // copy of the string
- };
-
// internal helpers
void copy(const parsed_expression &src);
- void print_tokens(FILE *out);
+ void print_tokens();
// parsing helpers
void parse_string_into_tokens();
@@ -361,7 +353,7 @@ private:
void parse_quoted_char(parse_token &token, const char *&string);
void parse_quoted_string(parse_token &token, const char *&string);
void parse_memory_operator(parse_token &token, const char *string, bool disable_se);
- void normalize_operator(parse_token *prevtoken, parse_token &thistoken);
+ void normalize_operator(parse_token &thistoken, parse_token *prevtoken, parse_token *nexttoken, const std::list<parse_token> &stack, bool was_rparen);
void infix_to_postfix();
// execution helpers
@@ -376,10 +368,11 @@ private:
static const int MAX_FUNCTION_PARAMS = 16;
// internal state
- symbol_table * m_symtable; // symbol table
+ std::reference_wrapper<symbol_table> m_symtable; // symbol table
+ int m_default_base; // default base
std::string m_original_string; // original string (prior to parsing)
- simple_list<parse_token> m_tokenlist; // token list
- simple_list<expression_string> m_stringlist; // string list
+ std::list<parse_token> m_tokenlist; // token list
+ std::list<std::string> m_stringlist; // string list
std::deque<parse_token> m_token_stack; // token stack (used during execution)
};
diff --git a/src/emu/debug/points.cpp b/src/emu/debug/points.cpp
new file mode 100644
index 00000000000..12e4b614843
--- /dev/null
+++ b/src/emu/debug/points.cpp
@@ -0,0 +1,525 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ points.cpp
+
+ Debugger breakpoints, watchpoints, etc.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "points.h"
+
+#include "debugger.h"
+#include "debugcon.h"
+#include "debugcpu.h"
+
+
+//**************************************************************************
+// DEBUG BREAKPOINT
+//**************************************************************************
+
+//-------------------------------------------------
+// debug_breakpoint - constructor
+//-------------------------------------------------
+
+debug_breakpoint::debug_breakpoint(
+ device_debug *debugInterface,
+ symbol_table &symbols,
+ int index,
+ offs_t address,
+ const char *condition,
+ std::string_view action) :
+ m_debugInterface(debugInterface),
+ m_index(index),
+ m_enabled(true),
+ m_address(address),
+ m_condition(symbols, condition ? condition : "1"),
+ m_action(action)
+{
+}
+
+
+//-------------------------------------------------
+// hit - detect a hit
+//-------------------------------------------------
+
+bool debug_breakpoint::hit(offs_t pc)
+{
+ // don't hit if disabled
+ if (!m_enabled)
+ return false;
+
+ // must match our address
+ if (m_address != pc)
+ return false;
+
+ // must satisfy the condition
+ if (!m_condition.is_empty())
+ {
+ try
+ {
+ return (m_condition.execute() != 0);
+ }
+ catch (expression_error const &)
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+
+//**************************************************************************
+// DEBUG WATCHPOINT
+//**************************************************************************
+
+//-------------------------------------------------
+// debug_watchpoint - constructor
+//-------------------------------------------------
+
+debug_watchpoint::debug_watchpoint(
+ device_debug* debugInterface,
+ symbol_table &symbols,
+ int index,
+ address_space &space,
+ read_or_write type,
+ offs_t address,
+ offs_t length,
+ const char *condition,
+ std::string_view 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 ? condition : "1"),
+ m_action(action),
+ m_installing(false)
+{
+ std::fill(std::begin(m_start_address), std::end(m_start_address), 0);
+ std::fill(std::begin(m_end_address), std::end(m_end_address), 0);
+ std::fill(std::begin(m_masks), std::end(m_masks), 0);
+
+ int ashift = m_space.addr_shift();
+ endianness_t endian = m_space.endianness();
+ offs_t subamask = m_space.alignment() - 1;
+ offs_t unit_size = ashift <= 0 ? 8 << -ashift : 8 >> ashift;
+ offs_t start = m_address;
+ offs_t end = (m_address + m_length - 1) & space.addrmask();
+ if (end < start)
+ end = space.addrmask();
+ offs_t rstart = start & ~subamask;
+ offs_t rend = end | subamask;
+ u64 smask, mmask, emask;
+ smask = mmask = emask = make_bitmask<u64>(m_space.data_width());
+ if (start != rstart)
+ {
+ if (endian == ENDIANNESS_LITTLE)
+ smask &= ~make_bitmask<u64>((start - rstart) * unit_size);
+ else
+ smask &= make_bitmask<u64>((rstart + subamask + 1 - start) * unit_size);
+ }
+ if (end != rend)
+ {
+ if (endian == ENDIANNESS_LITTLE)
+ emask &= make_bitmask<u64>((subamask + 1 + end - rend) * unit_size);
+ else
+ emask &= ~make_bitmask<u64>((rend - end) * unit_size);
+ }
+
+ if (rend == (rstart | subamask) || smask == emask)
+ {
+ m_start_address[0] = rstart;
+ m_end_address[0] = rend;
+ m_masks[0] = smask & emask;
+ }
+ else
+ {
+ int idx = 0;
+ if (smask != mmask)
+ {
+ m_start_address[idx] = rstart;
+ m_end_address[idx] = rstart | subamask;
+ m_masks[idx] = smask;
+ idx++;
+ rstart += subamask + 1;
+ }
+ if (mmask == emask)
+ {
+ m_start_address[idx] = rstart;
+ m_end_address[idx] = rend;
+ m_masks[idx] = emask;
+ }
+ else
+ {
+ if (rstart < rend - subamask)
+ {
+ m_start_address[idx] = rstart;
+ m_end_address[idx] = rend - subamask - 1;
+ m_masks[idx] = mmask;
+ idx++;
+ }
+ m_start_address[idx] = rend - subamask;
+ m_end_address[idx] = rend;
+ m_masks[idx] = emask;
+ }
+ }
+
+ install(read_or_write::READWRITE);
+ m_notifier = m_space.add_change_notifier(
+ [this] (read_or_write mode)
+ {
+ if (m_enabled)
+ {
+ install(mode);
+ }
+ });
+}
+
+debug_watchpoint::~debug_watchpoint()
+{
+ m_notifier.reset();
+ m_phr.remove();
+ m_phw.remove();
+}
+
+void debug_watchpoint::setEnabled(bool value)
+{
+ if (m_enabled != value)
+ {
+ m_enabled = value;
+ if (m_enabled)
+ install(read_or_write::READWRITE);
+ else
+ {
+ m_installing = true;
+ m_phr.remove();
+ m_phw.remove();
+ m_installing = false;
+ }
+ }
+}
+
+void debug_watchpoint::install(read_or_write mode)
+{
+ if (m_installing)
+ return;
+ m_installing = true;
+ if (u32(mode) & u32(read_or_write::READ))
+ m_phr.remove();
+ if (u32(mode) & u32(read_or_write::WRITE))
+ m_phw.remove();
+ std::string name = util::string_format("wp@%x", m_address);
+ switch (m_space.data_width())
+ {
+ case 8:
+ if (u32(m_type) & u32(mode) & u32(read_or_write::READ))
+ m_phr = m_space.install_read_tap(
+ m_start_address[0], m_end_address[0], name,
+ [this](offs_t offset, u8 &data, u8 mem_mask) {
+ triggered(read_or_write::READ, offset, data, mem_mask);
+ },
+ &m_phr);
+ if (u32(m_type) & u32(mode) & u32(read_or_write::WRITE))
+ m_phw = m_space.install_write_tap(
+ m_start_address[0], m_end_address[0], name,
+ [this](offs_t offset, u8 &data, u8 mem_mask) {
+ triggered(read_or_write::WRITE, offset, data, mem_mask);
+ },
+ &m_phw);
+ break;
+
+ case 16:
+ for (int i=0; i != 3; i++)
+ if (m_masks[i])
+ {
+ u16 mask = m_masks[i];
+ if (u32(m_type) & u32(mode) & u32(read_or_write::READ))
+ m_phr = m_space.install_read_tap(
+ m_start_address[i], m_end_address[i], name,
+ [this, mask](offs_t offset, u16 &data, u16 mem_mask) {
+ if (mem_mask & mask)
+ triggered(read_or_write::READ, offset, data, mem_mask);
+ },
+ &m_phr);
+ if (u32(m_type) & u32(mode) & u32(read_or_write::WRITE))
+ m_phw = m_space.install_write_tap(
+ m_start_address[i], m_end_address[i], name,
+ [this, mask](offs_t offset, u16 &data, u16 mem_mask) {
+ if (mem_mask & mask)
+ triggered(read_or_write::WRITE, offset, data, mem_mask);
+ },
+ &m_phw);
+ }
+ break;
+
+ case 32:
+ for (int i=0; i != 3; i++)
+ if (m_masks[i])
+ {
+ u32 mask = m_masks[i];
+ if (u32(m_type) & u32(mode) & u32(read_or_write::READ))
+ m_phr = m_space.install_read_tap(
+ m_start_address[i], m_end_address[i], name,
+ [this, mask](offs_t offset, u32 &data, u32 mem_mask) {
+ if (mem_mask & mask)
+ triggered(read_or_write::READ, offset, data, mem_mask);
+ },
+ &m_phr);
+ if (u32(m_type) & u32(mode) & u32(read_or_write::WRITE))
+ m_phw = m_space.install_write_tap(
+ m_start_address[i], m_end_address[i], name,
+ [this, mask](offs_t offset, u32 &data, u32 mem_mask) {
+ if (mem_mask & mask)
+ triggered(read_or_write::WRITE, offset, data, mem_mask);
+ },
+ &m_phw);
+ }
+ break;
+
+ case 64:
+ for (int i=0; i != 3; i++)
+ if (m_masks[i])
+ {
+ u64 mask = m_masks[i];
+ if (u32(m_type) & u32(mode) & u32(read_or_write::READ))
+ m_phr = m_space.install_read_tap(
+ m_start_address[i], m_end_address[i], name,
+ [this, mask](offs_t offset, u64 &data, u64 mem_mask) {
+ if (mem_mask & mask)
+ triggered(read_or_write::READ, offset, data, mem_mask);
+ },
+ &m_phr);
+ if (u32(m_type) & u32(mode) & u32(read_or_write::WRITE))
+ m_phw = m_space.install_write_tap(m_start_address[i], m_end_address[i], name,
+ [this, mask](offs_t offset, u64 &data, u64 mem_mask) {
+ if (mem_mask & mask)
+ triggered(read_or_write::WRITE, offset, data, mem_mask);
+ },
+ &m_phw);
+ }
+ break;
+ }
+ m_installing = false;
+}
+
+void debug_watchpoint::triggered(read_or_write type, offs_t address, u64 data, u64 mem_mask)
+{
+ running_machine &machine = m_debugInterface->device().machine();
+ debugger_manager &debug = machine.debugger();
+
+ // if we're within debugger code, don't trigger
+ if (debug.cpu().within_instruction_hook() || machine.side_effects_disabled())
+ return;
+
+ // adjust address, size & value_to_write based on mem_mask.
+ offs_t size = 0;
+ int ashift = m_space.addr_shift();
+ offs_t unit_size = ashift <= 0 ? 8 << -ashift : 8 >> ashift;
+ u64 unit_mask = make_bitmask<u64>(unit_size);
+
+ offs_t address_offset = 0;
+
+ if(!mem_mask)
+ mem_mask = 0xff;
+
+ while (!(mem_mask & unit_mask))
+ {
+ address_offset++;
+ data >>= unit_size;
+ mem_mask >>= unit_size;
+ }
+
+ while (mem_mask)
+ {
+ size++;
+ mem_mask >>= unit_size;
+ }
+
+ data &= make_bitmask<u64>(size * unit_size);
+
+ if (m_space.endianness() == ENDIANNESS_LITTLE)
+ address += address_offset;
+ else
+ address += m_space.alignment() - size - address_offset;
+
+ // stash the value that will be written or has just been read
+ debug.cpu().set_wpinfo(address, data, size * unit_size);
+
+ // protect against recursion
+ debug.cpu().set_within_instruction(true);
+
+ // must satisfy the condition
+ if (!m_condition.is_empty())
+ {
+ try
+ {
+ if (!m_condition.execute())
+ {
+ debug.cpu().set_within_instruction(false);
+ return;
+ }
+ }
+ catch (expression_error &)
+ {
+ debug.cpu().set_within_instruction(false);
+ return;
+ }
+ }
+
+ // halt in the debugger by default
+ bool was_stopped = debug.cpu().is_stopped();
+ debug.cpu().set_execution_stopped();
+
+ // evaluate the action
+ if (!m_action.empty())
+ debug.console().execute_command(m_action, false);
+
+ // print a notification, unless the action made us go again
+ if (debug.cpu().is_stopped())
+ {
+ std::string buffer;
+
+ if (m_space.is_octal())
+ buffer = string_format(type == read_or_write::READ ?
+ "Stopped at watchpoint %X reading %0*o from %0*o" :
+ "Stopped at watchpoint %X writing %0*o to %0*o",
+ m_index,
+ (size * unit_size + 2) / 3,
+ data,
+ (m_space.addr_width() + 2) / 3,
+ address);
+ else
+ buffer = string_format(type == read_or_write::READ ?
+ "Stopped at watchpoint %X reading %0*X from %0*X" :
+ "Stopped at watchpoint %X writing %0*X to %0*X",
+ m_index,
+ size * unit_size / 4,
+ data,
+ (m_space.addr_width() + 3) / 4,
+ address);
+
+ const device_state_interface *state;
+ if (debug.cpu().live_cpu() == &m_debugInterface->device() && m_debugInterface->device().interface(state))
+ {
+ debug.console().printf("%s (PC=%s)\n", buffer, state->state_string(STATE_GENPCBASE));
+ m_debugInterface->compute_debug_flags();
+ }
+ else if (!was_stopped)
+ {
+ debug.console().printf("%s\n", buffer);
+ debug.cpu().set_execution_running();
+ debug.cpu().set_break_cpu(&m_debugInterface->device());
+ }
+ m_debugInterface->set_triggered_watchpoint(this);
+ }
+
+ debug.cpu().set_within_instruction(false);
+}
+
+//**************************************************************************
+// DEBUG REGISTERPOINT
+//**************************************************************************
+
+//-------------------------------------------------
+// debug_registerpoint - constructor
+//-------------------------------------------------
+
+debug_registerpoint::debug_registerpoint(symbol_table &symbols, int index, const char *condition, std::string_view action)
+ : m_index(index),
+ m_enabled(true),
+ m_condition(symbols, (condition != nullptr) ? condition : "1"),
+ m_action(action)
+{
+}
+
+
+//-------------------------------------------------
+// hit - detect a hit
+//-------------------------------------------------
+
+bool debug_registerpoint::hit()
+{
+ // don't hit if disabled
+ if (!m_enabled)
+ return false;
+
+ // must satisfy the condition
+ if (!m_condition.is_empty())
+ {
+ try
+ {
+ return (m_condition.execute() != 0);
+ }
+ catch (expression_error &)
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+//**************************************************************************
+// DEBUG EXCEPTION POINT
+//**************************************************************************
+
+//-------------------------------------------------
+// debug_exceptionpoint - constructor
+//-------------------------------------------------
+
+debug_exceptionpoint::debug_exceptionpoint(
+ device_debug *debugInterface,
+ symbol_table &symbols,
+ int index,
+ int type,
+ const char *condition,
+ std::string_view action)
+ : m_debugInterface(debugInterface),
+ m_index(index),
+ m_enabled(true),
+ m_type(type),
+ m_condition(symbols, (condition != nullptr) ? condition : "1"),
+ m_action(action)
+{
+}
+
+
+//-------------------------------------------------
+// hit - detect a hit
+//-------------------------------------------------
+
+bool debug_exceptionpoint::hit(int exception)
+{
+ // don't hit if disabled
+ if (!m_enabled)
+ return false;
+
+ // must match our type
+ if (m_type != exception)
+ return false;
+
+ // must satisfy the condition
+ if (!m_condition.is_empty())
+ {
+ try
+ {
+ return (m_condition.execute() != 0);
+ }
+ catch (expression_error &)
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/src/emu/debug/points.h b/src/emu/debug/points.h
new file mode 100644
index 00000000000..cf9e5bfc44d
--- /dev/null
+++ b/src/emu/debug/points.h
@@ -0,0 +1,186 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ points.h
+
+ Debugger breakpoints, watchpoints, etc.
+
+***************************************************************************/
+
+#ifndef MAME_EMU_DEBUG_POINTS_H
+#define MAME_EMU_DEBUG_POINTS_H
+
+#pragma once
+
+#include "express.h"
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+
+// ======================> debug_breakpoint
+
+class debug_breakpoint
+{
+ friend class device_debug;
+
+public:
+ // construction/destruction
+ debug_breakpoint(
+ device_debug* debugInterface,
+ symbol_table &symbols,
+ int index,
+ offs_t address,
+ const char *condition = nullptr,
+ std::string_view action = {});
+
+ // getters
+ const device_debug *debugInterface() const { return m_debugInterface; }
+ int index() const { return m_index; }
+ bool enabled() const { return m_enabled; }
+ offs_t address() const { return m_address; }
+ const char *condition() const { return m_condition.original_string(); }
+ const std::string &action() const { return m_action; }
+
+ // setters
+ void setEnabled(bool value) { m_enabled = value; } // FIXME: need to update breakpoint flags but it's a private method
+
+private:
+ // internals
+ bool hit(offs_t pc);
+ const device_debug * m_debugInterface; // the interface we were created from
+ int m_index; // user reported index
+ bool m_enabled; // enabled?
+ offs_t m_address; // execution address
+ parsed_expression m_condition; // condition
+ std::string m_action; // action
+};
+
+// ======================> debug_watchpoint
+
+class debug_watchpoint
+{
+ friend class device_debug;
+
+public:
+ // construction/destruction
+ debug_watchpoint(
+ device_debug* debugInterface,
+ symbol_table &symbols,
+ int index,
+ address_space &space,
+ read_or_write type,
+ offs_t address,
+ offs_t length,
+ const char *condition = nullptr,
+ std::string_view action = {});
+ ~debug_watchpoint();
+
+ // getters
+ const device_debug *debugInterface() const { return m_debugInterface; }
+ address_space &space() const { return m_space; }
+ int index() const { return m_index; }
+ read_or_write type() const { return m_type; }
+ bool enabled() const { return m_enabled; }
+ offs_t address() const { return m_address; }
+ offs_t length() const { return m_length; }
+ const char *condition() const { return m_condition.original_string(); }
+ const std::string &action() const { return m_action; }
+
+ // setters
+ void setEnabled(bool value);
+
+ // internals
+ bool hit(int type, offs_t address, int size);
+
+private:
+ void install(read_or_write mode);
+ void triggered(read_or_write type, offs_t address, u64 data, u64 mem_mask);
+
+ device_debug * m_debugInterface; // the interface we were created from
+ memory_passthrough_handler m_phr; // passthrough handler reference, read access
+ memory_passthrough_handler m_phw; // passthrough handler reference, write access
+ address_space & m_space; // address space
+ int m_index; // user reported index
+ bool m_enabled; // enabled?
+ read_or_write m_type; // type (read/write)
+ offs_t m_address; // start address
+ offs_t m_length; // length of watch area
+ parsed_expression m_condition; // condition
+ std::string m_action; // action
+ util::notifier_subscription m_notifier; // address map change notifier ID
+
+ offs_t m_start_address[3]; // the start addresses of the checks to install
+ offs_t m_end_address[3]; // the end addresses
+ u64 m_masks[3]; // the access masks
+ bool m_installing; // prevent recursive multiple installs
+};
+
+// ======================> debug_registerpoint
+
+class debug_registerpoint
+{
+ friend class device_debug;
+
+public:
+ // construction/destruction
+ debug_registerpoint(symbol_table &symbols, int index, const char *condition, std::string_view action = {});
+
+ // getters
+ int index() const { return m_index; }
+ bool enabled() const { return m_enabled; }
+ const char *condition() const { return m_condition.original_string(); }
+ const std::string &action() const { return m_action; }
+
+private:
+ // internals
+ bool hit();
+
+ int m_index; // user reported index
+ bool m_enabled; // enabled?
+ parsed_expression m_condition; // condition
+ std::string m_action; // action
+};
+
+// ======================> debug_exceptionpoint
+
+class debug_exceptionpoint
+{
+ friend class device_debug;
+
+public:
+ // construction/destruction
+ debug_exceptionpoint(
+ device_debug* debugInterface,
+ symbol_table &symbols,
+ int index,
+ int type,
+ const char *condition = nullptr,
+ std::string_view action = {});
+
+ // getters
+ const device_debug *debugInterface() const { return m_debugInterface; }
+ int index() const { return m_index; }
+ bool enabled() const { return m_enabled; }
+ int type() const { return m_type; }
+ const char *condition() const { return m_condition.original_string(); }
+ const std::string &action() const { return m_action; }
+
+ // setters
+ void setEnabled(bool value) { m_enabled = value; }
+
+private:
+ // internals
+ bool hit(int exception);
+ const device_debug * m_debugInterface; // the interface we were created from
+ int m_index; // user reported index
+ bool m_enabled; // enabled?
+ int m_type; // exception type
+ parsed_expression m_condition; // condition
+ std::string m_action; // action
+};
+
+#endif // MAME_EMU_DEBUG_POINTS_H
diff --git a/src/emu/debug/textbuf.cpp b/src/emu/debug/textbuf.cpp
index 279ce8dead6..c70f8660185 100644
--- a/src/emu/debug/textbuf.cpp
+++ b/src/emu/debug/textbuf.cpp
@@ -2,15 +2,16 @@
// copyright-holders:Aaron Giles
/***************************************************************************
- textbuf.c
+ textbuf.cpp
Debugger text buffering engine.
***************************************************************************/
-#include "corealloc.h"
#include "textbuf.h"
+#include <new>
+
/***************************************************************************
@@ -27,47 +28,46 @@
struct text_buffer
{
- char * buffer;
- s32 * lineoffs;
- s32 bufsize;
- s32 bufstart;
- s32 bufend;
- s32 linesize;
- s32 linestart;
- s32 lineend;
- u32 linestartseq;
- s32 maxwidth;
-};
-
-
-
-/***************************************************************************
- INLINE FUNCTIONS
-***************************************************************************/
-
-/*-------------------------------------------------
- buffer_used - return the number of bytes
- currently held in the buffer
--------------------------------------------------*/
-
-static inline s32 buffer_used(text_buffer *text)
-{
- s32 used = text->bufend - text->bufstart;
- if (used < 0)
- used += text->bufsize;
- return used;
-}
+ text_buffer(u32 bytes, u32 lines) noexcept
+ : buffer(new (std::nothrow) char [bytes])
+ , lineoffs(new (std::nothrow) s32 [lines])
+ , bufsize(buffer ? bytes : 0)
+ , linesize(lineoffs ? lines : 0)
+ {
+ }
+ std::unique_ptr<char []> const buffer;
+ std::unique_ptr<s32 []> const lineoffs;
+ s32 const bufsize;
+ s32 bufstart = 0;
+ s32 bufend = 0;
+ s32 const linesize;
+ s32 linestart = 0;
+ s32 lineend = 0;
+ u32 linestartseq = 0;
+ s32 maxwidth = 0;
+
+ /*-------------------------------------------------
+ buffer_used - return the number of bytes
+ currently held in the buffer
+ -------------------------------------------------*/
+
+ s32 buffer_used() const noexcept
+ {
+ s32 const used(bufend - bufstart);
+ return (used < 0) ? (used + bufsize) : used;
+ }
-/*-------------------------------------------------
- buffer_space - return the number of bytes
- available in the buffer
--------------------------------------------------*/
+ /*-------------------------------------------------
+ buffer_space - return the number of bytes
+ available in the buffer
+ -------------------------------------------------*/
-static inline s32 buffer_space(text_buffer *text)
-{
- return text->bufsize - buffer_used(text);
-}
+ s32 buffer_space() const noexcept
+ {
+ return bufsize - buffer_used();
+ }
+};
@@ -81,36 +81,19 @@ static inline s32 buffer_space(text_buffer *text)
text_buffer_alloc - allocate a new text buffer
-------------------------------------------------*/
-text_buffer *text_buffer_alloc(u32 bytes, u32 lines)
+text_buffer_ptr text_buffer_alloc(u32 bytes, u32 lines)
{
- text_buffer *text;
+ // allocate memory for the text buffer object
+ text_buffer_ptr text(new (std::nothrow) text_buffer(bytes, lines));
- /* allocate memory for the text buffer object */
- text = global_alloc_nothrow(text_buffer);
if (!text)
return nullptr;
- /* allocate memory for the buffer itself */
- text->buffer = global_alloc_array_nothrow(char, bytes);
- if (!text->buffer)
- {
- global_free(text);
- return nullptr;
- }
-
- /* allocate memory for the lines array */
- text->lineoffs = global_alloc_array_nothrow(s32, lines);
- if (!text->lineoffs)
- {
- global_free_array(text->buffer);
- global_free(text);
+ if (!text->buffer || !text->lineoffs)
return nullptr;
- }
- /* initialize the buffer description */
- text->bufsize = bytes;
- text->linesize = lines;
- text_buffer_clear(text);
+ // initialize the buffer description
+ text_buffer_clear(*text);
return text;
}
@@ -121,13 +104,9 @@ text_buffer *text_buffer_alloc(u32 bytes, u32 lines)
text buffer
-------------------------------------------------*/
-void text_buffer_free(text_buffer *text)
+void text_buffer_deleter::operator()(text_buffer *text) const
{
- if (text->lineoffs)
- global_free_array(text->lineoffs);
- if (text->buffer)
- global_free_array(text->buffer);
- global_free(text);
+ delete text;
}
@@ -135,21 +114,21 @@ void text_buffer_free(text_buffer *text)
text_buffer_clear - clear a text buffer
-------------------------------------------------*/
-void text_buffer_clear(text_buffer *text)
+void text_buffer_clear(text_buffer &text)
{
- /* reset all the buffer pointers and other bits */
- text->bufstart = 0;
- text->bufend = 0;
+ // reset all the buffer pointers and other bits
+ text.bufstart = 0;
+ text.bufend = 0;
- text->linestart = 0;
- text->lineend = 0;
- text->linestartseq = 0;
+ text.linestart = 0;
+ text.lineend = 0;
+ text.linestartseq = 0;
- text->maxwidth = 0;
+ text.maxwidth = 0;
- /* create the initial line */
- text->lineoffs[0] = 0;
- text->buffer[text->lineoffs[0]] = 0;
+ // create the initial line
+ text.lineoffs[0] = 0;
+ text.buffer[text.lineoffs[0]] = 0;
}
@@ -160,109 +139,107 @@ void text_buffer_clear(text_buffer *text)
***************************************************************************/
-/*-------------------------------------------------
- text_buffer_print - print data to the text
- buffer
--------------------------------------------------*/
+//-------------------------------------------------
+// text_buffer_print - print data to the text
+// buffer
+//-------------------------------------------------
-void text_buffer_print(text_buffer *text, const char *data)
+void text_buffer_print(text_buffer &text, std::string_view data)
{
- text_buffer_print_wrap(text, data, 10000);
+ text_buffer_print_wrap(text, data, MAX_LINE_LENGTH);
}
-/*-------------------------------------------------
- text_buffer_print_wrap - print data to the
- text buffer with word wrapping to a given
- column
--------------------------------------------------*/
+//-------------------------------------------------
+// text_buffer_print_wrap - print data to the
+// text buffer with word wrapping to a given
+// column
+//-------------------------------------------------
-void text_buffer_print_wrap(text_buffer *text, const char *data, int wrapcol)
+void text_buffer_print_wrap(text_buffer &text, std::string_view data, int wrapcol)
{
- s32 stopcol = (wrapcol < MAX_LINE_LENGTH) ? wrapcol : MAX_LINE_LENGTH;
- s32 needed_space;
+ s32 const stopcol = (wrapcol < MAX_LINE_LENGTH) ? wrapcol : MAX_LINE_LENGTH;
- /* we need to ensure there is enough space for this string plus enough for the max line length */
- needed_space = s32(strlen(data)) + MAX_LINE_LENGTH;
+ // we need to ensure there is enough space for this string plus enough for the max line length
+ s32 const needed_space = s32(data.length()) + MAX_LINE_LENGTH;
- /* make space in the buffer if we need to */
- while (buffer_space(text) < needed_space && text->linestart != text->lineend)
+ // make space in the buffer if we need to
+ while (text.buffer_space() < needed_space && text.linestart != text.lineend)
{
- text->linestartseq++;
- if (++text->linestart >= text->linesize)
- text->linestart = 0;
- text->bufstart = text->lineoffs[text->linestart];
+ text.linestartseq++;
+ if (++text.linestart >= text.linesize)
+ text.linestart = 0;
+ text.bufstart = text.lineoffs[text.linestart];
}
- /* now add the data */
- for ( ; *data; data++)
+ // now add the data
+ for (int ch : data)
{
- int ch = *data;
int linelen;
- /* a CR resets our position */
+ // a CR resets our position
if (ch == '\r')
- text->bufend = text->lineoffs[text->lineend];
+ text.bufend = text.lineoffs[text.lineend];
- /* non-CR data is just characters */
+ // non-CR data is just characters
else if (ch != '\n')
- text->buffer[text->bufend++] = ch;
+ text.buffer[text.bufend++] = ch;
- /* an explicit newline or line-too-long condition inserts a newline */
- linelen = text->bufend - text->lineoffs[text->lineend];
+ // an explicit newline or line-too-long condition inserts a newline */
+ linelen = text.bufend - text.lineoffs[text.lineend];
if (ch == '\n' || linelen >= stopcol)
{
int overflow = 0;
- /* if we're wrapping, back off until we hit a space */
+ // if we're wrapping, back off until we hit a space
if (linelen >= wrapcol)
{
- /* scan backwards, removing characters along the way */
+ // scan backwards, removing characters along the way
overflow = 1;
- while (overflow < linelen && text->buffer[text->bufend - overflow] != ' ')
+ while (overflow < linelen && text.buffer[text.bufend - overflow] != ' ')
overflow++;
- /* if we found a space, take it; otherwise, reset and pretend we didn't try */
+ // if we found a space, take it; otherwise, reset and pretend we didn't try
if (overflow < linelen)
linelen -= overflow;
else
overflow = 0;
}
- /* did we beat the max width */
- if (linelen > text->maxwidth)
- text->maxwidth = linelen;
+ // did we beat the max width
+ if (linelen > text.maxwidth)
+ text.maxwidth = linelen;
- /* append a terminator */
+ // append a terminator
if (overflow == 0)
- text->buffer[text->bufend++] = 0;
+ text.buffer[text.bufend++] = 0;
else
- text->buffer[text->bufend - overflow] = 0;
+ text.buffer[text.bufend - overflow] = 0;
- /* determine what the next line will be */
- if (++text->lineend >= text->linesize)
- text->lineend = 0;
+ // determine what the next line will be
+ if (++text.lineend >= text.linesize)
+ text.lineend = 0;
- /* if we're out of lines, consume the next one */
- if (text->lineend == text->linestart)
+ // if we're out of lines, consume the next one
+ if (text.lineend == text.linestart)
{
- text->linestartseq++;
- if (++text->linestart >= text->linesize)
- text->linestart = 0;
- text->bufstart = text->lineoffs[text->linestart];
+ text.linestartseq++;
+ if (++text.linestart >= text.linesize)
+ text.linestart = 0;
+ text.bufstart = text.lineoffs[text.linestart];
}
- /* if we don't have enough room in the buffer for a max line, wrap to the start */
- if (text->bufend + MAX_LINE_LENGTH + 1 >= text->bufsize)
- text->bufend = 0;
+ // if we don't have enough room in the buffer for a max line, wrap to the start
+ if (text.bufend + MAX_LINE_LENGTH + 1 >= text.bufsize)
+ text.bufend = 0;
- /* create a new empty line */
- text->lineoffs[text->lineend] = text->bufend - (overflow ? (overflow - 1) : 0);
+ // create a new empty line
+ text.lineoffs[text.lineend] = text.bufend - (overflow ? (overflow - 1) : 0);
}
}
- /* nullptr terminate what we have on this line */
- text->buffer[text->bufend] = 0;
+ // null terminate what we have on this line
+ text.buffer[text.bufend] = 0;
}
@@ -278,9 +255,9 @@ void text_buffer_print_wrap(text_buffer *text, const char *data, int wrapcol)
width of all lines seen so far
-------------------------------------------------*/
-u32 text_buffer_max_width(text_buffer *text)
+u32 text_buffer_max_width(text_buffer const &text)
{
- return text->maxwidth;
+ return text.maxwidth;
}
@@ -289,12 +266,10 @@ u32 text_buffer_max_width(text_buffer *text)
lines in the text buffer
-------------------------------------------------*/
-u32 text_buffer_num_lines(text_buffer *text)
+u32 text_buffer_num_lines(text_buffer const &text)
{
- s32 lines = text->lineend + 1 - text->linestart;
- if (lines <= 0)
- lines += text->linesize;
- return lines;
+ s32 const lines = text.lineend + 1 - text.linestart;
+ return (lines <= 0) ? (lines + text.linesize) : lines;
}
@@ -303,9 +278,9 @@ u32 text_buffer_num_lines(text_buffer *text)
line index into a sequence number
-------------------------------------------------*/
-u32 text_buffer_line_index_to_seqnum(text_buffer *text, u32 index)
+u32 text_buffer_line_index_to_seqnum(text_buffer const &text, u32 index)
{
- return text->linestartseq + index;
+ return text.linestartseq + index;
}
@@ -314,11 +289,68 @@ u32 text_buffer_line_index_to_seqnum(text_buffer *text, u32 index)
an indexed line in the buffer
-------------------------------------------------*/
-const char *text_buffer_get_seqnum_line(text_buffer *text, u32 seqnum)
+const char *text_buffer_get_seqnum_line(text_buffer const &text, u32 seqnum)
{
- u32 numlines = text_buffer_num_lines(text);
- u32 index = seqnum - text->linestartseq;
+ u32 const numlines = text_buffer_num_lines(text);
+ u32 const index = seqnum - text.linestartseq;
if (index >= numlines)
return nullptr;
- return &text->buffer[text->lineoffs[(text->linestart + index) % text->linesize]];
+ return &text.buffer[text.lineoffs[(text.linestart + index) % text.linesize]];
+}
+
+/*---------------------------------------------------------------------
+ text_buffer_lines::text_buffer_line_iterator::operator*
+ Gets the line that the iterator currently points to.
+-----------------------------------------------------------------------*/
+
+std::string_view text_buffer_lines::text_buffer_line_iterator::operator*() const
+{
+ char const *const line = &m_buffer.buffer[m_buffer.lineoffs[m_lineptr]];
+
+ auto next_lineptr = m_lineptr + 1;
+ if (next_lineptr == m_buffer.linesize)
+ next_lineptr = 0;
+
+ char const *const nextline = &m_buffer.buffer[m_buffer.lineoffs[next_lineptr]];
+
+ // -1 for the '\0' at the end of line
+ ptrdiff_t difference = (nextline - line) - 1;
+
+ if (difference < 0)
+ difference += m_buffer.bufsize;
+
+ return std::string_view{ line, std::string_view::size_type(difference) };
+}
+
+/*---------------------------------------------------------------------
+ text_buffer_lines::text_buffer_line_iterator::operator++
+ Moves to the next line.
+-----------------------------------------------------------------------*/
+
+text_buffer_lines::text_buffer_line_iterator &text_buffer_lines::text_buffer_line_iterator::operator++()
+{
+ if (++m_lineptr == m_buffer.linesize)
+ m_lineptr = 0;
+
+ return *this;
+}
+
+/*------------------------------------------------------
+ text_buffer_lines::begin()
+ Returns an iterator that points to the first line.
+--------------------------------------------------------*/
+
+text_buffer_lines::iterator text_buffer_lines::begin() const
+{
+ return text_buffer_line_iterator(m_buffer, m_buffer.linestart);
+}
+
+/*-----------------------------------------------------------
+ text_buffer_lines::begin()
+ Returns an iterator that points just past the last line.
+-------------------------------------------------------------*/
+
+text_buffer_lines::iterator text_buffer_lines::end() const
+{
+ return text_buffer_line_iterator(m_buffer, m_buffer.lineend);
}
diff --git a/src/emu/debug/textbuf.h b/src/emu/debug/textbuf.h
index 9d42c3032ce..2ec6afca981 100644
--- a/src/emu/debug/textbuf.h
+++ b/src/emu/debug/textbuf.h
@@ -11,46 +11,87 @@
#ifndef MAME_EMU_DEBUG_TEXTBUF_H
#define MAME_EMU_DEBUG_TEXTBUF_H
+#include <memory>
+#include <string_view>
+
#include "emucore.h"
+
/***************************************************************************
TYPE DEFINITIONS
***************************************************************************/
struct text_buffer;
-
+// helper class for iterating over the lines of a text_buffer
+class text_buffer_lines
+{
+private:
+ const text_buffer &m_buffer;
+
+public:
+ text_buffer_lines(const text_buffer& buffer) : m_buffer(buffer) { }
+
+ class text_buffer_line_iterator
+ {
+ const text_buffer &m_buffer;
+ s32 m_lineptr;
+ public:
+ text_buffer_line_iterator(const text_buffer &buffer, s32 lineptr) :
+ m_buffer(buffer),
+ m_lineptr(lineptr)
+ {
+ }
+
+ // technically this isn't a valid forward iterator, because operator * doesn't return a reference
+ std::string_view operator*() const;
+ text_buffer_line_iterator &operator++();
+
+ bool operator!=(const text_buffer_line_iterator& rhs)
+ {
+ return m_lineptr != rhs.m_lineptr;
+ }
+ // according to C++ spec, only != is needed; == is present for completeness.
+ bool operator==(const text_buffer_line_iterator& rhs) { return !operator!=(rhs); }
+ };
+
+ typedef text_buffer_line_iterator iterator;
+ typedef text_buffer_line_iterator const iterator_const;
+
+ iterator begin() const;
+ iterator end() const;
+};
/***************************************************************************
FUNCTION PROTOTYPES
***************************************************************************/
-/* allocate a new text buffer */
-text_buffer *text_buffer_alloc(u32 bytes, u32 lines);
-
-/* free a text buffer */
-void text_buffer_free(text_buffer *text);
+// free a text buffer
+struct text_buffer_deleter { void operator()(text_buffer *text) const; };
+using text_buffer_ptr = std::unique_ptr<text_buffer, text_buffer_deleter>;
-/* clear a text buffer */
-void text_buffer_clear(text_buffer *text);
+// allocate a new text buffer
+text_buffer_ptr text_buffer_alloc(u32 bytes, u32 lines);
-/* "print" data to a text buffer */
-void text_buffer_print(text_buffer *text, const char *data);
+// clear a text buffer
+void text_buffer_clear(text_buffer &text);
-/* "print" data to a text buffer with word wrapping to a given column */
-void text_buffer_print_wrap(text_buffer *text, const char *data, int wrapcol);
+// "print" data to a text buffer
+void text_buffer_print(text_buffer &text, std::string_view data);
-/* get the maximum width of lines seen so far */
-u32 text_buffer_max_width(text_buffer *text);
+// "print" data to a text buffer with word wrapping to a given column
+void text_buffer_print_wrap(text_buffer &text, std::string_view data, int wrapcol);
-/* get the current number of lines in the buffer */
-u32 text_buffer_num_lines(text_buffer *text);
+// get the maximum width of lines seen so far
+u32 text_buffer_max_width(const text_buffer &text);
-/* get an absolute sequence number for a given line */
-u32 text_buffer_line_index_to_seqnum(text_buffer *text, u32 index);
+// get the current number of lines in the buffer
+u32 text_buffer_num_lines(const text_buffer &text);
-/* get a sequenced line from the text buffer */
-const char *text_buffer_get_seqnum_line(text_buffer *text, u32 seqnum);
+// get an absolute sequence number for a given line
+u32 text_buffer_line_index_to_seqnum(const text_buffer &text, u32 index);
+// get a sequenced line from the text buffer
+const char *text_buffer_get_seqnum_line(const text_buffer &text, u32 seqnum);
-#endif /* MAME_EMU_DEBUG_TEXTBUF_H */
+#endif // MAME_EMU_DEBUG_TEXTBUF_H