diff options
Diffstat (limited to 'src/emu/debug')
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 ¶m, 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 ¶m, 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 ¶m, 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> ¶ms) +void debugger_commands::execute_help(const std::vector<std::string_view> ¶ms) { 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> ¶ms) +void debugger_commands::execute_print(const std::vector<std::string_view> ¶ms) { /* 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> ¶ms) { - 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> ¶ms) +template <typename T> +void debugger_commands::execute_index_command(std::vector<std::string_view> const ¶ms, 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 ¶m : index) + m_console.printf(unused_message, param); +} + + +/*------------------------------------------------- + execute_printf - execute the printf command +-------------------------------------------------*/ + +void debugger_commands::execute_printf(const std::vector<std::string_view> ¶ms) +{ /* 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> ¶ms) +void debugger_commands::execute_logerror(const std::vector<std::string_view> ¶ms) { - /* 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> ¶ms) +void debugger_commands::execute_tracelog(const std::vector<std::string_view> ¶ms) { - /* 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> ¶ms) +void debugger_commands::execute_tracesym(const std::vector<std::string_view> ¶ms) { // 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> ¶ms) +{ + 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> ¶ms) +void debugger_commands::execute_quit(const std::vector<std::string_view> ¶ms) { 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> ¶ms) +void debugger_commands::execute_do(const std::vector<std::string_view> ¶ms) { 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> ¶ execute_step - execute the step command -------------------------------------------------*/ -void debugger_commands::execute_step(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_step(const std::vector<std::string_view> ¶ms) { /* 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> ¶ms) +void debugger_commands::execute_over(const std::vector<std::string_view> ¶ms) { /* 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> ¶ms) +void debugger_commands::execute_out(const std::vector<std::string_view> ¶ms) { - 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> ¶ms) +void debugger_commands::execute_go(const std::vector<std::string_view> ¶ms) { 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> ¶ command -------------------------------------------------*/ -void debugger_commands::execute_go_vblank(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_go_vblank(const std::vector<std::string_view> ¶ms) { - 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> ¶ms) +void debugger_commands::execute_go_interrupt(const std::vector<std::string_view> ¶ms) { 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> ¶ms) +void debugger_commands::execute_go_exception(const std::vector<std::string_view> ¶ms) { 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> ¶ms) +void debugger_commands::execute_go_time(const std::vector<std::string_view> ¶ms) { 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> ¶ms) +void debugger_commands::execute_go_privilege(const std::vector<std::string_view> ¶ms) +{ + 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> ¶ms) +{ + 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> ¶ms) { - 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> ¶ms) +void debugger_commands::execute_next(const std::vector<std::string_view> ¶ms) { - 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> ¶ms) +void debugger_commands::execute_focus(const std::vector<std::string_view> ¶ms) { - /* 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> ¶ms) +void debugger_commands::execute_ignore(const std::vector<std::string_view> ¶ms) { - /* 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> ¶ms) +void debugger_commands::execute_observe(const std::vector<std::string_view> ¶ms) { - /* 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> ¶ms) +void debugger_commands::execute_suspend(const std::vector<std::string_view> ¶ms) { - /* 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> ¶ms) +void debugger_commands::execute_resume(const std::vector<std::string_view> ¶ms) { - /* 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> ¶ms) +{ + 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> ¶ms) +{ + 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> ¶ms) +void debugger_commands::execute_comment_add(const std::vector<std::string_view> ¶ms) { - 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> ¶ms) +void debugger_commands::execute_comment_del(const std::vector<std::string_view> ¶ms) { - 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> ¶ms) * @brief Print current list of comments in debugger * * */ -void debugger_commands::execute_comment_list(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_comment_list(const std::vector<std::string_view> ¶ms) { 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> ¶ms) * @brief Add and Save current list of comments in debugger * */ -void debugger_commands::execute_comment_commit(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_comment_commit(const std::vector<std::string_view> ¶ms) { - 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> ¶ms) +void debugger_commands::execute_comment_save(const std::vector<std::string_view> ¶ms) { - 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> ¶ms) * @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> ¶ms) +void debugger_commands::execute_bpset(const std::vector<std::string_view> ¶ms) { - 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> ¶ms) +void debugger_commands::execute_bpclear(const std::vector<std::string_view> ¶ms) { - 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> ¶ms) +void debugger_commands::execute_bpdisenable(bool enable, const std::vector<std::string_view> ¶ms) { - 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> ¶ms) +void debugger_commands::execute_bplist(const std::vector<std::string_view> ¶ms) { 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> ¶ms) +void debugger_commands::execute_wpset(int spacenum, const std::vector<std::string_view> ¶ms) { - 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> ¶ms) +void debugger_commands::execute_wpclear(const std::vector<std::string_view> ¶ms) { - 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> ¶ms) +void debugger_commands::execute_wpdisenable(bool enable, const std::vector<std::string_view> ¶ms) { - 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> ¶ms) +void debugger_commands::execute_wplist(const std::vector<std::string_view> ¶ms) { 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> ¶ms) +void debugger_commands::execute_rpset(const std::vector<std::string_view> ¶ms) { + // 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> ¶ms) +void debugger_commands::execute_rpclear(const std::vector<std::string_view> ¶ms) { - 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> ¶ms) +void debugger_commands::execute_rpdisenable(bool enable, const std::vector<std::string_view> ¶ms) { - 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> ¶ms) +{ + // 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> ¶ms) +{ + 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> ¶ms) +void debugger_commands::execute_epdisenable(bool enable, const std::vector<std::string_view> ¶ms) { - 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> ¶ms) +{ + 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> ¶ms) +void debugger_commands::execute_rplist(const std::vector<std::string_view> ¶ms) { - /* 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> ¶ms) +void debugger_commands::execute_statesave(const std::vector<std::string_view> ¶ms) { - 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> ¶ms) +void debugger_commands::execute_stateload(const std::vector<std::string_view> ¶ms) { - 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> ¶ms) +void debugger_commands::execute_rewind(const std::vector<std::string_view> ¶ms) { 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> ¶ms) +void debugger_commands::execute_save(int spacenum, const std::vector<std::string_view> ¶ms) { 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> ¶ms) +{ + 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> ¶ms) +void debugger_commands::execute_load(int spacenum, const std::vector<std::string_view> ¶ms) { 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> ¶ms) +{ + 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> ¶ms) +void debugger_commands::execute_dump(int spacenum, const std::vector<std::string_view> ¶ms) { - /* 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> ¶ms) +void debugger_commands::execute_strdump(int spacenum, const std::vector<std::string_view> ¶ms) { - 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> ¶ms) +{ + 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> ¶ms) +void debugger_commands::execute_cheatnext(bool initial, const std::vector<std::string_view> ¶ms) { - 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> ¶ms) +void debugger_commands::execute_cheatlist(const std::vector<std::string_view> ¶ms) { - 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> ¶ms) +void debugger_commands::execute_cheatundo(const std::vector<std::string_view> ¶ms) { - 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> ¶ms) +void debugger_commands::execute_find(int spacenum, const std::vector<std::string_view> ¶ms) { - 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> ¶ms) +{ + 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> ¶ms) +void debugger_commands::execute_dasm(const std::vector<std::string_view> ¶ms) { - 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> ¶ms, bool trace_over) +void debugger_commands::execute_trace(const std::vector<std::string_view> ¶ms, 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> ¶ms) -{ - 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> ¶ms) -{ - 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> ¶ms) +void debugger_commands::execute_traceflush(const std::vector<std::string_view> ¶ms) { - 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> ¶ms) +void debugger_commands::execute_history(const std::vector<std::string_view> ¶ms) { - /* 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> ¶ms) +void debugger_commands::execute_trackpc(const std::vector<std::string_view> ¶ms) { // 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> ¶ms) +void debugger_commands::execute_trackmem(const std::vector<std::string_view> ¶ms) { // 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> ¶ms) +void debugger_commands::execute_pcatmem(int spacenum, const std::vector<std::string_view> ¶ms) { - // 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> ¶ms) +void debugger_commands::execute_snap(const std::vector<std::string_view> ¶ms) { /* 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> ¶ms) +void debugger_commands::execute_source(const std::vector<std::string_view> ¶ms) { - 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> ¶ms) +void debugger_commands::execute_map(int spacenum, const std::vector<std::string_view> ¶ms) { - 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> ¶ms) +void debugger_commands::execute_memdump(const std::vector<std::string_view> ¶ms) { - 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> ¶ms) +void debugger_commands::execute_symlist(const std::vector<std::string_view> ¶ms) { - 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> ¶ms) +void debugger_commands::execute_softreset(const std::vector<std::string_view> ¶ms) { 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> ¶ms) +void debugger_commands::execute_hardreset(const std::vector<std::string_view> ¶ms) { 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> ¶ms) +void debugger_commands::execute_images(const std::vector<std::string_view> ¶ms) { - 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> ¶ms) +void debugger_commands::execute_mount(const std::vector<std::string_view> ¶ms) { - 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> ¶ms) +void debugger_commands::execute_unmount(const std::vector<std::string_view> ¶ms) { - 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> ¶ms) +void debugger_commands::execute_input(const std::vector<std::string_view> ¶ms) { - 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> ¶ms) +void debugger_commands::execute_dumpkbd(const std::vector<std::string_view> ¶ms) { // 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 ¶m, bool &result); - - /* validates a parameter as a numeric value */ - bool validate_number_parameter(const std::string ¶m, 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 ¶m, 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> ¶ms, bool trace_over); - - void execute_help(int ref, const std::vector<std::string> ¶ms); - void execute_print(int ref, const std::vector<std::string> ¶ms); - void execute_printf(int ref, const std::vector<std::string> ¶ms); - void execute_logerror(int ref, const std::vector<std::string> ¶ms); - void execute_tracelog(int ref, const std::vector<std::string> ¶ms); - void execute_tracesym(int ref, const std::vector<std::string> ¶ms); - void execute_quit(int ref, const std::vector<std::string> ¶ms); - void execute_do(int ref, const std::vector<std::string> ¶ms); - void execute_step(int ref, const std::vector<std::string> ¶ms); - void execute_over(int ref, const std::vector<std::string> ¶ms); - void execute_out(int ref, const std::vector<std::string> ¶ms); - void execute_go(int ref, const std::vector<std::string> ¶ms); - void execute_go_vblank(int ref, const std::vector<std::string> ¶ms); - void execute_go_interrupt(int ref, const std::vector<std::string> ¶ms); - void execute_go_exception(int ref, const std::vector<std::string> ¶ms); - void execute_go_time(int ref, const std::vector<std::string> ¶ms); - void execute_go_privilege(int ref, const std::vector<std::string> ¶ms); - void execute_focus(int ref, const std::vector<std::string> ¶ms); - void execute_ignore(int ref, const std::vector<std::string> ¶ms); - void execute_observe(int ref, const std::vector<std::string> ¶ms); - void execute_suspend(int ref, const std::vector<std::string> ¶ms); - void execute_resume(int ref, const std::vector<std::string> ¶ms); - void execute_next(int ref, const std::vector<std::string> ¶ms); - void execute_comment_add(int ref, const std::vector<std::string> ¶ms); - void execute_comment_del(int ref, const std::vector<std::string> ¶ms); - void execute_comment_save(int ref, const std::vector<std::string> ¶ms); - void execute_comment_list(int ref, const std::vector<std::string> ¶ms); - void execute_comment_commit(int ref, const std::vector<std::string> ¶ms); - void execute_bpset(int ref, const std::vector<std::string> ¶ms); - void execute_bpclear(int ref, const std::vector<std::string> ¶ms); - void execute_bpdisenable(int ref, const std::vector<std::string> ¶ms); - void execute_bplist(int ref, const std::vector<std::string> ¶ms); - void execute_wpset(int ref, const std::vector<std::string> ¶ms); - void execute_wpclear(int ref, const std::vector<std::string> ¶ms); - void execute_wpdisenable(int ref, const std::vector<std::string> ¶ms); - void execute_wplist(int ref, const std::vector<std::string> ¶ms); - void execute_rpset(int ref, const std::vector<std::string> ¶ms); - void execute_rpclear(int ref, const std::vector<std::string> ¶ms); - void execute_rpdisenable(int ref, const std::vector<std::string> ¶ms); - void execute_rplist(int ref, const std::vector<std::string> ¶ms); - void execute_hotspot(int ref, const std::vector<std::string> ¶ms); - void execute_statesave(int ref, const std::vector<std::string> ¶ms); - void execute_stateload(int ref, const std::vector<std::string> ¶ms); - void execute_rewind(int ref, const std::vector<std::string> ¶ms); - void execute_save(int ref, const std::vector<std::string> ¶ms); - void execute_load(int ref, const std::vector<std::string> ¶ms); - void execute_dump(int ref, const std::vector<std::string> ¶ms); - void execute_cheatinit(int ref, const std::vector<std::string> ¶ms); - void execute_cheatnext(int ref, const std::vector<std::string> ¶ms); - void execute_cheatlist(int ref, const std::vector<std::string> ¶ms); - void execute_cheatundo(int ref, const std::vector<std::string> ¶ms); - void execute_dasm(int ref, const std::vector<std::string> ¶ms); - void execute_find(int ref, const std::vector<std::string> ¶ms); - void execute_trace(int ref, const std::vector<std::string> ¶ms); - void execute_traceover(int ref, const std::vector<std::string> ¶ms); - void execute_traceflush(int ref, const std::vector<std::string> ¶ms); - void execute_history(int ref, const std::vector<std::string> ¶ms); - void execute_trackpc(int ref, const std::vector<std::string> ¶ms); - void execute_trackmem(int ref, const std::vector<std::string> ¶ms); - void execute_pcatmem(int ref, const std::vector<std::string> ¶ms); - void execute_snap(int ref, const std::vector<std::string> ¶ms); - void execute_source(int ref, const std::vector<std::string> ¶ms); - void execute_map(int ref, const std::vector<std::string> ¶ms); - void execute_memdump(int ref, const std::vector<std::string> ¶ms); - void execute_symlist(int ref, const std::vector<std::string> ¶ms); - void execute_softreset(int ref, const std::vector<std::string> ¶ms); - void execute_hardreset(int ref, const std::vector<std::string> ¶ms); - void execute_images(int ref, const std::vector<std::string> ¶ms); - void execute_mount(int ref, const std::vector<std::string> ¶ms); - void execute_unmount(int ref, const std::vector<std::string> ¶ms); - void execute_input(int ref, const std::vector<std::string> ¶ms); - void execute_dumpkbd(int ref, const std::vector<std::string> ¶ms); + + 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> ¶ms); + template <typename T> + void execute_index_command(std::vector<std::string_view> const ¶ms, T &&apply, char const *unused_message); + + void execute_help(const std::vector<std::string_view> ¶ms); + void execute_print(const std::vector<std::string_view> ¶ms); + void execute_printf(const std::vector<std::string_view> ¶ms); + void execute_logerror(const std::vector<std::string_view> ¶ms); + void execute_tracelog(const std::vector<std::string_view> ¶ms); + void execute_tracesym(const std::vector<std::string_view> ¶ms); + void execute_cls(const std::vector<std::string_view> ¶ms); + void execute_quit(const std::vector<std::string_view> ¶ms); + void execute_do(const std::vector<std::string_view> ¶ms); + void execute_step(const std::vector<std::string_view> ¶ms); + void execute_over(const std::vector<std::string_view> ¶ms); + void execute_out(const std::vector<std::string_view> ¶ms); + void execute_go(const std::vector<std::string_view> ¶ms); + void execute_go_vblank(const std::vector<std::string_view> ¶ms); + void execute_go_interrupt(const std::vector<std::string_view> ¶ms); + void execute_go_exception(const std::vector<std::string_view> ¶ms); + void execute_go_time(const std::vector<std::string_view> ¶ms); + void execute_go_privilege(const std::vector<std::string_view> ¶ms); + void execute_go_branch(bool sense, const std::vector<std::string_view> ¶ms); + void execute_go_next_instruction(const std::vector<std::string_view> ¶ms); + void execute_focus(const std::vector<std::string_view> ¶ms); + void execute_ignore(const std::vector<std::string_view> ¶ms); + void execute_observe(const std::vector<std::string_view> ¶ms); + void execute_suspend(const std::vector<std::string_view> ¶ms); + void execute_resume(const std::vector<std::string_view> ¶ms); + void execute_next(const std::vector<std::string_view> ¶ms); + void execute_cpulist(const std::vector<std::string_view> ¶ms); + void execute_time(const std::vector<std::string_view> ¶ms); + void execute_comment_add(const std::vector<std::string_view> ¶ms); + void execute_comment_del(const std::vector<std::string_view> ¶ms); + void execute_comment_save(const std::vector<std::string_view> ¶ms); + void execute_comment_list(const std::vector<std::string_view> ¶ms); + void execute_comment_commit(const std::vector<std::string_view> ¶ms); + void execute_bpset(const std::vector<std::string_view> ¶ms); + void execute_bpclear(const std::vector<std::string_view> ¶ms); + void execute_bpdisenable(bool enable, const std::vector<std::string_view> ¶ms); + void execute_bplist(const std::vector<std::string_view> ¶ms); + void execute_wpset(int spacenum, const std::vector<std::string_view> ¶ms); + void execute_wpclear(const std::vector<std::string_view> ¶ms); + void execute_wpdisenable(bool enable, const std::vector<std::string_view> ¶ms); + void execute_wplist(const std::vector<std::string_view> ¶ms); + void execute_rpset(const std::vector<std::string_view> ¶ms); + void execute_rpclear(const std::vector<std::string_view> ¶ms); + void execute_rpdisenable(bool enable, const std::vector<std::string_view> ¶ms); + void execute_rplist(const std::vector<std::string_view> ¶ms); + void execute_epset(const std::vector<std::string_view> ¶ms); + void execute_epclear(const std::vector<std::string_view> ¶ms); + void execute_epdisenable(bool enable, const std::vector<std::string_view> ¶ms); + void execute_eplist(const std::vector<std::string_view> ¶ms); + void execute_statesave(const std::vector<std::string_view> ¶ms); + void execute_stateload(const std::vector<std::string_view> ¶ms); + void execute_rewind(const std::vector<std::string_view> ¶ms); + void execute_save(int spacenum, const std::vector<std::string_view> ¶ms); + void execute_saveregion(const std::vector<std::string_view> ¶ms); + void execute_load(int spacenum, const std::vector<std::string_view> ¶ms); + void execute_loadregion(const std::vector<std::string_view> ¶ms); + void execute_dump(int spacenum, const std::vector<std::string_view> ¶ms); + void execute_strdump(int spacenum, const std::vector<std::string_view> ¶ms); + void execute_cheatrange(bool init, const std::vector<std::string_view> ¶ms); + void execute_cheatnext(bool initial, const std::vector<std::string_view> ¶ms); + void execute_cheatlist(const std::vector<std::string_view> ¶ms); + void execute_cheatundo(const std::vector<std::string_view> ¶ms); + void execute_dasm(const std::vector<std::string_view> ¶ms); + void execute_find(int spacenum, const std::vector<std::string_view> ¶ms); + void execute_fill(int spacenum, const std::vector<std::string_view> ¶ms); + void execute_trace(const std::vector<std::string_view> ¶ms, bool trace_over); + void execute_traceflush(const std::vector<std::string_view> ¶ms); + void execute_history(const std::vector<std::string_view> ¶ms); + void execute_trackpc(const std::vector<std::string_view> ¶ms); + void execute_trackmem(const std::vector<std::string_view> ¶ms); + void execute_pcatmem(int spacenum, const std::vector<std::string_view> ¶ms); + void execute_snap(const std::vector<std::string_view> ¶ms); + void execute_source(const std::vector<std::string_view> ¶ms); + void execute_map(int spacenum, const std::vector<std::string_view> ¶ms); + void execute_memdump(const std::vector<std::string_view> ¶ms); + void execute_symlist(const std::vector<std::string_view> ¶ms); + void execute_softreset(const std::vector<std::string_view> ¶ms); + void execute_hardreset(const std::vector<std::string_view> ¶ms); + void execute_images(const std::vector<std::string_view> ¶ms); + void execute_mount(const std::vector<std::string_view> ¶ms); + void execute_unmount(const std::vector<std::string_view> ¶ms); + void execute_input(const std::vector<std::string_view> ¶ms); + void execute_dumpkbd(const std::vector<std::string_view> ¶ms); 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> ¶ms) +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> ¶ms) +{ + 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> ¶ms) { - 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 ¶m : 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(¶m[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, ¶ms[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 ®ions = 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 ¶m) 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> ¶ms); + void execute_help_custom(const std::vector<std::string_view> ¶ms); + 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> ¶ms); + 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 ¶m) 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> ®isterpoint_list() const { return m_rplist; } - int registerpoint_set(const char *condition, const char *action = nullptr); + const std::forward_list<debug_registerpoint> ®isterpoint_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 ®ion) - : 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 ®ion) + : 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 ®ion : 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 ®ion); - 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 ®ion); + 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 |