diff options
Diffstat (limited to 'src/emu/debug')
-rw-r--r-- | src/emu/debug/debugbuf.cpp | 43 | ||||
-rw-r--r-- | src/emu/debug/debugcmd.cpp | 1649 | ||||
-rw-r--r-- | src/emu/debug/debugcmd.h | 191 | ||||
-rw-r--r-- | src/emu/debug/debugcon.cpp | 758 | ||||
-rw-r--r-- | src/emu/debug/debugcon.h | 70 | ||||
-rw-r--r-- | src/emu/debug/debugcpu.cpp | 418 | ||||
-rw-r--r-- | src/emu/debug/debugcpu.h | 73 | ||||
-rw-r--r-- | src/emu/debug/debughlp.cpp | 235 | ||||
-rw-r--r-- | src/emu/debug/debughlp.h | 2 | ||||
-rw-r--r-- | src/emu/debug/debugvw.cpp | 4 | ||||
-rw-r--r-- | src/emu/debug/dvbpoints.cpp | 2 | ||||
-rw-r--r-- | src/emu/debug/dvmemory.cpp | 82 | ||||
-rw-r--r-- | src/emu/debug/dvmemory.h | 1 | ||||
-rw-r--r-- | src/emu/debug/dvrpoints.cpp | 2 | ||||
-rw-r--r-- | src/emu/debug/express.cpp | 42 | ||||
-rw-r--r-- | src/emu/debug/express.h | 25 | ||||
-rw-r--r-- | src/emu/debug/points.cpp | 255 | ||||
-rw-r--r-- | src/emu/debug/points.h | 54 |
18 files changed, 2397 insertions, 1509 deletions
diff --git a/src/emu/debug/debugbuf.cpp b/src/emu/debug/debugbuf.cpp index 85ec19d5fb9..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; @@ -209,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; } @@ -220,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; @@ -241,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; } @@ -255,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; } @@ -269,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; } @@ -283,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; } @@ -297,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; } @@ -1390,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; @@ -1421,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 b8de0298432..ad463ace500 100644 --- a/src/emu/debug/debugcmd.cpp +++ b/src/emu/debug/debugcmd.cpp @@ -21,6 +21,7 @@ #include "debugger.h" #include "emuopts.h" +#include "fileio.h" #include "natkeyboard.h" #include "render.h" #include "screen.h" @@ -31,35 +32,7 @@ #include <algorithm> #include <cctype> #include <fstream> - - - -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 +#include <sstream> @@ -80,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); } @@ -131,14 +105,15 @@ u64 debugger_commands::cheat_system::read_extended(offs_t address) const { address &= space->logaddrmask(); u64 value = space->unmap(); - if (space->device().memory().translate(space->spacenum(), TRANSLATE_READ_DEBUG, address)) + address_space *tspace; + if (space->device().memory().translate(space->spacenum(), device_memory_interface::TR_READ, address, tspace)) { switch (width) { - case 1: value = space->read_byte(address); break; - case 2: value = space->read_word_unaligned(address); break; - case 4: value = space->read_dword_unaligned(address); break; - case 8: value = space->read_qword_unaligned(address); break; + 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)); @@ -195,7 +170,7 @@ debugger_commands::debugger_commands(running_machine& machine, debugger_cpu& cpu 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( @@ -232,6 +207,9 @@ debugger_commands::debugger_commands(running_machine& machine, debugger_cpu& cpu 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)); @@ -240,6 +218,7 @@ debugger_commands::debugger_commands(running_machine& machine, debugger_cpu& cpu 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)); @@ -276,6 +255,13 @@ debugger_commands::debugger_commands(running_machine& machine, debugger_cpu& cpu 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)); @@ -443,475 +429,15 @@ void debugger_commands::global_set(global_entry *global, u64 value) -/*************************************************************************** - PARAMETER VALIDATION HELPERS -***************************************************************************/ - -/// \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_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 const is_true = !core_stricmp(param.c_str(), "true"); - bool const is_false = !core_stricmp(param.c_str(), "false"); - - 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_commands::validate_number_parameter(std::string_view param, u64 &result) -{ - // evaluate the expression; success if no error - try - { - result = parsed_expression(m_console.visible_symtable(), param).execute(); - return true; - } - catch (expression_error const &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; - } -} - - -/// \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_commands::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_console.get_visible_cpu(); - if (current) - { - result = current; - return true; - } - else - { - m_console.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(m_console.visible_symtable(), param).execute(); - } - catch (expression_error &) - { - m_console.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 - m_console.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_commands::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; - } - - m_console.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 address 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_commands::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_console.get_visible_cpu(); - if (!device) - { - m_console.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_console.get_visible_cpu()) - device = m_console.get_visible_cpu(); - else - device = &m_machine.root_device(); - } - } - } - - // if still no device found, evaluate as an expression - if (!device) - { - u64 cpunum; - try - { - cpunum = parsed_expression(m_console.visible_symtable(), param).execute(); - } - catch (expression_error const &) - { - // parsing failed - assume it was a tag - m_console.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 - m_console.printf("Invalid CPU index %u\n", cpunum); - return false; - } - } - - // ensure the device implements the memory interface - device_memory_interface *memory; - if (!device->interface(memory)) - { - m_console.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 - { - m_console.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()) - m_console.printf("No memory spaces found for device '%s'\n", device->tag()); - else - m_console.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_commands::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_commands::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 - { - m_console.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_commands::get_device_search_base(std::string_view ¶m) -{ - 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_console.get_visible_cpu(); - 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_console.get_visible_cpu(); - 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_commands::get_cpu_by_index(u64 cpunum) -{ - 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; -} - - -/*------------------------------------------------- - debug_command_parameter_expression - validates - an expression parameter --------------------------------------------------*/ - -bool debugger_commands::debug_command_parameter_expression(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 - 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.error_class() == CMDERR::NONE) - return true; - - /* output an error */ - m_console.printf("Error in command: %s\n", param); - m_console.printf(" %*s^", err.error_offset(), ""); - 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(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(std::string_view())); @@ -924,12 +450,12 @@ void debugger_commands::execute_help(const std::vector<std::string> ¶ms) execute_print - execute the print command -------------------------------------------------*/ -void debugger_commands::execute_print(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 */ @@ -943,131 +469,177 @@ void debugger_commands::execute_print(const std::vector<std::string> ¶ms) 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(); + + int param = 1; + u64 number; - /* parse the string looking for % signs */ - for (;;) + // 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 out the width */ - for (;;) + // parse optional left justification flag + if (f != format.end() && *f == '-') { - c = *f++; - if (!c || c < '0' || c > '9') break; - if (c == '0' && width == 0) - zerofill = 1; - width = width * 10 + (c - '0'); + left_justify = true; + f++; + } + + // parse optional zero fill flag + if (f != format.end() && *f == '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; } - 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 'O': case 'o': - if (params == 0) - { - m_console.printf("Not enough parameters for format!\n"); - return 0; - } - if (u32(*param >> 60) != 0) - { - p += sprintf(p, zerofill ? "%0*o" : "%*o", (width <= 20) ? 1 : width - 20, u32(*param >> 60)); - p += sprintf(p, "%0*o", 10, u32(BIT(*param, 30, 30))); - } + if (param < params.size() && m_console.validate_number_parameter(params[param++], number)) + util::stream_format(stream, zero_fill ? "%0*o" : "%*o", width, number); else { - if (width > 20) - p += sprintf(p, zerofill ? "%0*o" : "%*o", width - 20, 0); - if (u32(BIT(*param, 30, 30)) != 0) - p += sprintf(p, zerofill ? "%0*o" : "%*o", (width <= 10) ? 1 : width - 10, u32(BIT(*param, 30, 30))); - else if (width > 10) - p += sprintf(p, zerofill ? "%0*o" : "%*o", width - 10, 0); + m_console.printf("Not enough parameters for format!\n"); + return false; } - p += sprintf(p, zerofill ? "%0*o" : "%*o", (width < 10) ? width : 10, u32(BIT(*param, 0, 30))); - 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; } @@ -1077,12 +649,12 @@ int debugger_commands::mini_printf(char *buffer, const char *format, int params, -------------------------------------------------*/ template <typename T> -void debugger_commands::execute_index_command(std::vector<std::string> const ¶ms, T &&apply, char const *unused_message) +void debugger_commands::execute_index_command(std::vector<std::string_view> const ¶ms, T &&apply, char const *unused_message) { std::vector<u64> index(params.size()); for (int paramnum = 0; paramnum < params.size(); paramnum++) { - if (!validate_number_parameter(params[paramnum], index[paramnum])) + if (!m_console.validate_number_parameter(params[paramnum], index[paramnum])) return; } @@ -1106,18 +678,12 @@ void debugger_commands::execute_index_command(std::vector<std::string> const &pa execute_printf - execute the printf command -------------------------------------------------*/ -void debugger_commands::execute_printf(const std::vector<std::string> ¶ms) +void debugger_commands::execute_printf(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_console.printf("%s\n", buffer); + std::ostringstream buffer; + if (mini_printf(buffer, params)) + m_console.printf("%s\n", std::move(buffer).str()); } @@ -1125,18 +691,12 @@ void debugger_commands::execute_printf(const std::vector<std::string> ¶ms) execute_logerror - execute the logerror command -------------------------------------------------*/ -void debugger_commands::execute_logerror(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()); } @@ -1144,18 +704,12 @@ void debugger_commands::execute_logerror(const std::vector<std::string> ¶ms) execute_tracelog - execute the tracelog command -------------------------------------------------*/ -void debugger_commands::execute_tracelog(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_console.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()); } @@ -1163,11 +717,10 @@ void debugger_commands::execute_tracelog(const std::vector<std::string> ¶ms) execute_tracesym - execute the tracesym command -------------------------------------------------*/ -void debugger_commands::execute_tracesym(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]; for (int i = 0; i < params.size(); i++) { // find this symbol @@ -1182,16 +735,16 @@ void debugger_commands::execute_tracesym(const std::vector<std::string> ¶ms) 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 + std::vector<std::string_view> printf_params(params); + printf_params.insert(printf_params.begin(), format.str()); + // then do a printf - char buffer[1024]; - if (mini_printf(buffer, format.str().c_str(), params.size(), values)) - m_console.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()); } @@ -1199,7 +752,7 @@ void debugger_commands::execute_tracesym(const std::vector<std::string> ¶ms) execute_cls - execute the cls command -------------------------------------------------*/ -void debugger_commands::execute_cls(const std::vector<std::string> ¶ms) +void debugger_commands::execute_cls(const std::vector<std::string_view> ¶ms) { text_buffer_clear(m_console.get_console_textbuf()); } @@ -1209,7 +762,7 @@ void debugger_commands::execute_cls(const std::vector<std::string> ¶ms) execute_quit - execute the quit command -------------------------------------------------*/ -void debugger_commands::execute_quit(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(); @@ -1220,10 +773,10 @@ void debugger_commands::execute_quit(const std::vector<std::string> ¶ms) execute_do - execute the do command -------------------------------------------------*/ -void debugger_commands::execute_do(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); } @@ -1231,11 +784,11 @@ void debugger_commands::execute_do(const std::vector<std::string> ¶ms) execute_step - execute the step command -------------------------------------------------*/ -void debugger_commands::execute_step(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_console.get_visible_cpu()->debug()->single_step(steps); @@ -1246,11 +799,11 @@ void debugger_commands::execute_step(const std::vector<std::string> ¶ms) execute_over - execute the over command -------------------------------------------------*/ -void debugger_commands::execute_over(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_console.get_visible_cpu()->debug()->single_step_over(steps); @@ -1261,7 +814,7 @@ void debugger_commands::execute_over(const std::vector<std::string> ¶ms) execute_out - execute the out command -------------------------------------------------*/ -void debugger_commands::execute_out(const std::vector<std::string> ¶ms) +void debugger_commands::execute_out(const std::vector<std::string_view> ¶ms) { m_console.get_visible_cpu()->debug()->single_step_out(); } @@ -1271,12 +824,12 @@ void debugger_commands::execute_out(const std::vector<std::string> ¶ms) execute_go - execute the go command -------------------------------------------------*/ -void debugger_commands::execute_go(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_console.get_visible_cpu()->debug()->go(addr); @@ -1288,7 +841,7 @@ void debugger_commands::execute_go(const std::vector<std::string> ¶ms) command -------------------------------------------------*/ -void debugger_commands::execute_go_vblank(const std::vector<std::string> ¶ms) +void debugger_commands::execute_go_vblank(const std::vector<std::string_view> ¶ms) { m_console.get_visible_cpu()->debug()->go_vblank(); } @@ -1298,12 +851,12 @@ void debugger_commands::execute_go_vblank(const std::vector<std::string> ¶ms execute_go_interrupt - execute the goint command -------------------------------------------------*/ -void debugger_commands::execute_go_interrupt(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_console.get_visible_cpu()->debug()->go_interrupt(irqline); @@ -1313,16 +866,16 @@ void debugger_commands::execute_go_interrupt(const std::vector<std::string> &par execute_go_exception - execute the goex command -------------------------------------------------*/ -void debugger_commands::execute_go_exception(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 && !debug_command_parameter_expression(params[1], condition)) + if (params.size() > 1 && !m_console.validate_expression_parameter(params[1], condition)) return; m_console.get_visible_cpu()->debug()->go_exception(exception, condition.is_empty() ? "1" : condition.original_string()); @@ -1333,12 +886,12 @@ void debugger_commands::execute_go_exception(const std::vector<std::string> &par execute_go_time - execute the gtime command -------------------------------------------------*/ -void debugger_commands::execute_go_time(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_console.get_visible_cpu()->debug()->go_milliseconds(milliseconds); @@ -1349,20 +902,76 @@ void debugger_commands::execute_go_time(const std::vector<std::string> ¶ms) /*------------------------------------------------- execute_go_privilege - execute the gp command -------------------------------------------------*/ -void debugger_commands::execute_go_privilege(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 && !debug_command_parameter_expression(params[0], condition)) + 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) +{ + 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); + } + + cpu->debug()->go(pc); +} + + /*------------------------------------------------- execute_next - execute the next command -------------------------------------------------*/ -void debugger_commands::execute_next(const std::vector<std::string> ¶ms) +void debugger_commands::execute_next(const std::vector<std::string_view> ¶ms) { m_console.get_visible_cpu()->debug()->go_next_device(); } @@ -1372,11 +981,11 @@ void debugger_commands::execute_next(const std::vector<std::string> ¶ms) execute_focus - execute the focus command -------------------------------------------------*/ -void debugger_commands::execute_focus(const std::vector<std::string> ¶ms) +void debugger_commands::execute_focus(const std::vector<std::string_view> ¶ms) { // validate params device_t *cpu; - if (!validate_cpu_parameter(params[0], cpu)) + if (!m_console.validate_cpu_parameter(params[0], cpu)) return; // first clear the ignore flag on the focused CPU @@ -1394,7 +1003,7 @@ void debugger_commands::execute_focus(const std::vector<std::string> ¶ms) execute_ignore - execute the ignore command -------------------------------------------------*/ -void debugger_commands::execute_ignore(const std::vector<std::string> ¶ms) +void debugger_commands::execute_ignore(const std::vector<std::string_view> ¶ms) { if (params.empty()) { @@ -1426,7 +1035,7 @@ void debugger_commands::execute_ignore(const std::vector<std::string> ¶ms) // validate parameters for (int paramnum = 0; paramnum < params.size(); paramnum++) - if (!validate_cpu_parameter(params[paramnum], devicelist[paramnum])) + if (!m_console.validate_cpu_parameter(params[paramnum], devicelist[paramnum])) return; // set the ignore flags @@ -1457,7 +1066,7 @@ void debugger_commands::execute_ignore(const std::vector<std::string> ¶ms) execute_observe - execute the observe command -------------------------------------------------*/ -void debugger_commands::execute_observe(const std::vector<std::string> ¶ms) +void debugger_commands::execute_observe(const std::vector<std::string_view> ¶ms) { if (params.empty()) { @@ -1489,7 +1098,7 @@ void debugger_commands::execute_observe(const std::vector<std::string> ¶ms) // validate parameters for (int paramnum = 0; paramnum < params.size(); paramnum++) - if (!validate_cpu_parameter(params[paramnum], devicelist[paramnum])) + if (!m_console.validate_cpu_parameter(params[paramnum], devicelist[paramnum])) return; // clear the ignore flags @@ -1505,7 +1114,7 @@ void debugger_commands::execute_observe(const std::vector<std::string> ¶ms) execute_suspend - suspend execution on cpu -------------------------------------------------*/ -void debugger_commands::execute_suspend(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 (params.empty()) @@ -1535,7 +1144,7 @@ void debugger_commands::execute_suspend(const std::vector<std::string> ¶ms) // validate parameters for (int paramnum = 0; paramnum < params.size(); paramnum++) - if (!validate_cpu_parameter(params[paramnum], devicelist[paramnum])) + if (!m_console.validate_cpu_parameter(params[paramnum], devicelist[paramnum])) return; for (int paramnum = 0; paramnum < params.size(); paramnum++) @@ -1564,7 +1173,7 @@ void debugger_commands::execute_suspend(const std::vector<std::string> ¶ms) execute_resume - Resume execution on CPU -------------------------------------------------*/ -void debugger_commands::execute_resume(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 (params.empty()) @@ -1594,7 +1203,7 @@ void debugger_commands::execute_resume(const std::vector<std::string> ¶ms) // validate parameters for (int paramnum = 0; paramnum < params.size(); paramnum++) - if (!validate_cpu_parameter(params[paramnum], devicelist[paramnum])) + if (!m_console.validate_cpu_parameter(params[paramnum], devicelist[paramnum])) return; for (int paramnum = 0; paramnum < params.size(); paramnum++) @@ -1609,7 +1218,7 @@ void debugger_commands::execute_resume(const std::vector<std::string> ¶ms) // execute_cpulist - list all CPUs //------------------------------------------------- -void debugger_commands::execute_cpulist(const std::vector<std::string> ¶ms) +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())) @@ -1620,20 +1229,29 @@ void debugger_commands::execute_cpulist(const std::vector<std::string> ¶ms) } } +//------------------------------------------------- +// 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(const std::vector<std::string> ¶ms) +void debugger_commands::execute_comment_add(const std::vector<std::string_view> ¶ms) { // param 1 is the address for the comment u64 address; - if (!validate_number_parameter(params[0], address)) + if (!m_console.validate_number_parameter(params[0], address)) return; // CPU parameter is implicit device_t *cpu; - if (!validate_cpu_parameter(std::string_view(), cpu)) + if (!m_console.validate_cpu_parameter(std::string_view(), cpu)) return; // make sure param 2 exists @@ -1644,7 +1262,8 @@ void debugger_commands::execute_comment_add(const std::vector<std::string> ¶ } // Now try adding the comment - cpu->debug()->comment_add(address, params[1].c_str(), 0x00ff0000); + std::string const text(params[1]); + cpu->debug()->comment_add(address, text.c_str(), 0x00ff0000); cpu->machine().debug_view().update_all(DVT_DISASSEMBLY); } @@ -1653,16 +1272,16 @@ void debugger_commands::execute_comment_add(const std::vector<std::string> ¶ execute_comment_del - remove a comment from an addr --------------------------------------------------------*/ -void debugger_commands::execute_comment_del(const std::vector<std::string> ¶ms) +void debugger_commands::execute_comment_del(const std::vector<std::string_view> ¶ms) { // param 1 can either be a command or the address for the comment u64 address; - if (!validate_number_parameter(params[0], address)) + if (!m_console.validate_number_parameter(params[0], address)) return; // CPU parameter is implicit device_t *cpu; - if (!validate_cpu_parameter(std::string_view(), cpu)) + if (!m_console.validate_cpu_parameter(std::string_view(), cpu)) return; // If it's a number, it must be an address @@ -1672,25 +1291,25 @@ void debugger_commands::execute_comment_del(const std::vector<std::string> ¶ } /** - * @fn void execute_comment_list(const std::vector<std::string> ¶ms) + * @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(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(const std::vector<std::string> ¶ms) + * @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(const std::vector<std::string> ¶ms) +void debugger_commands::execute_comment_commit(const std::vector<std::string_view> ¶ms) { execute_comment_add(params); execute_comment_save(params); @@ -1700,7 +1319,7 @@ void debugger_commands::execute_comment_commit(const std::vector<std::string> &p execute_comment - add a comment to a line -------------------------------------------------*/ -void debugger_commands::execute_comment_save(const std::vector<std::string> ¶ms) +void debugger_commands::execute_comment_save(const std::vector<std::string_view> ¶ms) { if (m_machine.debugger().cpu().comment_save()) m_console.printf("Comment successfully saved\n"); @@ -1710,7 +1329,7 @@ void debugger_commands::execute_comment_save(const std::vector<std::string> &par // TODO: add color hex editing capabilities for comments, see below for more info /** - * @fn void execute_comment_color(const std::vector<std::string> ¶ms) + * @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. @@ -1727,12 +1346,12 @@ void debugger_commands::execute_comment_save(const std::vector<std::string> &par command -------------------------------------------------*/ -void debugger_commands::execute_bpset(const std::vector<std::string> ¶ms) +void debugger_commands::execute_bpset(const std::vector<std::string_view> ¶ms) { // param 1 is the address/CPU u64 address; address_space *space; - if (!validate_target_address_parameter(params[0], AS_PROGRAM, space, address)) + if (!m_console.validate_target_address_parameter(params[0], AS_PROGRAM, space, address)) return; device_execute_interface const *execute; @@ -1751,12 +1370,12 @@ void debugger_commands::execute_bpset(const std::vector<std::string> ¶ms) // param 2 is the condition parsed_expression condition(debug->symtable()); - if (params.size() > 1 && !debug_command_parameter_expression(params[1], condition)) + if (params.size() > 1 && !m_console.validate_expression_parameter(params[1], condition)) return; // param 3 is the action - const char *action = nullptr; - if (params.size() > 2 && !debug_command_parameter_command(action = params[2].c_str())) + std::string_view action; + if (params.size() > 2 && !m_console.validate_command_parameter(action = params[2])) return; // set the breakpoint @@ -1770,7 +1389,7 @@ void debugger_commands::execute_bpset(const std::vector<std::string> ¶ms) clear command -------------------------------------------------*/ -void debugger_commands::execute_bpclear(const std::vector<std::string> ¶ms) +void debugger_commands::execute_bpclear(const std::vector<std::string_view> ¶ms) { if (params.empty()) // if no parameters, clear all { @@ -1799,7 +1418,7 @@ void debugger_commands::execute_bpclear(const std::vector<std::string> ¶ms) disable/enable commands -------------------------------------------------*/ -void debugger_commands::execute_bpdisenable(bool enable, const std::vector<std::string> ¶ms) +void debugger_commands::execute_bpdisenable(bool enable, const std::vector<std::string_view> ¶ms) { if (params.empty()) // if no parameters, disable/enable all { @@ -1828,7 +1447,7 @@ void debugger_commands::execute_bpdisenable(bool enable, const std::vector<std:: command -------------------------------------------------*/ -void debugger_commands::execute_bplist(const std::vector<std::string> ¶ms) +void debugger_commands::execute_bplist(const std::vector<std::string_view> ¶ms) { int printed = 0; std::string buffer; @@ -1857,7 +1476,7 @@ void debugger_commands::execute_bplist(const std::vector<std::string> ¶ms) if (!params.empty()) { device_t *cpu; - if (!validate_cpu_parameter(params[0], cpu)) + if (!m_console.validate_cpu_parameter(params[0], cpu)) return; apply(*cpu); if (!printed) @@ -1879,13 +1498,13 @@ void debugger_commands::execute_bplist(const std::vector<std::string> ¶ms) command -------------------------------------------------*/ -void debugger_commands::execute_wpset(int spacenum, const std::vector<std::string> ¶ms) +void debugger_commands::execute_wpset(int spacenum, const std::vector<std::string_view> ¶ms) { u64 address, length; address_space *space; // param 1 is the address/CPU - if (!validate_target_address_parameter(params[0], spacenum, space, address)) + if (!m_console.validate_target_address_parameter(params[0], spacenum, space, address)) return; device_execute_interface const *execute; @@ -1897,31 +1516,35 @@ void debugger_commands::execute_wpset(int spacenum, const std::vector<std::strin device_debug *const debug = space->device().debug(); // param 2 is the length - if (!validate_number_parameter(params[1], length)) + if (!m_console.validate_number_parameter(params[1], length)) return; // param 3 is the type read_or_write type; - if (!core_stricmp(params[2].c_str(), "r")) - type = read_or_write::READ; - else if (!core_stricmp(params[2].c_str(), "w")) - type = read_or_write::WRITE; - else if (!core_stricmp(params[2].c_str(), "rw") || !core_stricmp(params[2].c_str(), "wr")) - type = read_or_write::READWRITE; - else { - 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(debug->symtable()); - if (params.size() > 3 && !debug_command_parameter_expression(params[3], condition)) + if (params.size() > 3 && !m_console.validate_expression_parameter(params[3], condition)) return; // param 5 is the action - const char *action = nullptr; - if (params.size() > 4 && !debug_command_parameter_command(action = params[4].c_str())) + std::string_view action; + if (params.size() > 4 && !m_console.validate_command_parameter(action = params[4])) return; // set the watchpoint @@ -1935,7 +1558,7 @@ void debugger_commands::execute_wpset(int spacenum, const std::vector<std::strin clear command -------------------------------------------------*/ -void debugger_commands::execute_wpclear(const std::vector<std::string> ¶ms) +void debugger_commands::execute_wpclear(const std::vector<std::string_view> ¶ms) { if (params.empty()) // if no parameters, clear all { @@ -1964,7 +1587,7 @@ void debugger_commands::execute_wpclear(const std::vector<std::string> ¶ms) disable/enable commands -------------------------------------------------*/ -void debugger_commands::execute_wpdisenable(bool enable, const std::vector<std::string> ¶ms) +void debugger_commands::execute_wpdisenable(bool enable, const std::vector<std::string_view> ¶ms) { if (params.empty()) // if no parameters, disable/enable all { @@ -1993,7 +1616,7 @@ void debugger_commands::execute_wpdisenable(bool enable, const std::vector<std:: command -------------------------------------------------*/ -void debugger_commands::execute_wplist(const std::vector<std::string> ¶ms) +void debugger_commands::execute_wplist(const std::vector<std::string_view> ¶ms) { int printed = 0; std::string buffer; @@ -2034,7 +1657,7 @@ void debugger_commands::execute_wplist(const std::vector<std::string> ¶ms) if (!params.empty()) { device_t *cpu; - if (!validate_cpu_parameter(params[0], cpu)) + if (!m_console.validate_cpu_parameter(params[0], cpu)) return; apply(*cpu); if (!printed) @@ -2056,21 +1679,21 @@ void debugger_commands::execute_wplist(const std::vector<std::string> ¶ms) command -------------------------------------------------*/ -void debugger_commands::execute_rpset(const std::vector<std::string> ¶ms) +void debugger_commands::execute_rpset(const std::vector<std::string_view> ¶ms) { // CPU is implicit device_t *cpu; - if (!validate_cpu_parameter(std::string_view(), 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)) + if (params.size() > 0 && !m_console.validate_expression_parameter(params[0], condition)) return; // param 2 is the action - const char *action = nullptr; - if (params.size() > 1 && !debug_command_parameter_command(action = params[1].c_str())) + std::string_view action; + if (params.size() > 1 && !m_console.validate_command_parameter(action = params[1])) return; // set the registerpoint @@ -2084,7 +1707,7 @@ void debugger_commands::execute_rpset(const std::vector<std::string> ¶ms) clear command -------------------------------------------------*/ -void debugger_commands::execute_rpclear(const std::vector<std::string> ¶ms) +void debugger_commands::execute_rpclear(const std::vector<std::string_view> ¶ms) { if (params.empty()) // if no parameters, clear all { @@ -2113,7 +1736,7 @@ void debugger_commands::execute_rpclear(const std::vector<std::string> ¶ms) disable/enable commands -------------------------------------------------*/ -void debugger_commands::execute_rpdisenable(bool enable, const std::vector<std::string> ¶ms) +void debugger_commands::execute_rpdisenable(bool enable, const std::vector<std::string_view> ¶ms) { if (params.empty()) // if no parameters, disable/enable all { @@ -2137,12 +1760,154 @@ void debugger_commands::execute_rpdisenable(bool enable, const std::vector<std:: } +//------------------------------------------------- +// 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; + + // 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 + { + 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_epdisenable - execute the exception +// point disable/enable commands +//------------------------------------------------- + +void debugger_commands::execute_epdisenable(bool enable, const std::vector<std::string_view> ¶ms) +{ + 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"); + } +} + + +//------------------------------------------------- +// 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) + { + 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 (!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_rplist - execute the registerpoint list command -------------------------------------------------*/ -void debugger_commands::execute_rplist(const std::vector<std::string> ¶ms) +void debugger_commands::execute_rplist(const std::vector<std::string_view> ¶ms) { int printed = 0; std::string buffer; @@ -2157,7 +1922,7 @@ void debugger_commands::execute_rplist(const std::vector<std::string> ¶ms) 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() && *rp.action()) + if (!rp.action().empty()) buffer.append(string_format(" do %s", rp.action())); m_console.printf("%s\n", buffer); printed++; @@ -2168,7 +1933,7 @@ void debugger_commands::execute_rplist(const std::vector<std::string> ¶ms) if (!params.empty()) { device_t *cpu; - if (!validate_cpu_parameter(params[0], cpu)) + if (!m_console.validate_cpu_parameter(params[0], cpu)) return; apply(*cpu); if (!printed) @@ -2189,10 +1954,9 @@ void debugger_commands::execute_rplist(const std::vector<std::string> ¶ms) execute_statesave - execute the statesave command -------------------------------------------------*/ -void debugger_commands::execute_statesave(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"); } @@ -2201,10 +1965,9 @@ void debugger_commands::execute_statesave(const std::vector<std::string> ¶ms execute_stateload - execute the stateload command -------------------------------------------------*/ -void debugger_commands::execute_stateload(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_enumerator(m_machine.root_device())) @@ -2220,7 +1983,7 @@ void debugger_commands::execute_stateload(const std::vector<std::string> ¶ms execute_rewind - execute the rewind command -------------------------------------------------*/ -void debugger_commands::execute_rewind(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) @@ -2239,15 +2002,15 @@ void debugger_commands::execute_rewind(const std::vector<std::string> ¶ms) execute_save - execute the save command -------------------------------------------------*/ -void debugger_commands::execute_save(int spacenum, 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; // validate parameters - if (!validate_target_address_parameter(params[1], spacenum, space, offset)) + if (!m_console.validate_target_address_parameter(params[1], spacenum, space, offset)) return; - if (!validate_number_parameter(params[2], length)) + if (!m_console.validate_number_parameter(params[2], length)) return; // determine the addresses to write @@ -2256,7 +2019,8 @@ void debugger_commands::execute_save(int spacenum, const std::vector<std::string endoffset++; // open the file - FILE *const f = fopen(params[0].c_str(), "wb"); + 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]); @@ -2271,8 +2035,9 @@ void debugger_commands::execute_save(int spacenum, const std::vector<std::string 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; @@ -2280,8 +2045,9 @@ void debugger_commands::execute_save(int spacenum, const std::vector<std::string 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; @@ -2289,8 +2055,9 @@ void debugger_commands::execute_save(int spacenum, const std::vector<std::string 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; @@ -2298,8 +2065,9 @@ void debugger_commands::execute_save(int spacenum, const std::vector<std::string 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; @@ -2309,8 +2077,9 @@ void debugger_commands::execute_save(int spacenum, const std::vector<std::string 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; @@ -2326,17 +2095,17 @@ void debugger_commands::execute_save(int spacenum, const std::vector<std::string execute_saveregion - execute the save command on region memory -------------------------------------------------*/ -void debugger_commands::execute_saveregion(const std::vector<std::string> ¶ms) +void debugger_commands::execute_saveregion(const std::vector<std::string_view> ¶ms) { u64 offset, length; memory_region *region; // validate parameters - if (!validate_number_parameter(params[1], offset)) + 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 (!validate_memory_region_parameter(params[3], region)) + if (!m_console.validate_memory_region_parameter(params[3], region)) return; if (offset >= region->bytes()) @@ -2348,7 +2117,8 @@ void debugger_commands::execute_saveregion(const std::vector<std::string> ¶m length = region->bytes() - offset; /* open the file */ - FILE *f = fopen(params[0].c_str(), "wb"); + 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]); @@ -2365,20 +2135,21 @@ void debugger_commands::execute_saveregion(const std::vector<std::string> ¶m execute_load - execute the load command -------------------------------------------------*/ -void debugger_commands::execute_load(int spacenum, 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_target_address_parameter(params[1], spacenum, space, offset)) + if (!m_console.validate_target_address_parameter(params[1], spacenum, space, offset)) return; - if (params.size() > 2 && !validate_number_parameter(params[2], length)) + 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]); @@ -2412,8 +2183,9 @@ void debugger_commands::execute_load(int spacenum, const std::vector<std::string 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: @@ -2422,8 +2194,9 @@ void debugger_commands::execute_load(int spacenum, const std::vector<std::string 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: @@ -2432,8 +2205,9 @@ void debugger_commands::execute_load(int spacenum, const std::vector<std::string 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: @@ -2442,8 +2216,9 @@ void debugger_commands::execute_load(int spacenum, const std::vector<std::string 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: @@ -2454,8 +2229,9 @@ void debugger_commands::execute_load(int spacenum, const std::vector<std::string 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; } @@ -2473,17 +2249,17 @@ void debugger_commands::execute_load(int spacenum, const std::vector<std::string execute_loadregion - execute the load command on region memory -------------------------------------------------*/ -void debugger_commands::execute_loadregion(const std::vector<std::string> ¶ms) +void debugger_commands::execute_loadregion(const std::vector<std::string_view> ¶ms) { u64 offset, length; memory_region *region; // validate parameters - if (!validate_number_parameter(params[1], offset)) + 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 (!validate_memory_region_parameter(params[3], region)) + if (!m_console.validate_memory_region_parameter(params[3], region)) return; if (offset >= region->bytes()) @@ -2495,7 +2271,8 @@ void debugger_commands::execute_loadregion(const std::vector<std::string> ¶m length = region->bytes() - offset; // open the file - FILE *const f = fopen(params[0].c_str(), "rb"); + 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]); @@ -2521,28 +2298,28 @@ void debugger_commands::execute_loadregion(const std::vector<std::string> ¶m execute_dump - execute the dump command -------------------------------------------------*/ -void debugger_commands::execute_dump(int spacenum, const std::vector<std::string> ¶ms) +void debugger_commands::execute_dump(int spacenum, const std::vector<std::string_view> ¶ms) { // validate parameters address_space *space; u64 offset; - if (!validate_target_address_parameter(params[1], spacenum, space, 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)) + if (params.size() > 3 && !m_console.validate_number_parameter(params[3], width)) return; bool ascii = true; - if (params.size() > 4 && !validate_boolean_parameter(params[4], ascii)) + 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(); @@ -2573,7 +2350,8 @@ void debugger_commands::execute_dump(int spacenum, const std::vector<std::string offset = offset & space->addrmask(); // open the file - FILE *const f = fopen(params[0].c_str(), "w"); + 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]); @@ -2603,21 +2381,22 @@ void debugger_commands::execute_dump(int spacenum, const std::vector<std::string 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; } } @@ -2637,22 +2416,23 @@ void debugger_commands::execute_dump(int spacenum, const std::vector<std::string 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++) { @@ -2682,23 +2462,23 @@ void debugger_commands::execute_dump(int spacenum, const std::vector<std::string // execute_strdump - execute the strdump command //------------------------------------------------- -void debugger_commands::execute_strdump(int spacenum, const std::vector<std::string> ¶ms) +void debugger_commands::execute_strdump(int spacenum, const std::vector<std::string_view> ¶ms) { // validate parameters u64 offset; - if (!validate_number_parameter(params[1], offset)) + if (!m_console.validate_number_parameter(params[1], offset)) return; u64 length; - if (!validate_number_parameter(params[2], length)) + if (!m_console.validate_number_parameter(params[2], length)) return; u64 term = 0; - if (params.size() > 3 && !validate_number_parameter(params[3], term)) + if (params.size() > 3 && !m_console.validate_number_parameter(params[3], term)) return; address_space *space; - if (!validate_device_space_parameter((params.size() > 4) ? params[4] : std::string_view(), spacenum, space)) + if (!m_console.validate_device_space_parameter((params.size() > 4) ? params[4] : std::string_view(), spacenum, space)) return; // further validation @@ -2709,7 +2489,8 @@ void debugger_commands::execute_strdump(int spacenum, const std::vector<std::str } // open the file - FILE *f = fopen(params[0].c_str(), "w"); + std::string filename(params[0]); + FILE *f = fopen(filename.c_str(), "w"); if (!f) { m_console.printf("Error opening file '%s'\n", params[0]); @@ -2747,28 +2528,29 @@ void debugger_commands::execute_strdump(int spacenum, const std::vector<std::str // get the character data u64 data = 0; offs_t curaddr = offset; - 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 1: - data = space->read_byte(curaddr); + data = tspace->read_byte(curaddr); break; case 2: - data = space->read_word(curaddr); + data = tspace->read_word(curaddr); if (be) data = swapendian_int16(data); break; case 4: - data = space->read_dword(curaddr); + data = tspace->read_dword(curaddr); if (be) data = swapendian_int32(data); break; case 8: - data = space->read_qword(curaddr); + data = tspace->read_qword(curaddr); if (be) data = swapendian_int64(data); break; @@ -2858,7 +2640,7 @@ void debugger_commands::execute_strdump(int spacenum, const std::vector<std::str cheats -------------------------------------------------*/ -void debugger_commands::execute_cheatrange(bool init, const std::vector<std::string> ¶ms) +void debugger_commands::execute_cheatrange(bool init, const std::vector<std::string_view> ¶ms) { address_space *space = m_cheat.space; if (!space && !init) @@ -2875,7 +2657,7 @@ void debugger_commands::execute_cheatrange(bool init, const std::vector<std::str // first argument is sign/size/swap flags if (!params.empty()) { - std::string const &srtpnt = params[0]; + std::string_view const &srtpnt = params[0]; if (!srtpnt.empty()) { width = 1; @@ -2928,7 +2710,7 @@ void debugger_commands::execute_cheatrange(bool init, const std::vector<std::str } // fourth argument is device/space - if (!validate_device_space_parameter((params.size() > 3) ? params[3] : std::string_view(), -1, space)) + if (!m_console.validate_device_space_parameter((params.size() > 3) ? params[3] : std::string_view(), -1, space)) return; } @@ -2938,9 +2720,9 @@ void debugger_commands::execute_cheatrange(bool init, const std::vector<std::str { // validate parameters u64 offset, length; - if (!validate_number_parameter(params[init ? 1 : 0], offset)) + if (!m_console.validate_number_parameter(params[init ? 1 : 0], offset)) return; - if (!validate_number_parameter(params[init ? 2 : 1], length)) + if (!m_console.validate_number_parameter(params[init ? 2 : 1], length)) return; // force region to the specified range @@ -3027,7 +2809,7 @@ void debugger_commands::execute_cheatrange(bool init, const std::vector<std::str execute_cheatnext - execute the search -------------------------------------------------*/ -void debugger_commands::execute_cheatnext(bool initial, const std::vector<std::string> ¶ms) +void debugger_commands::execute_cheatnext(bool initial, const std::vector<std::string_view> ¶ms) { enum { @@ -3055,36 +2837,40 @@ void debugger_commands::execute_cheatnext(bool initial, const std::vector<std::s } u64 comp_value = 0; - if (params.size() > 1 && !validate_number_parameter(params[1], comp_value)) + if (params.size() > 1 && !m_console.validate_number_parameter(params[1], comp_value)) return; comp_value = m_cheat.sign_extend(comp_value); // decode condition u8 condition; - if (!core_stricmp(params[0].c_str(), "all")) - condition = CHEAT_ALL; - else if (!core_stricmp(params[0].c_str(), "equal") || !core_stricmp(params[0].c_str(), "eq")) - condition = (params.size() > 1) ? CHEAT_EQUALTO : CHEAT_EQUAL; - else if (!core_stricmp(params[0].c_str(), "notequal") || !core_stricmp(params[0].c_str(), "ne")) - condition = (params.size() > 1) ? CHEAT_NOTEQUALTO : CHEAT_NOTEQUAL; - else if (!core_stricmp(params[0].c_str(), "decrease") || !core_stricmp(params[0].c_str(), "de") || params[0] == "-") - condition = (params.size() > 1) ? CHEAT_DECREASEOF : CHEAT_DECREASE; - else if (!core_stricmp(params[0].c_str(), "increase") || !core_stricmp(params[0].c_str(), "in") || params[0] == "+") - condition = (params.size() > 1) ? CHEAT_INCREASEOF : CHEAT_INCREASE; - else if (!core_stricmp(params[0].c_str(), "decreaseorequal") || !core_stricmp(params[0].c_str(), "deeq")) - condition = CHEAT_DECREASE_OR_EQUAL; - else if (!core_stricmp(params[0].c_str(), "increaseorequal") || !core_stricmp(params[0].c_str(), "ineq")) - condition = CHEAT_INCREASE_OR_EQUAL; - else if (!core_stricmp(params[0].c_str(), "smallerof") || !core_stricmp(params[0].c_str(), "lt") || params[0] == "<") - condition = CHEAT_SMALLEROF; - else if (!core_stricmp(params[0].c_str(), "greaterof") || !core_stricmp(params[0].c_str(), "gt") || params[0] == ">") - condition = CHEAT_GREATEROF; - else if (!core_stricmp(params[0].c_str(), "changedby") || !core_stricmp(params[0].c_str(), "ch") || params[0] == "~") - condition = CHEAT_CHANGEDBY; - else { - 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++; @@ -3191,7 +2977,7 @@ void debugger_commands::execute_cheatnext(bool initial, const std::vector<std::s } if (active_cheat <= 5) - execute_cheatlist(std::vector<std::string>()); + execute_cheatlist(std::vector<std::string_view>()); m_console.printf("%u cheats found\n", active_cheat); } @@ -3201,7 +2987,7 @@ void debugger_commands::execute_cheatnext(bool initial, const std::vector<std::s execute_cheatlist - show a list of active cheat -------------------------------------------------*/ -void debugger_commands::execute_cheatlist(const std::vector<std::string> ¶ms) +void debugger_commands::execute_cheatlist(const std::vector<std::string_view> ¶ms) { address_space *const space = m_cheat.space; if (!space) @@ -3213,7 +2999,8 @@ void debugger_commands::execute_cheatlist(const std::vector<std::string> ¶ms 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]); @@ -3318,7 +3105,7 @@ void debugger_commands::execute_cheatlist(const std::vector<std::string> ¶ms execute_cheatundo - undo the last search -------------------------------------------------*/ -void debugger_commands::execute_cheatundo(const std::vector<std::string> ¶ms) +void debugger_commands::execute_cheatundo(const std::vector<std::string_view> ¶ms) { if (m_cheat.undo > 0) { @@ -3347,15 +3134,15 @@ void debugger_commands::execute_cheatundo(const std::vector<std::string> ¶ms execute_find - execute the find command -------------------------------------------------*/ -void debugger_commands::execute_find(int spacenum, const std::vector<std::string> ¶ms) +void debugger_commands::execute_find(int spacenum, const std::vector<std::string_view> ¶ms) { u64 offset, length; address_space *space; // validate parameters - if (!validate_target_address_parameter(params[0], spacenum, space, offset)) + if (!m_console.validate_target_address_parameter(params[0], spacenum, space, offset)) return; - if (!validate_number_parameter(params[1], length)) + if (!m_console.validate_number_parameter(params[1], length)) return; // further validation @@ -3371,11 +3158,11 @@ void debugger_commands::execute_find(int spacenum, const std::vector<std::string int data_count = 0; for (int i = 2; i < params.size(); i++) { - char const *pdata = params[i].c_str(); - auto const pdatalen = params[i].length() - 1; + std::string_view pdata = params[i]; - if (pdata[0] == '"' && pdata[pdatalen] == '"') // check for a string + if (!pdata.empty() && pdata.front() == '"' && pdata.back() == '"') // check for a string { + auto const pdatalen = params[i].length() - 1; for (int j = 1; j < pdatalen; j++) { data_to_find[data_count] = pdata[j]; @@ -3386,17 +3173,20 @@ void debugger_commands::execute_find(int spacenum, const std::vector<std::string { // 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, "?")) + if (pdata == "?") data_size[data_count++] |= 0x10; // otherwise, validate as a number - else if (!validate_number_parameter(pdata, data_to_find[data_count++])) + else if (!m_console.validate_number_parameter(pdata, data_to_find[data_count++])) return; } } @@ -3414,36 +3204,37 @@ void debugger_commands::execute_find(int spacenum, const std::vector<std::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: address &= space->logaddrmask(); - if (memory.translate(space->spacenum(), TRANSLATE_READ_DEBUG, address)) - match = space->read_byte(address) == u8(data_to_find[j]); + 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(), TRANSLATE_READ_DEBUG, address)) - match = space->read_word_unaligned(address) == u16(data_to_find[j]); + 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(), TRANSLATE_READ_DEBUG, address)) - match = space->read_dword_unaligned(address) == u32(data_to_find[j]); + 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(), TRANSLATE_READ_DEBUG, address)) - match = space->read_qword_unaligned(address) == u64(data_to_find[j]); + 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; @@ -3473,15 +3264,15 @@ void debugger_commands::execute_find(int spacenum, const std::vector<std::string // execute_fill - execute the fill command //------------------------------------------------- -void debugger_commands::execute_fill(int spacenum, const std::vector<std::string> ¶ms) +void debugger_commands::execute_fill(int spacenum, const std::vector<std::string_view> ¶ms) { u64 offset, length; address_space *space; // validate parameters - if (!validate_target_address_parameter(params[0], spacenum, space, offset)) + if (!m_console.validate_target_address_parameter(params[0], spacenum, space, offset)) return; - if (!validate_number_parameter(params[1], length)) + if (!m_console.validate_number_parameter(params[1], length)) return; // further validation @@ -3496,12 +3287,12 @@ void debugger_commands::execute_fill(int spacenum, const std::vector<std::string 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() == '"') { + auto const pdatalen = pdata.length() - 1; for (int j = 1; j < pdatalen; j++) { fill_data[data_count] = pdata[j]; @@ -3514,13 +3305,16 @@ void debugger_commands::execute_fill(int spacenum, const std::vector<std::string { // check for a 'b','w','d',or 'q' prefix fill_data_size[data_count] = cur_data_size; - if (tolower(u8(pdata[0])) == 'b' && pdata[1] == '.') { fill_data_size[data_count] = cur_data_size = 1; pdata += 2; } - if (tolower(u8(pdata[0])) == 'w' && pdata[1] == '.') { fill_data_size[data_count] = cur_data_size = 2; pdata += 2; } - if (tolower(u8(pdata[0])) == 'd' && pdata[1] == '.') { fill_data_size[data_count] = cur_data_size = 4; pdata += 2; } - if (tolower(u8(pdata[0])) == 'q' && pdata[1] == '.') { fill_data_size[data_count] = cur_data_size = 8; pdata += 2; } + 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 (!validate_number_parameter(pdata, fill_data[data_count++])) + if (!m_console.validate_number_parameter(pdata, fill_data[data_count++])) return; } } @@ -3537,7 +3331,8 @@ void debugger_commands::execute_fill(int spacenum, const std::vector<std::string for (int j = 0; j < data_count; j++) { offs_t address = space->byte_to_address(offset) & space->logaddrmask(); - if (!memory.translate(space->spacenum(), TRANSLATE_WRITE_DEBUG, address)) + 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; @@ -3546,19 +3341,19 @@ void debugger_commands::execute_fill(int spacenum, const std::vector<std::string switch (fill_data_size[j]) { case 1: - space->write_byte(address, fill_data[j]); + tspace->write_byte(address, fill_data[j]); break; case 2: - space->write_word_unaligned(address, fill_data[j]); + tspace->write_word_unaligned(address, fill_data[j]); break; case 4: - space->write_dword_unaligned(address, fill_data[j]); + tspace->write_dword_unaligned(address, fill_data[j]); break; case 8: - space->read_qword_unaligned(address, fill_data[j]); + tspace->read_qword_unaligned(address, fill_data[j]); break; } offset += fill_data_size[j]; @@ -3578,20 +3373,20 @@ void debugger_commands::execute_fill(int spacenum, const std::vector<std::string execute_dasm - execute the dasm command -------------------------------------------------*/ -void debugger_commands::execute_dasm(const std::vector<std::string> ¶ms) +void debugger_commands::execute_dasm(const std::vector<std::string_view> ¶ms) { u64 offset, length; bool bytes = true; address_space *space; // validate parameters - if (!validate_number_parameter(params[1], offset)) + 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_boolean_parameter(params[3], bytes)) + if (params.size() > 3 && !m_console.validate_boolean_parameter(params[3], bytes)) return; - if (!validate_device_space_parameter(params.size() > 4 ? params[4] : std::string_view(), 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 @@ -3637,7 +3432,8 @@ void debugger_commands::execute_dasm(const std::vector<std::string> ¶ms) } /* 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]); @@ -3676,32 +3472,32 @@ void debugger_commands::execute_dasm(const std::vector<std::string> ¶ms) trace over and trace info -------------------------------------------------*/ -void debugger_commands::execute_trace(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; - const char *mode; - std::string filename = params[0]; + std::string filename(params[0]); // replace macros strreplace(filename, "{game}", m_machine.basename()); // validate parameters - if (!validate_cpu_parameter(params.size() > 1 ? params[1] : std::string_view(), cpu)) + 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 { @@ -3710,24 +3506,27 @@ void debugger_commands::execute_trace(const std::vector<std::string> ¶ms, bo } } } - 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 - FILE *f = nullptr; - if (core_stricmp(filename.c_str(), "off") != 0) + std::unique_ptr<std::ofstream> f; + using namespace std::literals; + if (!util::streqlower(filename, "off"sv)) { - mode = "w"; + std::ios_base::openmode mode = std::ios_base::out; // opening for append? if ((filename[0] == '>') && (filename[1] == '>')) { - mode = "a"; + mode |= std::ios_base::ate; filename = filename.substr(2); } + else + mode |= 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]); return; @@ -3735,8 +3534,9 @@ void debugger_commands::execute_trace(const std::vector<std::string> ¶ms, bo } // do it - cpu->debug()->trace(f, trace_over, detect_loops, logerror, action); - if (f) + 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()); @@ -3747,7 +3547,7 @@ void debugger_commands::execute_trace(const std::vector<std::string> ¶ms, bo execute_traceflush - execute the trace flush command -------------------------------------------------*/ -void debugger_commands::execute_traceflush(const std::vector<std::string> ¶ms) +void debugger_commands::execute_traceflush(const std::vector<std::string_view> ¶ms) { m_machine.debugger().cpu().flush_traces(); } @@ -3757,15 +3557,15 @@ void debugger_commands::execute_traceflush(const std::vector<std::string> ¶m execute_history - execute the history command -------------------------------------------------*/ -void debugger_commands::execute_history(const std::vector<std::string> ¶ms) +void debugger_commands::execute_history(const std::vector<std::string_view> ¶ms) { // validate parameters device_t *device; - if (!validate_cpu_parameter(!params.empty() ? params[0] : std::string_view(), 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 @@ -3782,13 +3582,13 @@ void debugger_commands::execute_history(const std::vector<std::string> ¶ms) } // loop over lines - debug_disasm_buffer buffer(*device); std::string instruction; for (int index = int(unsigned(count)); index > 0; index--) { 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; @@ -3805,16 +3605,16 @@ void debugger_commands::execute_history(const std::vector<std::string> ¶ms) execute_trackpc - execute the trackpc command -------------------------------------------------*/ -void debugger_commands::execute_trackpc(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] : std::string_view(), cpu)) + if (!m_console.validate_cpu_parameter((params.size() > 1) ? params[1] : std::string_view(), cpu)) return; const device_state_interface *state; @@ -3826,7 +3626,7 @@ void debugger_commands::execute_trackpc(const std::vector<std::string> ¶ms) // 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); @@ -3854,11 +3654,11 @@ void debugger_commands::execute_trackpc(const std::vector<std::string> ¶ms) execute_trackmem - execute the trackmem command -------------------------------------------------*/ -void debugger_commands::execute_trackmem(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) @@ -3866,17 +3666,17 @@ void debugger_commands::execute_trackmem(const std::vector<std::string> ¶ms) if (params.size() > 1) cpuparam = params[1]; device_t *cpu = nullptr; - if (!validate_cpu_parameter(cpuparam, 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_device_space_parameter(cpuparam, 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 @@ -3892,17 +3692,18 @@ void debugger_commands::execute_trackmem(const std::vector<std::string> ¶ms) execute_pcatmem - execute the pcatmem command -------------------------------------------------*/ -void debugger_commands::execute_pcatmem(int spacenum, const std::vector<std::string> ¶ms) +void debugger_commands::execute_pcatmem(int spacenum, const std::vector<std::string_view> ¶ms) { // Gather the required target address/space parameter u64 address; address_space *space; - if (!validate_target_address_parameter(params[0], spacenum, space, address)) + if (!m_console.validate_target_address_parameter(params[0], spacenum, space, address)) return; // Translate the address offs_t a = address & space->logaddrmask(); - if (!space->device().memory().translate(space->spacenum(), TRANSLATE_READ_DEBUG, a)) + 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; @@ -3914,19 +3715,19 @@ void debugger_commands::execute_pcatmem(int spacenum, const std::vector<std::str switch (space->data_width()) { case 8: - data = space->read_byte(a); + data = tspace->read_byte(a); break; case 16: - data = space->read_word_unaligned(a); + data = tspace->read_word_unaligned(a); break; case 32: - data = space->read_dword_unaligned(a); + data = tspace->read_dword_unaligned(a); break; case 64: - data = space->read_qword_unaligned(a); + data = tspace->read_qword_unaligned(a); break; } @@ -3943,7 +3744,7 @@ void debugger_commands::execute_pcatmem(int spacenum, const std::vector<std::str execute_snap - execute the snapshot command -------------------------------------------------*/ -void debugger_commands::execute_snap(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()) @@ -3955,8 +3756,9 @@ void debugger_commands::execute_snap(const std::vector<std::string> ¶ms) /* 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_enumerator iter(m_machine.root_device()); screen_device *screen = iter.byindex(scrnum); @@ -3967,7 +3769,7 @@ void debugger_commands::execute_snap(const std::vector<std::string> ¶ms) 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); @@ -3975,12 +3777,12 @@ void debugger_commands::execute_snap(const std::vector<std::string> ¶ms) if (filerr) { - m_console.printf("Error creating file '%s' (%s:%d %s)\n", filename, filerr.category().name(), filerr.value(), filerr.message()); + 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]); } } @@ -3989,9 +3791,10 @@ void debugger_commands::execute_snap(const std::vector<std::string> ¶ms) execute_source - execute the source command -------------------------------------------------*/ -void debugger_commands::execute_source(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()); } @@ -3999,27 +3802,29 @@ void debugger_commands::execute_source(const std::vector<std::string> ¶ms) execute_map - execute the map command -------------------------------------------------*/ -void debugger_commands::execute_map(int spacenum, const std::vector<std::string> ¶ms) +void debugger_commands::execute_map(int spacenum, const std::vector<std::string_view> ¶ms) { // validate parameters u64 address; address_space *space; - if (!validate_target_address_parameter(params[0], spacenum, space, address)) + if (!m_console.validate_target_address_parameter(params[0], spacenum, space, address)) return; + address &= space->logaddrmask(); // do the translation first - for (int intention = TRANSLATE_READ_DEBUG; intention <= TRANSLATE_FETCH_DEBUG; intention++) + for (int intention = device_memory_interface::TR_READ; intention <= device_memory_interface::TR_FETCH; intention++) { static const char *const intnames[] = { "Read", "Write", "Fetch" }; - offs_t 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 @@ -4032,14 +3837,15 @@ void debugger_commands::execute_map(int spacenum, const std::vector<std::string> execute_memdump - execute the memdump command -------------------------------------------------*/ -void debugger_commands::execute_memdump(const std::vector<std::string> ¶ms) +void debugger_commands::execute_memdump(const std::vector<std::string_view> ¶ms) { device_t *root = &m_machine.root_device(); - if ((params.size() >= 2) && !validate_device_parameter(params[1], root)) + if ((params.size() >= 2) && !m_console.validate_device_parameter(params[1], root)) return; - char const *const filename = params.empty() ? "memdump.log" : params[0].c_str(); - FILE *const file = fopen(filename, "w"); + 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); @@ -4098,7 +3904,7 @@ void debugger_commands::execute_memdump(const std::vector<std::string> ¶ms) execute_symlist - execute the symlist command -------------------------------------------------*/ -void debugger_commands::execute_symlist(const std::vector<std::string> ¶ms) +void debugger_commands::execute_symlist(const std::vector<std::string_view> ¶ms) { const char *namelist[1000]; symbol_table *symtable; @@ -4108,7 +3914,7 @@ void debugger_commands::execute_symlist(const std::vector<std::string> ¶ms) { // validate parameters device_t *cpu; - if (!validate_cpu_parameter(params[0], cpu)) + if (!m_console.validate_cpu_parameter(params[0], cpu)) return; symtable = &cpu->debug()->symtable(); m_console.printf("CPU '%s' symbols:\n", cpu->tag()); @@ -4160,7 +3966,7 @@ void debugger_commands::execute_symlist(const std::vector<std::string> ¶ms) execute_softreset - execute the softreset command -------------------------------------------------*/ -void debugger_commands::execute_softreset(const std::vector<std::string> ¶ms) +void debugger_commands::execute_softreset(const std::vector<std::string_view> ¶ms) { m_machine.schedule_soft_reset(); } @@ -4170,7 +3976,7 @@ void debugger_commands::execute_softreset(const std::vector<std::string> ¶ms execute_hardreset - execute the hardreset command -------------------------------------------------*/ -void debugger_commands::execute_hardreset(const std::vector<std::string> ¶ms) +void debugger_commands::execute_hardreset(const std::vector<std::string_view> ¶ms) { m_machine.schedule_hard_reset(); } @@ -4180,7 +3986,7 @@ void debugger_commands::execute_hardreset(const std::vector<std::string> ¶ms mounted files -------------------------------------------------*/ -void debugger_commands::execute_images(const std::vector<std::string> ¶ms) +void debugger_commands::execute_images(const std::vector<std::string_view> ¶ms) { image_interface_enumerator iter(m_machine.root_device()); for (device_image_interface &img : iter) @@ -4210,16 +4016,25 @@ void debugger_commands::execute_images(const std::vector<std::string> ¶ms) execute_mount - execute the image mount command -------------------------------------------------*/ -void debugger_commands::execute_mount(const std::vector<std::string> ¶ms) +void debugger_commands::execute_mount(const std::vector<std::string_view> ¶ms) { for (device_image_interface &img : image_interface_enumerator(m_machine.root_device())) { 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]); + } + else + { + m_console.printf( + "Unable to mount file %s on %s: %s\n", + params[1], + params[0], + !msg.empty() ? msg : err.message()); + } return; } } @@ -4230,7 +4045,7 @@ void debugger_commands::execute_mount(const std::vector<std::string> ¶ms) execute_unmount - execute the image unmount command -------------------------------------------------*/ -void debugger_commands::execute_unmount(const std::vector<std::string> ¶ms) +void debugger_commands::execute_unmount(const std::vector<std::string_view> ¶ms) { for (device_image_interface &img : image_interface_enumerator(m_machine.root_device())) { @@ -4257,9 +4072,9 @@ void debugger_commands::execute_unmount(const std::vector<std::string> ¶ms) natural keyboard input -------------------------------------------------*/ -void debugger_commands::execute_input(const std::vector<std::string> ¶ms) +void debugger_commands::execute_input(const std::vector<std::string_view> ¶ms) { - m_machine.natkeyboard().post_coded(params[0].c_str()); + m_machine.natkeyboard().post_coded(params[0]); } @@ -4268,15 +4083,15 @@ void debugger_commands::execute_input(const std::vector<std::string> ¶ms) keyboard codes -------------------------------------------------*/ -void debugger_commands::execute_dumpkbd(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); diff --git a/src/emu/debug/debugcmd.h b/src/emu/debug/debugcmd.h index fd0bcea673a..134f838f8a7 100644 --- a/src/emu/debug/debugcmd.h +++ b/src/emu/debug/debugcmd.h @@ -24,27 +24,6 @@ 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(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); - private: struct global_entry { @@ -87,11 +66,6 @@ private: u8 disabled = 0U; }; - device_t &get_device_search_base(std::string_view ¶m); - device_t *get_cpu_by_index(u64 cpunum); - bool debug_command_parameter_expression(std::string_view param, parsed_expression &result); - bool debug_command_parameter_command(const char *param); - bool cheat_address_is_valid(address_space &space, offs_t address); u64 get_cpunum(); @@ -99,86 +73,93 @@ private: u64 global_get(global_entry *global); void global_set(global_entry *global, u64 value); - int mini_printf(char *buffer, const char *format, int params, u64 *param); + bool mini_printf(std::ostream &stream, const std::vector<std::string_view> ¶ms); template <typename T> - void execute_index_command(std::vector<std::string> const ¶ms, T &&apply, char const *unused_message); - - void execute_help(const std::vector<std::string> ¶ms); - void execute_print(const std::vector<std::string> ¶ms); - void execute_printf(const std::vector<std::string> ¶ms); - void execute_logerror(const std::vector<std::string> ¶ms); - void execute_tracelog(const std::vector<std::string> ¶ms); - void execute_tracesym(const std::vector<std::string> ¶ms); - void execute_cls(const std::vector<std::string> ¶ms); - void execute_quit(const std::vector<std::string> ¶ms); - void execute_do(const std::vector<std::string> ¶ms); - void execute_step(const std::vector<std::string> ¶ms); - void execute_over(const std::vector<std::string> ¶ms); - void execute_out(const std::vector<std::string> ¶ms); - void execute_go(const std::vector<std::string> ¶ms); - void execute_go_vblank(const std::vector<std::string> ¶ms); - void execute_go_interrupt(const std::vector<std::string> ¶ms); - void execute_go_exception(const std::vector<std::string> ¶ms); - void execute_go_time(const std::vector<std::string> ¶ms); - void execute_go_privilege(const std::vector<std::string> ¶ms); - void execute_focus(const std::vector<std::string> ¶ms); - void execute_ignore(const std::vector<std::string> ¶ms); - void execute_observe(const std::vector<std::string> ¶ms); - void execute_suspend(const std::vector<std::string> ¶ms); - void execute_resume(const std::vector<std::string> ¶ms); - void execute_next(const std::vector<std::string> ¶ms); - void execute_cpulist(const std::vector<std::string> ¶ms); - void execute_comment_add(const std::vector<std::string> ¶ms); - void execute_comment_del(const std::vector<std::string> ¶ms); - void execute_comment_save(const std::vector<std::string> ¶ms); - void execute_comment_list(const std::vector<std::string> ¶ms); - void execute_comment_commit(const std::vector<std::string> ¶ms); - void execute_bpset(const std::vector<std::string> ¶ms); - void execute_bpclear(const std::vector<std::string> ¶ms); - void execute_bpdisenable(bool enable, const std::vector<std::string> ¶ms); - void execute_bplist(const std::vector<std::string> ¶ms); - void execute_wpset(int spacenum, const std::vector<std::string> ¶ms); - void execute_wpclear(const std::vector<std::string> ¶ms); - void execute_wpdisenable(bool enable, const std::vector<std::string> ¶ms); - void execute_wplist(const std::vector<std::string> ¶ms); - void execute_rpset(const std::vector<std::string> ¶ms); - void execute_rpclear(const std::vector<std::string> ¶ms); - void execute_rpdisenable(bool enable, const std::vector<std::string> ¶ms); - void execute_rplist(const std::vector<std::string> ¶ms); - void execute_statesave(const std::vector<std::string> ¶ms); - void execute_stateload(const std::vector<std::string> ¶ms); - void execute_rewind(const std::vector<std::string> ¶ms); - void execute_save(int spacenum, const std::vector<std::string> ¶ms); - void execute_saveregion(const std::vector<std::string> ¶ms); - void execute_load(int spacenum, const std::vector<std::string> ¶ms); - void execute_loadregion(const std::vector<std::string> ¶ms); - void execute_dump(int spacenum, const std::vector<std::string> ¶ms); - void execute_strdump(int spacenum, const std::vector<std::string> ¶ms); - void execute_cheatrange(bool init, const std::vector<std::string> ¶ms); - void execute_cheatnext(bool initial, const std::vector<std::string> ¶ms); - void execute_cheatlist(const std::vector<std::string> ¶ms); - void execute_cheatundo(const std::vector<std::string> ¶ms); - void execute_dasm(const std::vector<std::string> ¶ms); - void execute_find(int spacenum, const std::vector<std::string> ¶ms); - void execute_fill(int spacenum, const std::vector<std::string> ¶ms); - void execute_trace(const std::vector<std::string> ¶ms, bool trace_over); - void execute_traceflush(const std::vector<std::string> ¶ms); - void execute_history(const std::vector<std::string> ¶ms); - void execute_trackpc(const std::vector<std::string> ¶ms); - void execute_trackmem(const std::vector<std::string> ¶ms); - void execute_pcatmem(int spacenum, const std::vector<std::string> ¶ms); - void execute_snap(const std::vector<std::string> ¶ms); - void execute_source(const std::vector<std::string> ¶ms); - void execute_map(int spacenum, const std::vector<std::string> ¶ms); - void execute_memdump(const std::vector<std::string> ¶ms); - void execute_symlist(const std::vector<std::string> ¶ms); - void execute_softreset(const std::vector<std::string> ¶ms); - void execute_hardreset(const std::vector<std::string> ¶ms); - void execute_images(const std::vector<std::string> ¶ms); - void execute_mount(const std::vector<std::string> ¶ms); - void execute_unmount(const std::vector<std::string> ¶ms); - void execute_input(const std::vector<std::string> ¶ms); - void execute_dumpkbd(const std::vector<std::string> ¶ms); + 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_console& m_console; diff --git a/src/emu/debug/debugcon.cpp b/src/emu/debug/debugcon.cpp index 32a4888c285..ec5bc154751 100644 --- a/src/emu/debug/debugcon.cpp +++ b/src/emu/debug/debugcon.cpp @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:Aaron Giles, Vas Crabb /********************************************************************* debugcon.cpp @@ -16,6 +16,8 @@ #include "textbuf.h" #include "debugger.h" +#include "fileio.h" +#include "main.h" #include "corestr.h" @@ -86,10 +88,14 @@ debugger_console::debugger_console(running_machine &machine) } } +debugger_console::~debugger_console() +{ +} -/*------------------------------------------------- - exit - frees the console system --------------------------------------------------*/ + +//------------------------------------------------- +// exit - frees the console system +//------------------------------------------------- void debugger_console::exit() { @@ -114,41 +120,39 @@ void debugger_console::exit() inline bool debugger_console::debug_command::compare::operator()(const debug_command &a, const debug_command &b) const { - return core_stricmp(a.command.c_str(), b.command.c_str()) < 0; + return a.command < b.command; } inline bool debugger_console::debug_command::compare::operator()(const char *a, const debug_command &b) const { - return core_stricmp(a, b.command.c_str()) < 0; + return strcmp(a, b.command.c_str()) < 0; } inline bool debugger_console::debug_command::compare::operator()(const debug_command &a, const char *b) const { - return core_stricmp(a.command.c_str(), b) < 0; + return strcmp(a.command.c_str(), b) < 0; } -debugger_console::debug_command::debug_command(const char *_command, u32 _flags, int _minparams, int _maxparams, std::function<void (const std::vector<std::string> &)> &&_handler) +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 -------------------------------------------------------------*/ +//------------------------------------------------------------ +// execute_help_custom - execute the helpcustom command +//------------------------------------------------------------ -void debugger_console::execute_help_custom(const std::vector<std::string> ¶ms) +void debugger_console::execute_help_custom(const std::vector<std::string_view> ¶ms) { - char buf[64]; for (const debug_command &cmd : m_commandlist) { if (cmd.flags & CMDFLAG_CUSTOM_HELP) { - snprintf(buf, 63, "%s help", cmd.command.c_str()); - 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); } } } @@ -157,9 +161,9 @@ void debugger_console::execute_help_custom(const std::vector<std::string> ¶m execute_condump - execute the condump command ------------------------------------------------------------*/ -void debugger_console::execute_condump(const std::vector<std::string>& params) +void debugger_console::execute_condump(const std::vector<std::string_view>& params) { - std::string filename = params[0]; + std::string filename(params[0]); const char* mode; /* replace macros */ @@ -195,166 +199,149 @@ void debugger_console::execute_condump(const std::vector<std::string>& params) // symbol table //------------------------------------------------- -symbol_table &debugger_console::visible_symtable() +symbol_table &debugger_console::visible_symtable() const { return m_visiblecpu->debug()->symtable(); } -/*------------------------------------------------- - trim_parameter - executes a - command --------------------------------------------------*/ +//------------------------------------------------- +// trim_parameter - trim spaces and quotes around +// a command parameter +//------------------------------------------------- -void debugger_console::trim_parameter(char **paramptr, bool keep_quotes) +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) { // no params is an error - if (params == 0) + if (params.empty()) return CMDERR::none(); // the first parameter has the command and the real first parameter; separate them - char *p, *command; - for (p = param[0]; *p && isspace(u8(*p)); p++) { } - for (command = p; *p && !isspace(u8(*p)); p++) { } - if (*p != 0) - { - *p++ = 0; - for ( ; *p && isspace(u8(*p)); p++) { } - if (*p != 0) - param[0] = p; - else - params = 0; - } + 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; - param[0] = nullptr; - } + params[0].remove_prefix(pos); // search the command list - size_t const len = strlen(command); - auto const found = m_commandlist.lower_bound(command); + auto const found = m_commandlist.lower_bound(command.c_str()); // error if not found - if ((m_commandlist.end() == found) || core_strnicmp(command, found->command.c_str(), len)) + 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() > len) + if (found->command.length() > command.length()) { auto const next = std::next(found); - if ((m_commandlist.end() != next) && !core_strnicmp(command, next->command.c_str(), len)) + if (m_commandlist.end() != next && std::string_view(command) == std::string_view(next->command).substr(0, command.length())) return CMDERR::ambiguous_command(0); } - // NUL-terminate and trim space around all the parameters - for (int i = 1; i < params; i++) - *param[i]++ = 0; - // now go back and trim quotes and braces and any spaces they reveal - for (int i = 0; i < params; i++) - trim_parameter(¶m[i], found->flags & CMDFLAG_KEEP_QUOTES); + for (std::string_view ¶m : params) + param = trim_parameter(param, found->flags & CMDFLAG_KEEP_QUOTES); // see if we have the right number of parameters - if (params < found->minparams) + if (params.size() < found->minparams) return CMDERR::not_enough_params(0); - if (params > found->maxparams) + if (params.size() > found->maxparams) return CMDERR::too_many_params(0); // execute the handler if (execute) - { - std::vector<std::string> params_vec(param, param + params); - found->handler(params_vec); - } + 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 }; - 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 @@ -364,47 +351,44 @@ 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 CMDERR::unbalanced_parens(p - command); break; - case ']': if (parendex == 0 || parens[--parendex] != '[') return CMDERR::unbalanced_parens(p - command); break; - case '}': if (parendex == 0 || parens[--parendex] != '{') return 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 = 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 CMDERR::unbalanced_quotes(p - command); - if (parendex != 0) - return 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 { - parsed_expression(visible_symtable(), command_start).execute(); + parsed_expression expr(visible_symtable(), command_or_expr); + if (execute) + expr.execute(); } catch (expression_error &err) { @@ -413,29 +397,29 @@ CMDERR debugger_console::internal_parse_command(const std::string &original_comm } else { - const CMDERR result = internal_execute_command(execute, paramcount, ¶ms[0]); + const CMDERR result = internal_execute_command(execute, params); if (result.error_class() != CMDERR::NONE) - return CMDERR(result.error_class(), command_start - command); + return CMDERR(result.error_class(), startpos); } } 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) { - /* echo if requested */ + // echo if requested if (echo) printf(">%s\n", command); - /* parse and execute */ + // parse and execute const CMDERR result = internal_parse_command(command, true); - /* display errors */ + // display errors if (result.error_class() != CMDERR::NONE) { if (!echo) @@ -444,7 +428,7 @@ CMDERR debugger_console::execute_command(const std::string &command, bool echo) printf("%s\n", cmderr_to_string(result)); } - /* update all views */ + // update all views if (echo) { m_machine.debug_view().update_all(); @@ -454,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); } @@ -468,7 +452,7 @@ 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 minparams, int maxparams, std::function<void (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!"); @@ -576,6 +560,494 @@ std::string debugger_console::cmderr_to_string(CMDERR 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; +} + /*************************************************************************** @@ -614,7 +1086,7 @@ void debugger_console::print_core_wrap(std::string_view text, int wrapcol) // the format to the debug console //------------------------------------------------- -void debugger_console::vprintf(util::format_argument_pack<std::ostream> const &args) +void debugger_console::vprintf(util::format_argument_pack<char> const &args) { print_core(util::string_format(args)); @@ -622,7 +1094,7 @@ void debugger_console::vprintf(util::format_argument_pack<std::ostream> const &a 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) { print_core(util::string_format(std::move(args))); @@ -636,7 +1108,7 @@ void debugger_console::vprintf(util::format_argument_pack<std::ostream> &&args) // 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) { print_core_wrap(util::string_format(args), wrapcol); @@ -644,7 +1116,7 @@ void debugger_console::vprintf_wrap(int wrapcol, util::format_argument_pack<std: 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) { print_core_wrap(util::string_format(std::move(args)), wrapcol); diff --git a/src/emu/debug/debugcon.h b/src/emu/debug/debugcon.h index 5271b0e5ae5..37b2e235348 100644 --- a/src/emu/debug/debugcon.h +++ b/src/emu/debug/debugcon.h @@ -23,18 +23,13 @@ CONSTANTS ***************************************************************************/ -#define MAX_COMMAND_LENGTH 4096 -#define MAX_COMMAND_PARAMS 128 +constexpr int MAX_COMMAND_PARAMS = 128; // flags for command parsing constexpr u32 CMDFLAG_NONE = 0x0000; constexpr u32 CMDFLAG_KEEP_QUOTES = 0x0001; constexpr u32 CMDFLAG_CUSTOM_HELP = 0x0002; -// parameter separator macros -#define CMDPARAM_SEPARATOR "\0" -#define CMDPARAM_TERMINATOR "\0\0" - /*************************************************************************** @@ -79,19 +74,20 @@ 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 minparams, int maxparams, std::function<void (const std::vector<std::string> &)> &&handler); + 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); + 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 @@ -110,28 +106,58 @@ public: vprintf_wrap(wrapcol, util::make_format_argument_pack(std::forward<Format>(fmt), std::forward<Params>(args)...)); } - device_t *get_visible_cpu() { return m_visiblecpu; } + device_t *get_visible_cpu() const { return m_visiblecpu; } void set_visible_cpu(device_t *visiblecpu) { m_visiblecpu = visiblecpu; } - symbol_table &visible_symtable(); + 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(const std::vector<std::string> ¶ms); - void execute_condump(const std::vector<std::string>& params); + void execute_help_custom(const std::vector<std::string_view> ¶ms); + void execute_condump(const std::vector<std::string_view>& params); - 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); + [[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 + 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(const char *_command, u32 _flags, int _minparams, int _maxparams, std::function<void (const std::vector<std::string> &)> &&_handler); + debug_command(std::string_view _command, u32 _flags, int _minparams, int _maxparams, std::function<void (const std::vector<std::string_view> &)> &&_handler); struct compare { @@ -144,7 +170,7 @@ private: std::string command; const char * params; const char * help; - std::function<void (const std::vector<std::string> &)> handler; + std::function<void (const std::vector<std::string_view> &)> handler; u32 flags; int minparams; int maxparams; diff --git a/src/emu/debug/debugcpu.cpp b/src/emu/debug/debugcpu.cpp index 403d0b104ee..fd0093bd283 100644 --- a/src/emu/debug/debugcpu.cpp +++ b/src/emu/debug/debugcpu.cpp @@ -19,11 +19,12 @@ #include "debugger.h" #include "emuopts.h" +#include "fileio.h" +#include "main.h" #include "screen.h" #include "uiinput.h" #include "corestr.h" -#include "coreutil.h" #include "osdepend.h" #include "xmlfile.h" @@ -46,6 +47,7 @@ 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) @@ -91,7 +93,7 @@ debugger_cpu::debugger_cpu(running_machine &machine) for (int regnum = 0; regnum < NUM_TEMP_VARIABLES; regnum++) { char symname[10]; - sprintf(symname, "temp%d", regnum); + snprintf(symname, 10, "temp%d", regnum); m_symtable->add(symname, symbol_table::READ_WRITE, &m_tempvar[regnum]); } } @@ -385,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) @@ -423,9 +425,9 @@ device_debug::device_debug(device_t &device) , m_disasm(nullptr) , m_flags(0) , m_symtable(std::make_unique<symbol_table>(device.machine(), &device.machine().debugger().cpu().global_symtable(), &device)) - , m_instrhook(nullptr) , m_stepaddr(0) , m_stepsleft(0) + , m_delay_steps(0) , m_stopaddr(0) , m_stoptime(attotime::zero) , m_stopirq(0) @@ -436,7 +438,8 @@ device_debug::device_debug(device_t &device) , m_pc_history_index(0) , m_pc_history_valid(0) , m_bplist() - , m_rplist(std::make_unique<std::forward_list<debug_registerpoint>>()) + , m_rplist() + , m_eplist() , m_triggered_breakpoint(nullptr) , m_triggered_watchpoint(nullptr) , m_trace(nullptr) @@ -458,14 +461,14 @@ device_debug::device_debug(device_t &device) // set up notifiers and clear the passthrough handlers if (m_memory) { int count = m_memory->max_space_count(); - m_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 @@ -547,6 +550,7 @@ 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) @@ -562,15 +566,14 @@ void device_debug::reinstall(address_space &space, read_or_write mode) int id = space.spacenum(); 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_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; + 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; } } } @@ -633,15 +636,46 @@ void device_debug::stop_hook() // acknowledged //------------------------------------------------- -void device_debug::interrupt_hook(int irqline) +void device_debug::interrupt_hook(int irqline, offs_t pc) { // see if this matches a pending interrupt request if ((m_flags & DEBUG_FLAG_STOP_INTERRUPT) != 0 && (m_stopirq == -1 || m_stopirq == irqline)) { m_device.machine().debugger().cpu().set_execution_stopped(); - m_device.machine().debugger().console().printf("Stopped on interrupt (CPU '%s', IRQ %d)\n", m_device.tag(), irqline); + const address_space &space = m_memory->space(AS_PROGRAM); + if (space.is_octal()) + m_device.machine().debugger().console().printf("Stopped on interrupt (CPU '%s', IRQ %d, PC=%0*o)\n", m_device.tag(), irqline, (space.logaddr_width() + 2) / 3, pc); + else + m_device.machine().debugger().console().printf("Stopped on interrupt (CPU '%s', IRQ %d, PC=%0*X)\n", m_device.tag(), irqline, space.logaddrchars(), pc); compute_debug_flags(); } + + if (m_trace != nullptr) + m_trace->interrupt_update(irqline, pc); + + if ((m_flags & (DEBUG_FLAG_STEPPING_OVER | DEBUG_FLAG_STEPPING_OUT | DEBUG_FLAG_STEPPING_BRANCH)) != 0) + { + if ((m_flags & DEBUG_FLAG_CALL_IN_PROGRESS) == 0) + { + if ((m_flags & DEBUG_FLAG_TEST_IN_PROGRESS) != 0) + { + if ((m_stepaddr == pc && (m_flags & DEBUG_FLAG_STEPPING_BRANCH_FALSE) != 0) || + (m_stepaddr != pc && m_delay_steps == 1 && (m_flags & (DEBUG_FLAG_STEPPING_OUT | DEBUG_FLAG_STEPPING_BRANCH_TRUE)) != 0)) + { + // step over the interrupt and then call it finished + m_flags = (m_flags & ~(DEBUG_FLAG_TEST_IN_PROGRESS | DEBUG_FLAG_STEPPING_ANY)) | DEBUG_FLAG_STEPPING_OVER; + m_stepsleft = 1; + } + } + + // remember the interrupt return address + m_flags |= DEBUG_FLAG_CALL_IN_PROGRESS; + m_stepaddr = pc; + } + + m_flags &= ~DEBUG_FLAG_TEST_IN_PROGRESS; + m_delay_steps = 0; + } } @@ -671,10 +705,39 @@ void device_debug::exception_hook(int exception) if (matched) { m_device.machine().debugger().cpu().set_execution_stopped(); - m_device.machine().debugger().console().printf("Stopped on exception (CPU '%s', exception %d, PC=%X)\n", m_device.tag(), exception, m_state->pcbase()); + m_device.machine().debugger().console().printf("Stopped on exception (CPU '%s', exception %X, PC=%s)\n", m_device.tag(), exception, m_state->state_string(STATE_GENPC)); compute_debug_flags(); } } + + // see if any exception points match + if (!m_eplist.empty()) + { + auto epitp = m_eplist.equal_range(exception); + for (auto epit = epitp.first; epit != epitp.second; ++epit) + { + debug_exceptionpoint &ep = *epit->second; + if (ep.hit(exception)) + { + // halt in the debugger by default + debugger_cpu &debugcpu = m_device.machine().debugger().cpu(); + debugcpu.set_execution_stopped(); + + // if we hit, evaluate the action + if (!ep.m_action.empty()) + m_device.machine().debugger().console().execute_command(ep.m_action, false); + + // print a notification, unless the action made us go again + if (debugcpu.is_stopped()) + { + debugcpu.set_execution_stopped(); + m_device.machine().debugger().console().printf("Stopped at exception point %X (CPU '%s', PC=%s)\n", ep.m_index, m_device.tag(), m_state->state_string(STATE_GENPC)); + compute_debug_flags(); + } + break; + } + } + } } @@ -688,11 +751,11 @@ void device_debug::privilege_hook() if ((m_flags & DEBUG_FLAG_STOP_PRIVILEGE) != 0) { bool matched = true; - if (m_privilege_condition && !m_privilege_condition->is_empty()) + if (m_stop_condition && !m_stop_condition->is_empty()) { try { - matched = m_privilege_condition->execute(); + matched = m_stop_condition->execute(); } catch (expression_error &) { @@ -743,26 +806,56 @@ void device_debug::instruction_hook(offs_t curpc) if (m_trace != nullptr) m_trace->update(curpc); - // per-instruction hook? - if (!debugcpu.is_stopped() && (m_flags & DEBUG_FLAG_HOOKED) != 0 && (*m_instrhook)(m_device, curpc)) - debugcpu.set_execution_stopped(); - // handle single stepping if (!debugcpu.is_stopped() && (m_flags & DEBUG_FLAG_STEPPING_ANY) != 0) { + bool do_step = true; + if ((m_flags & (DEBUG_FLAG_CALL_IN_PROGRESS | DEBUG_FLAG_TEST_IN_PROGRESS)) != 0) + { + if (curpc == m_stepaddr) + { + if ((~m_flags & (DEBUG_FLAG_TEST_IN_PROGRESS | DEBUG_FLAG_STEPPING_BRANCH_FALSE)) == 0) + { + debugcpu.set_execution_stopped(); + do_step = false; + } + + // reset the breakpoint + m_flags &= ~(DEBUG_FLAG_CALL_IN_PROGRESS | DEBUG_FLAG_TEST_IN_PROGRESS); + m_delay_steps = 0; + } + else if (m_delay_steps != 0) + { + m_delay_steps--; + if (m_delay_steps == 0) + { + // branch taken or subroutine entered (TODO: interleaved multithreading can falsely trigger this) + if ((m_flags & DEBUG_FLAG_TEST_IN_PROGRESS) != 0 && (m_flags & (DEBUG_FLAG_STEPPING_OUT | DEBUG_FLAG_STEPPING_BRANCH_TRUE)) != 0) + { + debugcpu.set_execution_stopped(); + do_step = false; + } + if ((m_flags & DEBUG_FLAG_CALL_IN_PROGRESS) != 0) + do_step = false; + m_flags &= ~DEBUG_FLAG_TEST_IN_PROGRESS; + } + } + else + do_step = false; + } + // is this an actual step? - if (m_stepaddr == ~0 || curpc == m_stepaddr) + if (do_step) { - // decrement the count and reset the breakpoint + // decrement the count m_stepsleft--; - m_stepaddr = ~0; // if we hit 0, stop if (m_stepsleft == 0) debugcpu.set_execution_stopped(); // update every 100 steps until we are within 200 of the end - else if ((m_flags & DEBUG_FLAG_STEPPING_OUT) == 0 && (m_stepsleft < 200 || m_stepsleft % 100 == 0)) + else if ((m_flags & (DEBUG_FLAG_STEPPING_OUT | DEBUG_FLAG_STEPPING_BRANCH_TRUE | DEBUG_FLAG_STEPPING_BRANCH_FALSE)) == 0 && (m_stepsleft < 200 || m_stepsleft % 100 == 0)) { machine.debug_view().update_all(); machine.debug_view().flush_osd_updates(); @@ -784,7 +877,10 @@ void device_debug::instruction_hook(offs_t curpc) // check the temp running breakpoint and break if we hit it else if ((m_flags & DEBUG_FLAG_STOP_PC) != 0 && m_stopaddr == curpc) { - machine.debugger().console().printf("Stopped at temporary breakpoint %X on CPU '%s'\n", m_stopaddr, m_device.tag()); + if (is_octal()) + machine.debugger().console().printf("Stopped at temporary breakpoint %o on CPU '%s'\n", m_stopaddr, m_device.tag()); + else + machine.debugger().console().printf("Stopped at temporary breakpoint %X on CPU '%s'\n", m_stopaddr, m_device.tag()); debugcpu.set_execution_stopped(); } @@ -849,7 +945,7 @@ void device_debug::instruction_hook(offs_t curpc) } // handle step out/over on the instruction we are about to execute - if ((m_flags & (DEBUG_FLAG_STEPPING_OVER | DEBUG_FLAG_STEPPING_OUT)) != 0 && m_stepaddr == ~0) + if ((m_flags & (DEBUG_FLAG_STEPPING_OVER | DEBUG_FLAG_STEPPING_OUT | DEBUG_FLAG_STEPPING_BRANCH)) != 0 && (m_flags & (DEBUG_FLAG_CALL_IN_PROGRESS | DEBUG_FLAG_TEST_IN_PROGRESS)) == 0) prepare_for_step_overout(m_state->pcbase()); // no longer in debugger code @@ -858,22 +954,6 @@ void device_debug::instruction_hook(offs_t curpc) //------------------------------------------------- -// set_instruction_hook - set a hook to be -// called on each instruction for a given device -//------------------------------------------------- - -void device_debug::set_instruction_hook(debug_instruction_hook_func hook) -{ - // set the hook and also the CPU's flag for fast knowledge of the hook - m_instrhook = hook; - if (hook != nullptr) - m_flags |= DEBUG_FLAG_HOOKED; - else - m_flags &= ~DEBUG_FLAG_HOOKED; -} - - -//------------------------------------------------- // ignore - ignore/observe a given device //------------------------------------------------- @@ -928,8 +1008,9 @@ void device_debug::single_step(int numsteps) m_device.machine().rewind_capture(); m_stepsleft = numsteps; - m_stepaddr = ~0; + m_delay_steps = 0; m_flags |= DEBUG_FLAG_STEPPING; + m_flags &= ~(DEBUG_FLAG_CALL_IN_PROGRESS | DEBUG_FLAG_TEST_IN_PROGRESS); m_device.machine().debugger().cpu().set_execution_running(); } @@ -945,8 +1026,9 @@ void device_debug::single_step_over(int numsteps) m_device.machine().rewind_capture(); m_stepsleft = numsteps; - m_stepaddr = ~0; + m_delay_steps = 0; m_flags |= DEBUG_FLAG_STEPPING_OVER; + m_flags &= ~(DEBUG_FLAG_CALL_IN_PROGRESS | DEBUG_FLAG_TEST_IN_PROGRESS); m_device.machine().debugger().cpu().set_execution_running(); } @@ -961,9 +1043,11 @@ void device_debug::single_step_out() assert(m_exec != nullptr); m_device.machine().rewind_capture(); + m_stop_condition.reset(); m_stepsleft = 100; - m_stepaddr = ~0; + m_delay_steps = 0; m_flags |= DEBUG_FLAG_STEPPING_OUT; + m_flags &= ~(DEBUG_FLAG_CALL_IN_PROGRESS | DEBUG_FLAG_TEST_IN_PROGRESS); m_device.machine().debugger().cpu().set_execution_running(); } @@ -1060,20 +1144,38 @@ void device_debug::go_privilege(const char *condition) { assert(m_exec != nullptr); m_device.machine().rewind_invalidate(); - m_privilege_condition = std::make_unique<parsed_expression>(*m_symtable, condition); + m_stop_condition = std::make_unique<parsed_expression>(*m_symtable, condition); m_flags |= DEBUG_FLAG_STOP_PRIVILEGE; m_device.machine().debugger().cpu().set_execution_running(); } //------------------------------------------------- +// go_branch - execute until branch taken or +// not taken +//------------------------------------------------- + +void device_debug::go_branch(bool sense, const char *condition) +{ + assert(m_exec != nullptr); + m_device.machine().rewind_invalidate(); + m_stop_condition = std::make_unique<parsed_expression>(*m_symtable, condition); + m_stepsleft = 100; + m_delay_steps = 0; + m_flags |= sense ? DEBUG_FLAG_STEPPING_BRANCH_TRUE : DEBUG_FLAG_STEPPING_BRANCH_FALSE; + m_flags &= ~(DEBUG_FLAG_CALL_IN_PROGRESS | DEBUG_FLAG_TEST_IN_PROGRESS); + m_device.machine().debugger().cpu().set_execution_running(); +} + + +//------------------------------------------------- // halt_on_next_instruction_impl - halt in the // debugger on the next instruction, internal // implementation which is necessary solely due // to templates in C++ being janky as all get out //------------------------------------------------- -void device_debug::halt_on_next_instruction_impl(util::format_argument_pack<std::ostream> &&args) +void device_debug::halt_on_next_instruction_impl(util::format_argument_pack<char> &&args) { assert(m_exec != nullptr); m_device.machine().debugger().cpu().halt_on_next_instruction(&m_device, std::move(args)); @@ -1098,7 +1200,7 @@ const debug_breakpoint *device_debug::breakpoint_find(offs_t address) const // returning its index //------------------------------------------------- -int device_debug::breakpoint_set(offs_t address, const char *condition, const char *action) +int device_debug::breakpoint_set(offs_t address, const char *condition, std::string_view action) { // allocate a new one and hook it into our list u32 id = m_device.machine().debugger().cpu().get_breakpoint_index(); @@ -1185,7 +1287,7 @@ void device_debug::breakpoint_enable_all(bool enable) // returning its index //------------------------------------------------- -int device_debug::watchpoint_set(address_space &space, read_or_write type, offs_t address, offs_t length, const char *condition, const char *action) +int device_debug::watchpoint_set(address_space &space, read_or_write type, offs_t address, offs_t length, const char *condition, std::string_view action) { if (space.spacenum() >= int(m_wplist.size())) m_wplist.resize(space.spacenum()+1); @@ -1272,15 +1374,15 @@ void device_debug::watchpoint_enable_all(bool enable) // returning its index //------------------------------------------------- -int device_debug::registerpoint_set(const char *condition, const char *action) +int device_debug::registerpoint_set(const char *condition, std::string_view action) { // allocate a new one u32 id = m_device.machine().debugger().cpu().get_registerpoint_index(); - m_rplist->emplace_front(*m_symtable, id, condition, action); + m_rplist.emplace_front(*m_symtable, id, condition, action); // update the flags and return the index breakpoint_update_flags(); - return m_rplist->front().m_index; + return m_rplist.front().m_index; } @@ -1292,10 +1394,10 @@ int device_debug::registerpoint_set(const char *condition, const char *action) bool device_debug::registerpoint_clear(int index) { // scan the list to see if we own this registerpoint - for (auto brp = m_rplist->before_begin(); std::next(brp) != m_rplist->end(); ++brp) + for (auto brp = m_rplist.before_begin(); std::next(brp) != m_rplist.end(); ++brp) if (std::next(brp)->m_index == index) { - m_rplist->erase_after(brp); + m_rplist.erase_after(brp); breakpoint_update_flags(); return true; } @@ -1312,7 +1414,7 @@ bool device_debug::registerpoint_clear(int index) void device_debug::registerpoint_clear_all() { // clear the list - m_rplist->clear(); + m_rplist.clear(); breakpoint_update_flags(); } @@ -1325,7 +1427,7 @@ void device_debug::registerpoint_clear_all() bool device_debug::registerpoint_enable(int index, bool enable) { // scan the list to see if we own this conditionpoint - for (debug_registerpoint &rp : *m_rplist) + for (debug_registerpoint &rp : m_rplist) if (rp.m_index == index) { rp.m_enabled = enable; @@ -1346,12 +1448,97 @@ bool device_debug::registerpoint_enable(int index, bool enable) void device_debug::registerpoint_enable_all(bool enable) { // apply the enable to all registerpoints we own - for (debug_registerpoint &rp : *m_rplist) + for (debug_registerpoint &rp : m_rplist) registerpoint_enable(rp.index(), enable); } //------------------------------------------------- +// exceptionpoint_set - set a new exception +// point, returning its index +//------------------------------------------------- + +int device_debug::exceptionpoint_set(int exception, 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_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; +} + + +//------------------------------------------------- +// 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; + } + } + + // 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); +} + + +//------------------------------------------------- // history_pc - return an entry from the PC // history //------------------------------------------------- @@ -1385,7 +1572,6 @@ void device_debug::set_track_pc(bool value) // 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(offs_t pc) const @@ -1399,7 +1585,6 @@ bool device_debug::track_pc_visited(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(offs_t pc) @@ -1541,7 +1726,7 @@ u32 device_debug::compute_opcode_crc32(offs_t pc) const buffer.data_get(pc, dasmresult & util::disasm_interface::LENGTHMASK, true, opbuf); // return a CRC of the exact count of opcode bytes - return core_crc32(0, &opbuf[0], opbuf.size()); + return util::crc32_creator::simple(&opbuf[0], opbuf.size()); } @@ -1549,31 +1734,14 @@ u32 device_debug::compute_opcode_crc32(offs_t pc) const // trace - trace execution of a given device //------------------------------------------------- -void device_debug::trace(FILE *file, bool trace_over, bool detect_loops, bool logerror, const char *action) +void device_debug::trace(std::unique_ptr<std::ostream> &&file, bool trace_over, bool detect_loops, bool logerror, std::string_view action) { // delete any existing tracers m_trace = nullptr; // if we have a new file, make a new tracer if (file != nullptr) - m_trace = std::make_unique<tracer>(*this, *file, trace_over, detect_loops, logerror, action); -} - - -//------------------------------------------------- -// trace_printf - output data into the given -// device's tracefile, if tracing -//------------------------------------------------- - -void device_debug::trace_printf(const char *fmt, ...) -{ - if (m_trace != nullptr) - { - va_list va; - va_start(va, fmt); - m_trace->vprintf(fmt, va); - va_end(va); - } + m_trace = std::make_unique<tracer>(*this, std::move(file), trace_over, detect_loops, logerror, action); } @@ -1601,7 +1769,7 @@ void device_debug::compute_debug_flags() // if we're tracking history, or we're hooked, or stepping, or stopping at a breakpoint // make sure we call the hook - if ((m_flags & (DEBUG_FLAG_HISTORY | DEBUG_FLAG_HOOKED | DEBUG_FLAG_STEPPING_ANY | DEBUG_FLAG_STOP_PC | DEBUG_FLAG_LIVE_BP)) != 0) + if ((m_flags & (DEBUG_FLAG_HISTORY | DEBUG_FLAG_STEPPING_ANY | DEBUG_FLAG_STOP_PC | DEBUG_FLAG_LIVE_BP)) != 0) machine.debug_flags |= DEBUG_FLAG_CALL_HOOK; // also call if we are tracing @@ -1625,12 +1793,32 @@ void device_debug::prepare_for_step_overout(offs_t pc) // disassemble the current instruction and get the flags u32 dasmresult = buffer.disassemble_info(pc); + if ((dasmresult & util::disasm_interface::SUPPORTED) == 0) + return; + + bool step_out = (m_flags & DEBUG_FLAG_STEPPING_OUT) != 0 && (dasmresult & util::disasm_interface::STEP_OUT) != 0; + bool test_cond = (dasmresult & util::disasm_interface::STEP_COND) != 0 && ((m_flags & (DEBUG_FLAG_STEPPING_BRANCH_TRUE | DEBUG_FLAG_STEPPING_BRANCH_FALSE)) != 0 || step_out); + if (test_cond && m_stop_condition && !m_stop_condition->is_empty()) + { + try + { + test_cond = m_stop_condition->execute(); + } + catch (expression_error &) + { + test_cond = false; + } + } // if flags are supported and it's a call-style opcode, set a temp breakpoint after that instruction - if ((dasmresult & util::disasm_interface::SUPPORTED) != 0 && (dasmresult & util::disasm_interface::STEP_OVER) != 0) + // (TODO: this completely fails for subroutines that consume inline operands or use alternate returns) + if ((dasmresult & util::disasm_interface::STEP_OVER) != 0 || test_cond) { int extraskip = (dasmresult & util::disasm_interface::OVERINSTMASK) >> util::disasm_interface::OVERINSTSHIFT; pc = buffer.next_pc_wrap(pc, dasmresult & util::disasm_interface::LENGTHMASK); + m_delay_steps = extraskip + 1; + if (m_stepsleft < m_delay_steps) + m_stepsleft = m_delay_steps; // if we need to skip additional instructions, advance as requested while (extraskip-- > 0) { @@ -1638,13 +1826,17 @@ 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 - // (TODO: this doesn't work with conditional return instructions) - 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 { @@ -1678,7 +1870,7 @@ void device_debug::breakpoint_update_flags() if (!(m_flags & DEBUG_FLAG_LIVE_BP)) { // see if there are any enabled registerpoints - for (debug_registerpoint &rp : *m_rplist) + for (debug_registerpoint &rp : m_rplist) { if (rp.m_enabled) { @@ -1728,7 +1920,7 @@ void device_debug::breakpoint_check(offs_t pc) } // see if we have any matching registerpoints - for (debug_registerpoint &rp : *m_rplist) + for (debug_registerpoint &rp : m_rplist) { if (rp.hit()) { @@ -1761,10 +1953,10 @@ void device_debug::breakpoint_check(offs_t pc) // 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) @@ -1783,7 +1975,7 @@ device_debug::tracer::tracer(device_debug &debug, FILE &file, bool trace_over, b device_debug::tracer::~tracer() { // make sure we close the file if we can - fclose(&m_file); + m_file.reset(); } @@ -1819,7 +2011,7 @@ void device_debug::tracer::update(offs_t pc) // if we just finished looping, indicate as much if (m_loops != 0) - fprintf(&m_file, "\n (loops for %d instructions)\n\n", m_loops); + util::stream_format(*m_file, "\n (loops for %d instructions)\n\n", m_loops); m_loops = 0; } @@ -1834,7 +2026,7 @@ void device_debug::tracer::update(offs_t pc) buffer.disassemble(pc, instruction, next_pc, size, dasmresult); // output the result - fprintf(&m_file, "%s: %s\n", buffer.pc_to_string(pc).c_str(), instruction.c_str()); + util::stream_format(*m_file, "%s: %s\n", buffer.pc_to_string(pc), instruction); // do we need to step the trace over this instruction? if (m_trace_over && (dasmresult & util::disasm_interface::SUPPORTED) != 0 && (dasmresult & util::disasm_interface::STEP_OVER) != 0) @@ -1852,7 +2044,33 @@ void device_debug::tracer::update(offs_t pc) // log this PC m_nextdex = (m_nextdex + 1) % TRACE_LOOPS; m_history[m_nextdex] = pc; - fflush(&m_file); + m_file->flush(); +} + + +//------------------------------------------------- +// interrupt_update - log interrupt to tracefile +//------------------------------------------------- + +void device_debug::tracer::interrupt_update(int irqline, offs_t pc) +{ + if (m_trace_over) + { + if (m_trace_over_target != ~0) + return; + m_trace_over_target = pc; + } + + // if we just finished looping, indicate as much + *m_file << "\n"; + if (m_detect_loops && m_loops != 0) + { + util::stream_format(*m_file, " (loops for %d instructions)\n", m_loops); + m_loops = 0; + } + + util::stream_format(*m_file, " (interrupted at %s, IRQ %d)\n\n", debug_disasm_buffer(m_debug.device()).pc_to_string(pc), irqline); + m_file->flush(); } @@ -1860,11 +2078,11 @@ void device_debug::tracer::update(offs_t pc) // vprintf - generic print to the trace file //------------------------------------------------- -void device_debug::tracer::vprintf(const char *format, va_list va) +void device_debug::tracer::vprintf(util::format_argument_pack<char> const &args) { // pass through to the file - vfprintf(&m_file, format, va); - fflush(&m_file); + util::stream_format(*m_file, args); + m_file->flush(); } @@ -1875,7 +2093,7 @@ void device_debug::tracer::vprintf(const char *format, va_list va) void device_debug::tracer::flush() { - fflush(&m_file); + m_file->flush(); } diff --git a/src/emu/debug/debugcpu.h b/src/emu/debug/debugcpu.h index 9e3d23d8d7f..9a9f6e69f70 100644 --- a/src/emu/debug/debugcpu.h +++ b/src/emu/debug/debugcpu.h @@ -29,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. @@ -54,14 +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); - // debugger focus void ignore(bool ignore = true); bool observing() const { return ((m_flags & DEBUG_FLAG_OBSERVING) != 0); } @@ -82,6 +76,7 @@ public: 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> @@ -93,7 +88,7 @@ public: // breakpoints 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, const char *action = nullptr); + 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); @@ -103,7 +98,7 @@ public: // watchpoints int watchpoint_space_count() const { return m_wplist.size(); } 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, const char *action); + 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); @@ -112,12 +107,20 @@ public: debug_watchpoint *triggered_watchpoint() { debug_watchpoint *ret = m_triggered_watchpoint; m_triggered_watchpoint = nullptr; return ret; } // registerpoints - const std::forward_list<debug_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); + + // 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); @@ -146,8 +149,12 @@ public: 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; } @@ -158,7 +165,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); @@ -181,18 +188,18 @@ private: // global state u32 m_flags; // debugging flags for this CPU std::unique_ptr<symbol_table> m_symtable; // symbol table for expression evaluation - debug_instruction_hook_func m_instrhook; // per-instruction callback hook // 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 @@ -206,7 +213,8 @@ private: // breakpoints and watchpoints 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::unique_ptr<std::forward_list<debug_registerpoint>> m_rplist; // list of registerpoints + 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 debug_breakpoint * m_triggered_breakpoint; // latest breakpoint that was triggered debug_watchpoint * m_triggered_watchpoint; // latest watchpoint that was triggered @@ -215,11 +223,12 @@ private: 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; } @@ -227,7 +236,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 @@ -239,10 +248,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 - 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 @@ -310,7 +319,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 @@ -322,12 +330,17 @@ private: 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_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; }; //************************************************************************** @@ -377,6 +390,7 @@ 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_break_cpu(device_t * breakcpu) { m_breakcpu = breakcpu; } @@ -392,7 +406,7 @@ 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(); @@ -419,6 +433,7 @@ private: u32 m_bpindex; u32 m_wpindex; u32 m_rpindex; + u32 m_epindex; u64 m_wpdata; u64 m_wpaddr; diff --git a/src/emu/debug/debughlp.cpp b/src/emu/debug/debughlp.cpp index 87bf8f0f18f..76102402bcb 100644 --- a/src/emu/debug/debughlp.cpp +++ b/src/emu/debug/debughlp.cpp @@ -46,6 +46,7 @@ const help_item f_static_help_list[] = " Breakpoints\n" " Watchpoints\n" " Registerpoints\n" + " Exception Points\n" " Expressions\n" " Comments\n" " Cheats\n" @@ -81,6 +82,7 @@ const help_item f_static_help_list[] = " 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" }, @@ -132,8 +134,12 @@ const help_item f_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" + " 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" @@ -187,6 +193,18 @@ const help_item f_static_help_list[] = " rplist [<CPU>] -- lists all the registerpoints\n" }, { + "exceptionpoints", + "\n" + "Exception Point Commands\n" + "Type help <command> for further details on each command\n" + "\n" + " ep[set] <type>[,<condition>[,<action>]] -- sets exception point on <type>\n" + " epclear [<epnum>] -- clears a given exception point or all if no <epnum> specified\n" + " epdisable [<epnum>] -- disabled a given exception point or all if no <epnum> specified\n" + " epenable [<epnum>] -- enables a given exception point or all if no <epnum> specified\n" + " eplist -- lists all the exception points\n" + }, + { "expressions", "\n" "Expressions can be used anywhere a numeric parameter is expected. The syntax for expressions is " @@ -341,16 +359,20 @@ const help_item f_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" @@ -361,18 +383,12 @@ const help_item f_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" @@ -578,6 +594,13 @@ const help_item f_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" @@ -920,6 +943,66 @@ const help_item f_static_help_list[] = " Resume execution, stopping at address 1234 unless something else stops us first.\n" }, { + "gbf", + "\n" + " gbf [<condition>]\n" + "\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" + "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" @@ -965,6 +1048,27 @@ const help_item f_static_help_list[] = "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" @@ -1484,6 +1588,92 @@ const help_item f_static_help_list[] = "actions attached to them.\n" }, { + "epset", + "\n" + " ep[set] <type>[,<condition>[,<action>]]\n" + "\n" + "Sets a new exception point 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 exception point is hit. If the result of the expression is true (non-zero), " + "the exception point 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 exception point 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 exception point that is set is assigned an index which can be used in other " + "exception point commands to reference this exception point.\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 exception points. If <epnum> is specified, only the requested " + "exception points are cleared, otherwise all exception points are cleared.\n" + "\n" + "Examples:\n" + "\n" + "epclear 3\n" + " Clear exception point index 3.\n" + "\n" + "epclear\n" + " Clear all exception points.\n" + }, + { + "epdisable", + "\n" + " epdisable [<epnum>[,...]]\n" + "\n" + "The epdisable command disables exception points. If <epnum> is specified, only the requested " + "exception points are disabled, otherwise all exception points are disabled. Note that " + "disabling an exception point does not delete it, it just temporarily marks the exception " + "point as inactive.\n" + "\n" + "Examples:\n" + "\n" + "epdisable 3\n" + " Disable exception point index 3.\n" + "\n" + "epdisable\n" + " Disable all exception points.\n" + }, + { + "epenable", + "\n" + " epenable [<epnum>[,...]]\n" + "\n" + "The epenable command enables exception points. If <epnum> is specified, only the " + "requested exception points are enabled, otherwise all exception points are enabled.\n" + "\n" + "Examples:\n" + "\n" + "epenable 3\n" + " Enable exception point index 3.\n" + "\n" + "epenable\n" + " Enable all exception points.\n" + }, + { + "eplist", + "\n" + " eplist\n" + "\n" + "The eplist command lists all the current exception points, along with their index and " + "any conditions or actions attached to them.\n" + }, + { "map", "\n" " map[{d|i|o}] <address>[:<space>]\n" @@ -1853,10 +2043,12 @@ private: 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: - char const *find(std::string_view tag) + std::string_view find(std::string_view tag) { // find a cached exact match if possible std::string const lower = strmakelower(tag); @@ -1892,16 +2084,17 @@ public: if ((m_help_list.end() == next) || (next->first.substr(0, lower.length()) != lower)) return candidate->second; - // TODO: pointers to static strings are bad, mmmkay? - static char ambig_message[1024]; - int msglen = std::sprintf(ambig_message, "Ambiguous help request, did you mean:\n"); + 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 { - msglen += std::sprintf(&ambig_message[msglen], " help %.*s?\n", int(candidate->first.length()), &candidate->first[0]); + 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 ambig_message; + return util::buf_to_string_view(m_message_buffer); } // take the first help entry if no matches at all @@ -1923,7 +2116,7 @@ public: PUBLIC INTERFACE ***************************************************************************/ -const char *debug_get_help(std::string_view tag) +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 495d674eb0d..9efe12acaca 100644 --- a/src/emu/debug/debughlp.h +++ b/src/emu/debug/debughlp.h @@ -21,6 +21,6 @@ ***************************************************************************/ // help management -const char *debug_get_help(std::string_view tag); +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 5bcc3d057b8..54fe57ecf5a 100644 --- a/src/emu/debug/debugvw.cpp +++ b/src/emu/debug/debugvw.cpp @@ -503,11 +503,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/dvbpoints.cpp b/src/emu/debug/dvbpoints.cpp index b42b86a0e90..d7be62c1eaa 100644 --- a/src/emu/debug/dvbpoints.cpp +++ b/src/emu/debug/dvbpoints.cpp @@ -71,7 +71,7 @@ static bool cConditionDescending(const debug_breakpoint *a, const debug_breakpoi 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 debug_breakpoint *a, const debug_breakpoint *b) diff --git a/src/emu/debug/dvmemory.cpp b/src/emu/debug/dvmemory.cpp index 02fcde9cbc3..da792305c0d 100644 --- a/src/emu/debug/dvmemory.cpp +++ b/src/emu/debug/dvmemory.cpp @@ -20,6 +20,20 @@ //************************************************************************** +// 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 //************************************************************************** @@ -295,13 +309,13 @@ void debug_view_memory::generate_row(debug_view_char *destmin, debug_view_char * // generate the address char addrtext[20]; - sprintf(addrtext, m_addrformat.c_str(), address); + 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 + // generate the data and the ASCII string std::string chunkascii; if (m_shift_bits != 0) { @@ -323,7 +337,7 @@ void debug_view_memory::generate_row(debug_view_char *destmin, debug_view_char * 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 : '.'); + chunkascii += char(ismapped ? sanitise_character(chval) : '.'); } } } @@ -345,15 +359,15 @@ void debug_view_memory::generate_row(debug_view_char *destmin, debug_view_char * switch (m_data_format) { case data_format::FLOAT_32BIT: - sprintf(valuetext, "%.8g", u32_to_float(u32(chunkdata))); + snprintf(valuetext, 64, "%.8g", u32_to_float(u32(chunkdata))); break; case data_format::FLOAT_64BIT: - sprintf(valuetext, "%.24g", u64_to_double(chunkdata)); + snprintf(valuetext, 64, "%.24g", u64_to_double(chunkdata)); break; case data_format::FLOAT_80BIT: { float64_t f64 = extF80M_to_f64(&chunkdata80); - sprintf(valuetext, "%.24g", u64_to_double(f64.v)); + snprintf(valuetext, 64, "%.24g", u64_to_double(f64.v)); break; } default: @@ -380,7 +394,7 @@ void debug_view_memory::generate_row(debug_view_char *destmin, debug_view_char * 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 : '.'); + chunkascii += char(ismapped ? sanitise_character(chval) : '.'); } } } @@ -526,16 +540,8 @@ void debug_view_memory::view_char(int chval) if (hexchar == nullptr || (m_shift_bits == 3 && chval >= '8')) 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; - - data &= ~(util::make_bitmask<u64>(m_shift_bits) << 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]]; @@ -856,14 +862,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) - data = m_expression.context().read_memory(*source.m_space, offs, size, !m_no_translation); + data = m_expression.context().read_memory(*tspace, offs, size, !m_no_translation); return ismapped; } @@ -984,6 +993,41 @@ void debug_view_memory::write(u8 size, offs_t offs, u64 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; + + // clamp to chunk size + if (m_bytes_per_chunk * 8 < pos + m_shift_bits) + { + assert(m_bytes_per_chunk * 8 > pos); + digit &= util::make_bitmask<u8>(m_bytes_per_chunk * 8 - pos); + } + + 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; +} + + +//------------------------------------------------- // set_expression - set the expression string // describing the home address //------------------------------------------------- diff --git a/src/emu/debug/dvmemory.h b/src/emu/debug/dvmemory.h index f2c738a6ff4..6f1ffab4e9e 100644 --- a/src/emu/debug/dvmemory.h +++ b/src/emu/debug/dvmemory.h @@ -127,6 +127,7 @@ private: // memory access bool read(u8 size, offs_t offs, u64 &data); void write(u8 size, offs_t offs, u64 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); diff --git a/src/emu/debug/dvrpoints.cpp b/src/emu/debug/dvrpoints.cpp index 3ecadaec8e5..7295a1b2170 100644 --- a/src/emu/debug/dvrpoints.cpp +++ b/src/emu/debug/dvrpoints.cpp @@ -62,7 +62,7 @@ bool cConditionDescending(std::pair<device_t *, debug_registerpoint const *> con bool cActionAscending(std::pair<device_t *, debug_registerpoint const *> const &a, std::pair<device_t *, debug_registerpoint const *> const &b) { - return strcmp(a.second->action(), b.second->action()) < 0; + 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) diff --git a/src/emu/debug/express.cpp b/src/emu/debug/express.cpp index 8363928e5cb..9ab75b7b655 100644 --- a/src/emu/debug/express.cpp +++ b/src/emu/debug/express.cpp @@ -387,10 +387,10 @@ void symbol_table::set_memory_modified_func(memory_modified_func modified) // add - add a new u64 pointer symbol //------------------------------------------------- -void symbol_table::add(const char *name, read_write rw, u64 *ptr) +symbol_entry &symbol_table::add(const char *name, read_write rw, u64 *ptr) { m_symlist.erase(name); - m_symlist.emplace(name, std::make_unique<integer_symbol_entry>(*this, name, rw, ptr)); + return *m_symlist.emplace(name, std::make_unique<integer_symbol_entry>(*this, name, rw, ptr)).first->second; } @@ -398,10 +398,10 @@ void symbol_table::add(const char *name, read_write rw, u64 *ptr) // add - add a new value symbol //------------------------------------------------- -void symbol_table::add(const char *name, u64 value) +symbol_entry &symbol_table::add(const char *name, u64 value) { m_symlist.erase(name); - m_symlist.emplace(name, std::make_unique<integer_symbol_entry>(*this, name, value)); + return *m_symlist.emplace(name, std::make_unique<integer_symbol_entry>(*this, name, value)).first->second; } @@ -409,10 +409,10 @@ void symbol_table::add(const char *name, u64 value) // add - add a new register symbol //------------------------------------------------- -void symbol_table::add(const char *name, getter_func getter, setter_func setter, const std::string &format_string) +symbol_entry &symbol_table::add(const char *name, getter_func getter, setter_func setter, const std::string &format_string) { m_symlist.erase(name); - m_symlist.emplace(name, std::make_unique<integer_symbol_entry>(*this, name, getter, setter, format_string)); + return *m_symlist.emplace(name, std::make_unique<integer_symbol_entry>(*this, name, getter, setter, format_string)).first->second; } @@ -420,10 +420,10 @@ void symbol_table::add(const char *name, getter_func getter, setter_func setter, // add - add a new function symbol //------------------------------------------------- -void symbol_table::add(const char *name, int minparams, int maxparams, execute_func execute) +symbol_entry &symbol_table::add(const char *name, int minparams, int maxparams, execute_func execute) { m_symlist.erase(name); - m_symlist.emplace(name, std::make_unique<function_symbol_entry>(*this, name, minparams, maxparams, execute)); + return *m_symlist.emplace(name, std::make_unique<function_symbol_entry>(*this, name, minparams, maxparams, execute)).first->second; } @@ -482,23 +482,25 @@ u64 symbol_table::read_memory(address_space &space, offs_t address, int size, bo { u64 result = ~u64(0) >> (64 - 8*size); + address_space *tspace = &space; + if (apply_translation) { // mask against the logical byte mask address &= space.logaddrmask(); // translate if necessary; if not mapped, return 0xffffffffffffffff - if (!space.device().memory().translate(space.spacenum(), TRANSLATE_READ_DEBUG, address)) + 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 = space.read_byte(address); break; - case 2: result = space.read_word_unaligned(address); break; - case 4: result = space.read_dword_unaligned(address); break; - case 8: result = space.read_qword_unaligned(address); break; + 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; } @@ -511,23 +513,25 @@ u64 symbol_table::read_memory(address_space &space, offs_t address, int size, bo void symbol_table::write_memory(address_space &space, offs_t address, u64 data, int size, bool apply_translation) { + 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(), TRANSLATE_WRITE_DEBUG, address)) + 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: space.write_byte(address, data); break; - case 2: space.write_word_unaligned(address, data); break; - case 4: space.write_dword_unaligned(address, data); break; - case 8: space.write_qword_unaligned(address, data); break; + 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(); @@ -1736,7 +1740,7 @@ void parsed_expression::infix_to_postfix() else if (token->is_operator()) { // normalize the operator based on neighbors - normalize_operator(*token, prev, next != m_tokenlist.end() ? &*next : nullptr, stack, was_rparen); + 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. diff --git a/src/emu/debug/express.h b/src/emu/debug/express.h index 23284193f47..251d8b4365c 100644 --- a/src/emu/debug/express.h +++ b/src/emu/debug/express.h @@ -86,17 +86,19 @@ public: }; // construction/destruction - expression_error(error_code code, int offset = 0, int num = 0) - : m_code(code), - m_offset(offset), - m_num(num) { } + 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; } + constexpr error_code code() const { return m_code; } + constexpr int offset() const { return m_offset; } std::string code_string() const; private: @@ -175,15 +177,16 @@ public: // getters const std::unordered_map<std::string, std::unique_ptr<symbol_entry>> &entries() const { return m_symlist; } symbol_table *parent() const { return m_parent; } + running_machine &machine() { return m_machine; } // setters 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); diff --git a/src/emu/debug/points.cpp b/src/emu/debug/points.cpp index c44bc3f7563..961488eebb2 100644 --- a/src/emu/debug/points.cpp +++ b/src/emu/debug/points.cpp @@ -22,18 +22,19 @@ // debug_breakpoint - constructor //------------------------------------------------- -debug_breakpoint::debug_breakpoint(device_debug* debugInterface, - symbol_table &symbols, - int index, - offs_t address, - const char *condition, - const char *action) - : m_debugInterface(debugInterface), - m_index(index), - m_enabled(true), - m_address(address), - m_condition(symbols, (condition != nullptr) ? condition : "1"), - m_action((action != nullptr) ? action : "") +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) { } @@ -59,7 +60,7 @@ bool debug_breakpoint::hit(offs_t pc) { return (m_condition.execute() != 0); } - catch (expression_error &) + catch (expression_error const &) { return false; } @@ -78,27 +79,28 @@ bool debug_breakpoint::hit(offs_t pc) // debug_watchpoint - constructor //------------------------------------------------- -debug_watchpoint::debug_watchpoint(device_debug* debugInterface, - symbol_table &symbols, - int index, - address_space &space, - read_or_write type, - offs_t address, - offs_t length, - const char *condition, - const char *action) - : m_debugInterface(debugInterface), - m_phr(nullptr), - m_phw(nullptr), - m_space(space), - m_index(index), - m_enabled(true), - m_type(type), - m_address(address & space.addrmask()), - m_length(length), - m_condition(symbols, (condition != nullptr) ? condition : "1"), - m_action((action != nullptr) ? action : ""), - m_installing(false) +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); @@ -170,21 +172,21 @@ debug_watchpoint::debug_watchpoint(device_debug* debugInterface, } install(read_or_write::READWRITE); - m_notifier = m_space.add_change_notifier([this](read_or_write mode) { - if (m_enabled) - { - install(mode); - } - }); + m_notifier = m_space.add_change_notifier( + [this] (read_or_write mode) + { + if (m_enabled) + { + install(mode); + } + }); } debug_watchpoint::~debug_watchpoint() { - m_space.remove_change_notifier(m_notifier); - if (m_phr) - m_phr->remove(); - if (m_phw) - m_phw->remove(); + m_notifier.reset(); + m_phr.remove(); + m_phw.remove(); } void debug_watchpoint::setEnabled(bool value) @@ -197,10 +199,8 @@ void debug_watchpoint::setEnabled(bool value) else { m_installing = true; - if(m_phr) - m_phr->remove(); - if(m_phw) - m_phw->remove(); + m_phr.remove(); + m_phw.remove(); m_installing = false; } } @@ -211,24 +211,28 @@ void debug_watchpoint::install(read_or_write mode) if (m_installing) return; m_installing = true; - if ((u32(mode) & u32(read_or_write::READ)) && m_phr) - m_phr->remove(); - if ((u32(mode) & u32(read_or_write::WRITE)) && m_phw) - m_phw->remove(); + 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); + 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); + 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: @@ -237,17 +241,21 @@ void debug_watchpoint::install(read_or_write mode) { 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); + 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); + 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; @@ -257,17 +265,21 @@ void debug_watchpoint::install(read_or_write mode) { 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); + 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); + 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; @@ -277,17 +289,20 @@ void debug_watchpoint::install(read_or_write mode) { 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); + 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); + [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; } @@ -416,11 +431,11 @@ void debug_watchpoint::triggered(read_or_write type, offs_t address, u64 data, u // debug_registerpoint - constructor //------------------------------------------------- -debug_registerpoint::debug_registerpoint(symbol_table &symbols, int index, const char *condition, const char *action) +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 != nullptr) ? action : "") + m_action(action) { } @@ -450,3 +465,59 @@ bool debug_registerpoint::hit() 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 index 84cdad7720b..42abd46398f 100644 --- a/src/emu/debug/points.h +++ b/src/emu/debug/points.h @@ -36,7 +36,7 @@ public: int index, offs_t address, const char *condition = nullptr, - const char *action = nullptr); + std::string_view action = {}); // getters const device_debug *debugInterface() const { return m_debugInterface; } @@ -44,7 +44,7 @@ public: 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(); } + 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 @@ -77,7 +77,7 @@ public: offs_t address, offs_t length, const char *condition = nullptr, - const char *action = nullptr); + std::string_view action = {}); ~debug_watchpoint(); // getters @@ -102,8 +102,8 @@ private: 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 + 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? @@ -112,7 +112,7 @@ private: 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 + 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 @@ -128,13 +128,13 @@ class debug_registerpoint public: // construction/destruction - debug_registerpoint(symbol_table &symbols, int index, const char *condition, const char *action = nullptr); + 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 char *action() const { return m_action.c_str(); } + const std::string &action() const { return m_action; } private: // internals @@ -146,4 +146,42 @@ private: 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 |