diff --git a/docs/source/debugger/index.rst b/docs/source/debugger/index.rst index 2947d0253acfb..a035a7e13c022 100644 --- a/docs/source/debugger/index.rst +++ b/docs/source/debugger/index.rst @@ -158,6 +158,10 @@ Examples: Address 9660 in the default address space of the device with the absolute tag ``:ram``, or the ``ram`` space of the root machine device. +``1883:vram.m`` + Address 1883 in the memory region with the absolute tag ``:vram``. +``1923:sprites.s`` + Address 1923 in the memory share with the absolute tag ``:sprites``. The examples here include a lot of corner cases, but in general the debugger should take the most likely meaning for a device or address @@ -327,6 +331,7 @@ The size may optionally be preceded by an access type specification: * ``o`` specifies direct read/write pointer access defaulting to space 3 (opcodes) * ``m`` specifies a memory region +* ``s`` specifies a memory share Finally, this may be preceded by a tag and/or address space name followed by a dot (``.``). diff --git a/docs/source/debugger/memory.rst b/docs/source/debugger/memory.rst index dfb5914b5a9dd..b32273a40c609 100644 --- a/docs/source/debugger/memory.rst +++ b/docs/source/debugger/memory.rst @@ -65,6 +65,7 @@ find ---- **f[ind][{d|i|o}]
[:],[,[,…]]** +**f[ind]
:.{m|s},[,[,…]]** Search through memory for the specified sequence of data. The **
** is the address to begin searching from, optionally @@ -114,6 +115,7 @@ fill ---- **fill[{d|i|o}]
[:],[,[,…]]** +**fill
:.{m|s},[,[,…]]** Overwrite a block of memory with copies of the supplied data sequence. The **
** specifies the address to begin writing at, optionally @@ -146,6 +148,7 @@ dump ---- **dump[{d|i|o}] ,
[:],[,[,[,]]]** +**dump ,
:.{m|s},[,[,[,]]]** Dump memory to the text file specified by the **** parameter. The **
** specifies the address to start dumping from, @@ -190,6 +193,7 @@ strdump ------- **strdump[{d|i|o}] ,
[:],[,]** +**strdump ,
:.{m|s},[,]** Dump memory to the text file specified by the **** parameter. The **
** specifies the address to start dumping from, @@ -216,6 +220,7 @@ save ---- **save[{d|i|o}] ,
[:],** +**save ,
:.{m|s},** Save raw memory to the binary file specified by the **** parameter. The **
** specifies the address to start saving @@ -260,6 +265,9 @@ start saving from, and the **** specifies how much memory to save. The range **
** through **
+-1**, inclusive, will be output to the file. +Alternetevely use :ref:`debugger-command-save` syntax: +``save ,
:.m,`` + Examples: ``saver data.bin,200,100,:monitor`` @@ -278,6 +286,7 @@ load ---- **load[{d|i|o}] ,
[:][,]** +**load ,
:.{m|s}[,]** Load raw memory from the binary file specified by the **** parameter. The **
** specifies the address to start loading to, @@ -334,6 +343,9 @@ through **
+-1**, inclusive, will be read in from the file. If the **** is zero, or is greater than the total length of the file, the entire contents of the file will be loaded but no more. +Alternetevely use :ref:`debugger-command-load` syntax: +``load ,
:.m[,]`` + Examples: ``loadr data.bin,200,100,:monitor`` diff --git a/src/emu/debug/debugcmd.cpp b/src/emu/debug/debugcmd.cpp index 76a6053477f9c..3e1a158d3920f 100644 --- a/src/emu/debug/debugcmd.cpp +++ b/src/emu/debug/debugcmd.cpp @@ -28,6 +28,7 @@ #include "softlist.h" #include "corestr.h" +#include "multibyte.h" #include #include @@ -2007,6 +2008,9 @@ void debugger_commands::execute_rewind(const std::vector ¶ void debugger_commands::execute_save(int spacenum, const std::vector ¶ms) { + if (execute_save_try_memory(params)) + return; + u64 offset, endoffset, length; address_space *space; @@ -2093,31 +2097,42 @@ void debugger_commands::execute_save(int spacenum, const std::vector ¶ms) +bool debugger_commands::execute_save_try_memory(const std::vector ¶ms) { - u64 offset, length; - memory_region *region; + u64 offset = u64(-1); + memory_region *region = nullptr; + memory_share *share = nullptr; + if (!m_console.validate_address_with_memory_parameter(params[1], offset, region, share)) + return false; // not memory case - // validate parameters - if (!m_console.validate_number_parameter(params[1], offset)) - return; - if (!m_console.validate_number_parameter(params[2], length)) - return; - if (!m_console.validate_memory_region_parameter(params[3], region)) - return; + u64 length; + if (offset == u64(-1) || !m_console.validate_number_parameter(params[2], length) || (region == nullptr && share == nullptr)) + return true; + + u32 msize; + u8 *base; + if (region != nullptr) + { + msize = region->bytes(); + base = region->base(); + } + else // if (share != nullptr) + { + msize = share->bytes(); + base = reinterpret_cast(share->ptr()); + } - if (offset >= region->bytes()) + if (offset >= msize) { m_console.printf("Invalid offset\n"); - return; + return true; } - if ((length <= 0) || ((length + offset) >= region->bytes())) - length = region->bytes() - offset; + if ((length <= 0) || ((length + offset) >= msize)) + length = msize - offset; /* open the file */ std::string const filename(params[0]); @@ -2125,12 +2140,24 @@ void debugger_commands::execute_saveregion(const std::vector & if (!f) { m_console.printf("Error opening file '%s'\n", params[0]); - return; + return true; } - fwrite(region->base() + offset, 1, length, f); + fwrite(base + offset, 1, length, f); fclose(f); m_console.printf("Data saved successfully\n"); + + return true; +} + + +/*------------------------------------------------- + execute_saveregion - execute the save command on region memory +-------------------------------------------------*/ + +void debugger_commands::execute_saveregion(const std::vector ¶ms) +{ + execute_save(-1, std::vector{ params[0], std::string(params[1]) + std::string(params[3]) + ".m", params[2] }); } @@ -2140,6 +2167,9 @@ void debugger_commands::execute_saveregion(const std::vector & void debugger_commands::execute_load(int spacenum, const std::vector ¶ms) { + if (execute_load_try_memory(params)) + return; + u64 offset, endoffset, length = 0; address_space *space; @@ -2247,31 +2277,43 @@ void debugger_commands::execute_load(int spacenum, const std::vector ¶ms) +bool debugger_commands::execute_load_try_memory(const std::vector ¶ms) { - u64 offset, length; - memory_region *region; + u64 offset = u64(-1); + memory_region *region = nullptr; + memory_share *share = nullptr; + if (!m_console.validate_address_with_memory_parameter(params[1], offset, region, share)) + return false; // not memory case + u64 length; // validate parameters - if (!m_console.validate_number_parameter(params[1], offset)) - return; - if (!m_console.validate_number_parameter(params[2], length)) - return; - if (!m_console.validate_memory_region_parameter(params[3], region)) - return; + if (offset == u64(-1) || !m_console.validate_number_parameter(params[2], length) || (region == nullptr && share == nullptr)) + return true; + + u32 msize; + u8 *base; + if (region != nullptr) + { + msize = region->bytes(); + base = region->base(); + } + else // if (share != nullptr) + { + msize = share->bytes(); + base = reinterpret_cast(share->ptr()); + } - if (offset >= region->bytes()) + if (offset >= msize) { m_console.printf("Invalid offset\n"); - return; + return true; } - if ((length <= 0) || ((length + offset) >= region->bytes())) - length = region->bytes() - offset; + if ((length <= 0) || ((length + offset) >= msize)) + length = msize - offset; // open the file std::string filename(params[0]); @@ -2279,7 +2321,7 @@ void debugger_commands::execute_loadregion(const std::vector & if (!f) { m_console.printf("Error opening file '%s'\n", params[0]); - return; + return true; } fseek(f, 0L, SEEK_END); @@ -2290,10 +2332,22 @@ void debugger_commands::execute_loadregion(const std::vector & if (length >= size) length = size; - fread(region->base() + offset, 1, length, f); + fread(base + offset, 1, length, f); fclose(f); m_console.printf("Data loaded successfully to memory : 0x%X to 0x%X\n", offset, offset + length - 1); + + return true; +} + + +/*------------------------------------------------- + execute_loadregion - execute the load command on region memory +-------------------------------------------------*/ + +void debugger_commands::execute_loadregion(const std::vector ¶ms) +{ + execute_load(-1, std::vector{ params[0], std::string(params[1]) + std::string(params[3]) + ".m", params[2] }); } @@ -2303,6 +2357,9 @@ void debugger_commands::execute_loadregion(const std::vector & void debugger_commands::execute_dump(int spacenum, const std::vector ¶ms) { + if (execute_dump_try_memory(params)) + return; + // validate parameters address_space *space; u64 offset; @@ -2460,6 +2517,172 @@ void debugger_commands::execute_dump(int spacenum, const std::vector ¶ms) +{ + u64 offset = u64(-1); + memory_region *region = nullptr; + memory_share *share = nullptr; + if (!m_console.validate_address_with_memory_parameter(params[1], offset, region, share)) + return false; // not memory case + + u64 length; + if (offset == u64(-1) || !m_console.validate_number_parameter(params[2], length) || (region == nullptr && share == nullptr)) + return true; + + u64 width = 0; + if (params.size() > 3 && !m_console.validate_number_parameter(params[3], width)) + return true; + + bool ascii = true; + if (params.size() > 4 && !m_console.validate_boolean_parameter(params[4], ascii)) + return true; + + u64 rowsize = 16; + if (params.size() > 5 && !m_console.validate_number_parameter(params[5], rowsize)) + return true; + + int shift = 0; + u64 granularity = shift >= 0 ? 1 : 1 << -shift; + + u32 msize; + u8 *base; + bool be; + if (region != nullptr) + { + msize = region->bytes(); + base = region->base(); + be = region->endianness() == ENDIANNESS_BIG; + if (width == 0) + width = region->bytewidth(); + } + else // if (share != nullptr) + { + msize = share->bytes(); + base = reinterpret_cast(share->ptr()); + be = share->endianness() == ENDIANNESS_BIG; + if (width == 0) + width = share->bytewidth(); + } + + if (offset >= msize) + { + m_console.printf("Invalid offset\n"); + return true; + } + if ((length <= 0) || ((length + offset) >= msize)) + length = msize - offset; + + // further validation + if (width != 1 && width != 2 && width != 4 && width != 8) + { + m_console.printf("Invalid width! (must be 1,2,4 or 8)\n"); + return true; + } + if (width < granularity) + { + m_console.printf("Invalid width! (must be at least %d)\n", granularity); + return true; + } + if (rowsize == 0 || (rowsize % width) != 0) + { + m_console.printf("Invalid row size! (must be a positive multiple of %d)\n", width); + return true; + } + + u64 endoffset = offset + length - 1; + + // open the file + std::string filename(params[0]); + FILE *const f = fopen(filename.c_str(), "w"); + if (!f) + { + m_console.printf("Error opening file '%s'\n", params[0]); + return true; + } + + // now write the data out + util::ovectorstream output; + output.reserve(200); + + const unsigned delta = (shift >= 0) ? (width << shift) : (width >> -shift); + + for (u64 i = offset; i <= endoffset; i += rowsize) + { + output.clear(); + output.rdbuf()->clear(); + + // print the address + util::stream_format(output, "%0*X: ", 8, i); + + // print the bytes + for (u64 j = 0; j < rowsize; j += delta) + { + if (i + j <= endoffset) + { + switch (width) + { + case 8: + util::stream_format(output, " %016X", get_u64be(&base[i+j])); + break; + case 4: + util::stream_format(output, " %08X", get_u32be(&base[i+j])); + break; + case 2: + util::stream_format(output, " %04X", get_u16be(&base[i+j])); + break; + case 1: + util::stream_format(output, " %02X", base[i+j]); + break; + } + } + else + util::stream_format(output, " %*s", width * 2, ""); + } + + // print the ASCII + if (ascii) + { + util::stream_format(output, " "); + for (u64 j = 0; j < rowsize && (i + j) <= endoffset; j += delta) + { + u64 data = 0; + switch (width) + { + case 8: + data = get_u64be(&base[i+j]); + break; + case 4: + data = get_u32be(&base[i+j]); + break; + case 2: + data = get_u16be(&base[i+j]); + break; + case 1: + data = base[i+j]; + break; + } + for (unsigned int b = 0; b != width; b++) { + u8 byte = data >> (8 * (be ? (width-1-b) : b)); + util::stream_format(output, "%c", (byte >= 32 && byte < 127) ? byte : '.'); + } + } + } + + // output the result + auto const &text = output.vec(); + fprintf(f, "%.*s\n", int(unsigned(text.size())), &text[0]); + } + + // close the file + fclose(f); + m_console.printf("Data dumped successfully\n"); + + return true; +} //------------------------------------------------- // execute_strdump - execute the strdump command @@ -2467,6 +2690,9 @@ void debugger_commands::execute_dump(int spacenum, const std::vector ¶ms) { + if (execute_strdump_try_memory(params)) + return; + // validate parameters u64 offset; if (!m_console.validate_number_parameter(params[1], offset)) @@ -2637,6 +2863,187 @@ void debugger_commands::execute_strdump(int spacenum, const std::vector ¶ms) +{ + u64 offset = u64(-1); + memory_region *region = nullptr; + memory_share *share = nullptr; + if (!m_console.validate_address_with_memory_parameter(params[1], offset, region, share)) + return false; // not memory case + + u64 length; + if (offset == u64(-1) || !m_console.validate_number_parameter(params[2], length) || (region == nullptr && share == nullptr)) + return true; + + u64 term = 0; + if (params.size() > 3 && !m_console.validate_number_parameter(params[3], term)) + return true; + + // further validation + if (term >= 0x100 && term != u64(-0x80)) + { + m_console.printf("Invalid termination character\n"); + return true; + } + + // open the file + std::string filename(params[0]); + FILE *f = fopen(filename.c_str(), "w"); + if (!f) + { + m_console.printf("Error opening file '%s'\n", params[0]); + return true; + } + + u32 msize; + u8 *base; + bool be; + u64 width; + if (region != nullptr) + { + msize = region->bytes(); + base = region->base(); + be = region->endianness() == ENDIANNESS_BIG; + width = region->bytewidth(); + } + else // if (share != nullptr) + { + msize = share->bytes(); + base = reinterpret_cast(share->ptr()); + be = share->endianness() == ENDIANNESS_BIG; + width = share->bytewidth(); + } + + if (offset >= msize) + { + m_console.printf("Invalid offset\n"); + return true; + } + if ((length <= 0) || ((length + offset) >= msize)) + length = msize - offset; + + // now write the data out + util::ovectorstream output; + output.reserve(200); + + bool terminated = true; + while (length-- != 0) + { + if (terminated) + { + terminated = false; + output.clear(); + output.rdbuf()->clear(); + + // print the address + util::stream_format(output, "%0*X: \"", 8, offset); + } + + // get the character data + u64 data = 0; + offs_t curaddr = offset; + switch (width) + { + case 1: + data = base[curaddr]; + break; + + case 2: + data = be ? get_u16be(&base[curaddr]) : get_u16le(&base[curaddr]); + break; + + case 4: + data = be ? get_u32be(&base[curaddr]) : get_u32le(&base[curaddr]); + break; + + case 8: + data = be ? get_u64be(&base[curaddr]) : get_u64le(&base[curaddr]); + break; + } + + // print the characters + for (int n = 0; n < width; n++) + { + // check for termination within word + if (terminated) + { + terminated = false; + + // output the result + auto const &text = output.vec(); + fprintf(f, "%.*s\"\n", int(unsigned(text.size())), &text[0]); + output.clear(); + output.rdbuf()->clear(); + + // print the address + util::stream_format(output, "%0*X.%d: \"", 8, offset, n); + } + + u8 ch = data & 0xff; + data >>= 8; + + // check for termination + if (term == u64(-0x80)) + { + if (BIT(ch, 7)) + { + terminated = true; + ch &= 0x7f; + } + } + else if (ch == term) + { + terminated = true; + continue; + } + + // check for non-ASCII characters + if (ch < 0x20 || ch >= 0x7f) + { + // use special or octal escape + if (ch >= 0x07 && ch <= 0x0d) + util::stream_format(output, "\\%c", "abtnvfr"[ch - 0x07]); + else + util::stream_format(output, "\\%03o", ch); + } + else + { + if (ch == '"' || ch == '\\') + output << '\\'; + output << char(ch); + } + } + + if (terminated) + { + // output the result + auto const &text = output.vec(); + fprintf(f, "%.*s\"\n", int(unsigned(text.size())), &text[0]); + output.clear(); + output.rdbuf()->clear(); + } + + offset += width; + } + + if (!terminated) + { + // output the result + auto const &text = output.vec(); + fprintf(f, "%.*s\"\\\n", int(unsigned(text.size())), &text[0]); + } + + // close the file + fclose(f); + m_console.printf("Data dumped successfully\n"); + + return true; +} + /*------------------------------------------------- execute_cheatrange - add a range to search for @@ -3139,6 +3546,9 @@ void debugger_commands::execute_cheatundo(const std::vector &p void debugger_commands::execute_find(int spacenum, const std::vector ¶ms) { + if (execute_find_try_memory(params)) + return; + u64 offset, length; address_space *space; @@ -3262,6 +3672,140 @@ void debugger_commands::execute_find(int spacenum, const std::vector ¶ms) +{ + u64 offset = u64(-1); + memory_region *region = nullptr; + memory_share *share = nullptr; + if (!m_console.validate_address_with_memory_parameter(params[0], offset, region, share)) + return false; // not memory case + + u64 length; + if (offset == u64(-1) || !m_console.validate_number_parameter(params[1], length) || (region == nullptr && share == nullptr)) + return true; + + u32 msize; + u8 *base; + bool be; + if (region != nullptr) + { + msize = region->bytes(); + base = region->base(); + be = region->endianness() == ENDIANNESS_BIG; + } + else // if (share != nullptr) + { + msize = share->bytes(); + base = reinterpret_cast(share->ptr()); + be = share->endianness() == ENDIANNESS_BIG; + } + + if (offset >= msize) + { + m_console.printf("Invalid offset\n"); + return true; + } + if ((length <= 0) || ((length + offset) >= msize)) + length = msize - offset; + + // further validation + u64 const endoffset = offset + length - 1; + int cur_data_size = 1; + + // parse the data parameters + u64 data_to_find[256]; + u8 data_size[256]; + int data_count = 0; + for (int i = 2; i < params.size(); i++) + { + std::string_view pdata = params[i]; + + 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]; + data_size[data_count++] = 1; + } + } + else // otherwise, validate as a number + { + // check for a 'b','w','d',or 'q' prefix + data_size[data_count] = cur_data_size; + 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 (pdata == "?") + data_size[data_count++] |= 0x10; + + // otherwise, validate as a number + else if (!m_console.validate_number_parameter(pdata, data_to_find[data_count++])) + return true; + } + } + + // now search + int found = 0; + for (u64 i = offset; i <= endoffset; i += data_size[0]) + { + int suboffset = 0; + bool match = true; + + // find the entire string + for (int j = 0; j < data_count && match; j++) + { + offs_t address = i + suboffset; + switch (data_size[j]) + { + case 1: + match = u8(data_to_find[j]) == base[address]; + break; + + case 2: + match = u16(data_to_find[j]) == (be ? get_u16be(&base[address]) : get_u16le(&base[address])); + break; + + case 4: + match = u32(data_to_find[j]) == (be ? get_u32be(&base[address]) : get_u32le(&base[address])); + break; + + case 8: + match = u64(data_to_find[j]) == (be ? get_u64be(&base[address]) : get_u64le(&base[address])); + break; + + default: + // all other cases are wildcards + break; + } + suboffset += data_size[j] & 0x0f; + } + + // did we find it? + if (match) + { + found++; + m_console.printf("Found at %04X\n", i); + } + } + + // print something if not found + if (found == 0) + m_console.printf("Not found\n"); + + return true; +} + //------------------------------------------------- // execute_fill - execute the fill command @@ -3269,6 +3813,9 @@ void debugger_commands::execute_find(int spacenum, const std::vector ¶ms) { + if (execute_fill_try_memory(params)) + return; + u64 offset, length; address_space *space; @@ -3371,6 +3918,139 @@ void debugger_commands::execute_fill(int spacenum, const std::vector ¶ms) +{ + u64 offset = u64(-1); + memory_region *region = nullptr; + memory_share *share = nullptr; + if (!m_console.validate_address_with_memory_parameter(params[0], offset, region, share)) + return false; // not memory case + + u64 length; + if (offset == u64(-1) || !m_console.validate_number_parameter(params[1], length) || (region == nullptr && share == nullptr)) + return true; + + u32 msize; + u8 *base; + bool be; + if (region != nullptr) + { + msize = region->bytes(); + base = region->base(); + be = region->endianness() == ENDIANNESS_BIG; + } + else // if (share != nullptr) + { + msize = share->bytes(); + base = reinterpret_cast(share->ptr()); + be = share->endianness() == ENDIANNESS_BIG; + } + + if (offset >= msize) + { + m_console.printf("Invalid offset\n"); + return true; + } + if ((length <= 0) || ((length + offset) >= msize)) + length = msize - offset; + + // further validation + int cur_data_size = 1; + + // parse the data parameters + u64 fill_data[256]; + u8 fill_data_size[256]; + int data_count = 0; + for (int i = 2; i < params.size(); i++) + { + std::string_view pdata = params[i]; + + // check for a string + if (!pdata.empty() && pdata.front() == '"' && pdata.back() == '"') + { + auto const pdatalen = pdata.length() - 1; + for (int j = 1; j < pdatalen; j++) + { + fill_data[data_count] = pdata[j]; + fill_data_size[data_count++] = 1; + } + } + + // otherwise, validate as a number + else + { + // check for a 'b','w','d',or 'q' prefix + fill_data_size[data_count] = cur_data_size; + if (pdata.length() >= 2) + { + if (tolower(u8(pdata[0])) == 'b' && pdata[1] == '.') { fill_data_size[data_count] = cur_data_size = 1; pdata.remove_prefix(2); } + if (tolower(u8(pdata[0])) == 'w' && pdata[1] == '.') { fill_data_size[data_count] = cur_data_size = 2; pdata.remove_prefix(2); } + if (tolower(u8(pdata[0])) == 'd' && pdata[1] == '.') { fill_data_size[data_count] = cur_data_size = 4; pdata.remove_prefix(2); } + if (tolower(u8(pdata[0])) == 'q' && pdata[1] == '.') { fill_data_size[data_count] = cur_data_size = 8; pdata.remove_prefix(2); } + } + + // validate as a number + if (!m_console.validate_number_parameter(pdata, fill_data[data_count++])) + return true; + } + } + if (data_count == 0) + return true; + + // now fill memory + u64 count = length; + while (count != 0) + { + // write the entire string + for (int j = 0; j < data_count; j++) + { + offs_t address = offset; + switch (fill_data_size[j]) + { + case 1: + base[address] = u8(fill_data[j]); + break; + + case 2: + if (be) + put_u16be(&base[address], u16(fill_data[j])); + else + put_u16le(&base[address], u16(fill_data[j])); + break; + + case 4: + if (be) + put_u32be(&base[address], u32(fill_data[j])); + else + put_u32le(&base[address], u32(fill_data[j])); + break; + + case 8: + if (be) + put_u64be(&base[address], u64(fill_data[j])); + else + put_u64le(&base[address], u64(fill_data[j])); + break; + } + + offset += fill_data_size[j]; + if (count <= fill_data_size[j]) + { + count = 0; + break; + } + else + count -= fill_data_size[j]; + } + } + + return true; +} + /*------------------------------------------------- execute_dasm - execute the dasm command diff --git a/src/emu/debug/debugcmd.h b/src/emu/debug/debugcmd.h index cf38c6d903a27..0836790e404a3 100644 --- a/src/emu/debug/debugcmd.h +++ b/src/emu/debug/debugcmd.h @@ -127,18 +127,24 @@ class debugger_commands void execute_stateload(const std::vector ¶ms); void execute_rewind(const std::vector ¶ms); void execute_save(int spacenum, const std::vector ¶ms); + bool execute_save_try_memory(const std::vector ¶ms); void execute_saveregion(const std::vector ¶ms); void execute_load(int spacenum, const std::vector ¶ms); + bool execute_load_try_memory(const std::vector ¶ms); void execute_loadregion(const std::vector ¶ms); void execute_dump(int spacenum, const std::vector ¶ms); + bool execute_dump_try_memory(const std::vector ¶ms); void execute_strdump(int spacenum, const std::vector ¶ms); + bool execute_strdump_try_memory(const std::vector ¶ms); void execute_cheatrange(bool init, const std::vector ¶ms); void execute_cheatnext(bool initial, const std::vector ¶ms); void execute_cheatlist(const std::vector ¶ms); void execute_cheatundo(const std::vector ¶ms); void execute_dasm(const std::vector ¶ms); void execute_find(int spacenum, const std::vector ¶ms); + bool execute_find_try_memory(const std::vector ¶ms); void execute_fill(int spacenum, const std::vector ¶ms); + bool execute_fill_try_memory(const std::vector ¶ms); void execute_trace(const std::vector ¶ms, bool trace_over); void execute_traceflush(const std::vector ¶ms); void execute_history(const std::vector ¶ms); diff --git a/src/emu/debug/debugcon.cpp b/src/emu/debug/debugcon.cpp index ec5bc15475101..f9076dda5235e 100644 --- a/src/emu/debug/debugcon.cpp +++ b/src/emu/debug/debugcon.cpp @@ -24,6 +24,7 @@ #include #include #include +#include /*************************************************************************** @@ -883,7 +884,7 @@ bool debugger_console::validate_device_space_parameter(std::string_view param, i /// 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. +/// device parameters are 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 @@ -916,6 +917,40 @@ bool debugger_console::validate_target_address_parameter(std::string_view param, } +/// \brief Validate a parameter as a address with memory region or share name +/// +/// Validates a parameter as a address with memory region or share tag. addres +/// parameters stays unchanged if invalid and requires additional validation on +/// the caller side. +/// \param [in] The parameter string. +/// \param [out] addr The address on success, or unchanged on failure. +/// \param [out] region The region on success, or unchanged on failure. +/// \param [out] share The share 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_address_with_memory_parameter(std::string_view param, u64 &addr, memory_region *®ion, memory_share *&share) +{ + std::string str(param); + std::regex re("^([^:]+)(:.+)\\.([ms])$"); + std::smatch m; + if (std::regex_match(str, m, re)) + { + if ('m' == m[3]) + validate_memory_region_parameter(m.str(2), region); + else if ('s' == m[3]) + validate_memory_share_parameter(m.str(2), share); + else + return false; + + validate_number_parameter(m.str(1), addr); + + return true; + } + + return false; +} + + /// \brief Validate a parameter as a memory region /// /// Validates a parameter as a memory region tag and retrieves the @@ -944,6 +979,34 @@ bool debugger_console::validate_memory_region_parameter(std::string_view param, } +/// \brief Validate a parameter as a memory share +/// +/// Validates a parameter as a memory share tag and retrieves the +/// specified memory share. +/// \param [in] The parameter string. +/// \param [out] result The memory share on success, or unchanged on +/// failure. +/// \return true if the parameter refers to a memory share in the +/// current system, or false otherwise. +bool debugger_console::validate_memory_share_parameter(std::string_view param, memory_share *&result) +{ + auto const &shares = m_machine.memory().shares(); + std::string_view relative = param; + device_t &base = get_device_search_base(relative); + auto const iter = shares.find(base.subtag(strmakelower(relative))); + if (shares.end() != iter) + { + result = iter->second.get(); + return true; + } + else + { + printf("No matching memory share 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 diff --git a/src/emu/debug/debugcon.h b/src/emu/debug/debugcon.h index 37b2e235348cc..b1cef0f7c847d 100644 --- a/src/emu/debug/debugcon.h +++ b/src/emu/debug/debugcon.h @@ -130,9 +130,15 @@ class debugger_console // 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 address with memory region or share name and retrieves the given address and region or share + bool validate_address_with_memory_parameter(std::string_view param, u64 &addr, memory_region *®ion, memory_share *&share); + // 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 memory region name and retrieves the given share + bool validate_memory_share_parameter(std::string_view param, memory_share *&result); + // validates a parameter as a debugger expression bool validate_expression_parameter(std::string_view param, parsed_expression &result); diff --git a/src/emu/debug/debughlp.cpp b/src/emu/debug/debughlp.cpp index 8e5fa1b9c64f2..e78a31d7568fd 100644 --- a/src/emu/debug/debughlp.cpp +++ b/src/emu/debug/debughlp.cpp @@ -94,26 +94,32 @@ const help_item f_static_help_list[] = "\n" " dasm ,
,[,[,]] -- disassemble to the given file\n" " f[ind]
,[,[,...]] -- search memory for data\n" + " f[ind]
:.{m|s},[,[,...]] -- search memory region or share for data\n" " f[ind]d
,[,[,...]] -- search data memory for data\n" " f[ind]i
,[,[,...]] -- search I/O memory for data\n" " fill
,[,[,...]] -- fill memory with data\n" + " fill
:.{m|s},[,[,...]] -- fill memory region or share with data\n" " filld
[:],[,[,...]] -- fill data memory with data\n" " filli
[:],[,[,...][ -- fill I/O memory with data\n" " fillo
[:],[,[,...][ -- fill opcode memory with data\n" " dump ,
[:],[,[,[,]]] -- dump memory as text\n" + " dump ,
:.{m|s},[,[,[,]]] -- dump memory region or share as text\n" " dumpd ,
[:],[,[,[,]]] -- dump data memory as text\n" " dumpi ,
[:],[,[,[,]]] -- dump I/O memory as text\n" " dumpo ,
[:],[,[,[,]]] -- dump opcodes memory as text\n" " strdump ,
[:],[,] -- dump ASCII strings from memory\n" + " strdump ,
:.{m|s},[,] -- dump ASCII strings from memory region or share\n" " strdumpd ,
[:],[,] -- dump ASCII strings from data memory\n" " strdumpi ,
[:],[,] -- dump ASCII strings from I/O memory\n" " strdumpo ,
[:],[,] -- dump ASCII strings from opcodes memory\n" " save ,
[:], -- save binary memory to the given file\n" + " save ,
:.{m|s}, -- load binary memory region or share to the given file\n" " saved ,
[:], -- save binary data memory to the given file\n" " savei ,
[:], -- save binary I/O memory to the given file\n" " saveo ,
[:], -- save binary opcode memory to the given file\n" " saver ,
[:],, -- save binary memory region to the given file\n" " load ,
[:][,] -- load binary memory from the given file\n" + " load ,
:.{m|s}[,] -- load binary memory region or share from the given file\n" " loadd ,
[:][,] -- load binary data memory from the given file\n" " loadi ,
[:][,] -- load binary I/O memory from the given file\n" " loado ,
[:][,] -- load binary opcode memory from the given file\n" @@ -633,6 +639,7 @@ const help_item f_static_help_list[] = "find", "\n" " f[ind][{d|i|o}]
[:],[,[,...]]\n" + " f[ind]
:.{m|s},[,[,...]]\n" "\n" "The find commands search through memory for the specified sequence of data. The
" "is the address to begin searching from, optionally followed by a device and/or address " @@ -674,6 +681,7 @@ const help_item f_static_help_list[] = "fill", "\n" " fill[{d|i|o}]
[:],[,[,...]]\n" + " fill
:.{m|s},[,[,...]]\n" "\n" "The fill commands overwrite a block of memory with copies of the supplied data sequence. " "The
specifies the address to begin writing at, optionally followed by a device " @@ -700,6 +708,7 @@ const help_item f_static_help_list[] = "dump", "\n" " dump[{d|i|o}] ,
[:],[,[,[,]]]\n" + " dump ,
:.{m|s},[,[,[,]]]\n" "\n" "The dump commands dump memory to the text file specified in the parameter. The " "
specifies the address to start dumping from, optionally followed by a device " @@ -739,6 +748,7 @@ const help_item f_static_help_list[] = "strdump", "\n" " strdump[{d|i|o}] ,
[:],[,]\n" + " strdump ,
:.{m|s},[,]\n" "\n" "The strdump commands dump memory to the text file specified in the parameter. " "The
specifies the address to start dumping from, optionally followed by a device " @@ -761,6 +771,7 @@ const help_item f_static_help_list[] = "save", "\n" " save[{d|i|o}] ,
[:],\n" + " save ,
:.{m|s},\n" "\n" "The save commands save raw memory to the binary file specified in the " "parameter. The
specifies the address to start saving from, optionally followed " @@ -798,6 +809,9 @@ const help_item f_static_help_list[] = "saving from, and the specifies how much memory to save. The range
" "through
+-1, inclusive, will be output to the file.\n" "\n" + "Alternetevely use 'save' syntax:\n" + " save ,
:.m,\n" + "\n" "Examples:\n" "\n" "saver data.bin,200,100,:monitor\n" @@ -811,6 +825,7 @@ const help_item f_static_help_list[] = "load", "\n" " load[{d|i|o}] ,
[:][,]\n" + " load ,
:.{m|s}[,]\n" "\n" "The load commands load raw memory from the binary file specified in the " "parameter. The
specifies the address to start loading to, optionally followed " @@ -857,6 +872,9 @@ const help_item f_static_help_list[] = "is zero, or is greater than the total length of the file, the entire contents of the file " "will be loaded but no more.\n" "\n" + "Alternetevely use 'load' syntax:\n" + " load ,
:.m[,]\n" + "\n" "Examples:\n" "\n" "loadr data.bin,200,100,:monitor\n" diff --git a/src/emu/debug/express.cpp b/src/emu/debug/express.cpp index 219ed6a89ef9a..4408d62146076 100644 --- a/src/emu/debug/express.cpp +++ b/src/emu/debug/express.cpp @@ -319,7 +319,7 @@ std::string expression_error::code_string() const case TOO_MANY_STRINGS: return "too many strings"; case INVALID_MEMORY_SIZE: return "invalid memory size (b/w/d/q expected)"; case NO_SUCH_MEMORY_SPACE: return "non-existent memory space"; - case INVALID_MEMORY_SPACE: return "invalid memory space (p/d/i/o/r/m expected)"; + case INVALID_MEMORY_SPACE: return "invalid memory space (p/d/i/o/r/m/s expected)"; case INVALID_MEMORY_NAME: return "invalid memory name"; case MISSING_MEMORY_NAME: return "missing memory name"; default: return "unknown error"; @@ -673,6 +673,11 @@ u64 symbol_table::memory_value(const char *name, expression_space spacenum, u32 return read_memory_region(name, address, size); break; + case EXPSPACE_SHARE: + if (name) + return read_memory_share(name, address, size); + break; + default: break; } @@ -782,6 +787,55 @@ u64 symbol_table::read_memory_region(const char *rgntag, offs_t address, int siz } +//------------------------------------------------- +// read_memory_share - read memory from a +// memory share +//------------------------------------------------- + +u64 symbol_table::read_memory_share(const char *shatag, offs_t address, int size) +{ + auto search = get_device_search(m_machine, m_memintf, shatag); + memory_share *const share = search.first.memshare(search.second); + u64 result = ~u64(0) >> (64 - 8*size); + + // make sure we get a valid base before proceeding + if (share) + { + // call ourself recursively until we are byte-sized + if (size > 1) + { + int halfsize = size / 2; + u64 r0, r1; + + // read each half, from lower address to upper address + r0 = read_memory_share(shatag, address + 0, halfsize); + r1 = read_memory_share(shatag, address + halfsize, halfsize); + + // assemble based on the target endianness + if (share->endianness() == ENDIANNESS_LITTLE) + result = r0 | (r1 << (8 * halfsize)); + else + result = r1 | (r0 << (8 * halfsize)); + } + + // only process if we're within range + else if (address < share->bytes()) + { + // lowmask specified which address bits are within the databus width + u32 lowmask = share->bytewidth() - 1; + u8 *base = reinterpret_cast(share->ptr()) + (address & ~lowmask); + + // if we have a valid base, return the appropriate byte + if (share->endianness() == ENDIANNESS_LITTLE) + result = base[BYTE8_XOR_LE(address) & lowmask]; + else + result = base[BYTE8_XOR_BE(address) & lowmask]; + } + } + return result; +} + + //------------------------------------------------- // set_memory_value - write 1,2,4 or 8 bytes at // the given offset in the given address space @@ -831,6 +885,11 @@ void symbol_table::set_memory_value(const char *name, expression_space spacenum, write_memory_region(name, address, size, data); break; + case EXPSPACE_SHARE: + if (name) + write_memory_share(name, address, size, data); + break; + default: break; } @@ -953,6 +1012,65 @@ void symbol_table::write_memory_region(const char *rgntag, offs_t address, int s } +//------------------------------------------------- +// write_memory_share - write memory to a +// memory share +//------------------------------------------------- + +void symbol_table::write_memory_share(const char *shatag, offs_t address, int size, u64 data) +{ + auto search = get_device_search(m_machine, m_memintf, shatag); + memory_share *const share = search.first.memshare(search.second); + + // make sure we get a valid base before proceeding + if (share) + { + // call ourself recursively until we are byte-sized + if (size > 1) + { + int halfsize = size / 2; + + // break apart based on the target endianness + u64 halfmask = ~u64(0) >> (64 - 8 * halfsize); + u64 r0, r1; + if (share->endianness() == ENDIANNESS_LITTLE) + { + r0 = data & halfmask; + r1 = (data >> (8 * halfsize)) & halfmask; + } + else + { + r0 = (data >> (8 * halfsize)) & halfmask; + r1 = data & halfmask; + } + + // write each half, from lower address to upper address + write_memory_share(shatag, address + 0, halfsize, r0); + write_memory_share(shatag, address + halfsize, halfsize, r1); + } + + // only process if we're within range + else if (address < share->bytes()) + { + // lowmask specified which address bits are within the databus width + u32 lowmask = share->bytewidth() - 1; + u8 *base = reinterpret_cast(share->ptr()) + (address & ~lowmask); + + // if we have a valid base, set the appropriate byte + if (share->endianness() == ENDIANNESS_LITTLE) + { + base[BYTE8_XOR_LE(address) & lowmask] = data; + } + else + { + base[BYTE8_XOR_BE(address) & lowmask] = data; + } + notify_memory_modified(); + } + } +} + + //------------------------------------------------- // memory_valid - return true if the given // memory name/space/offset combination is valid @@ -998,6 +1116,20 @@ expression_error::error_code symbol_table::memory_valid(const char *name, expres } break; + case EXPSPACE_SHARE: + if (!name) + { + return expression_error::MISSING_MEMORY_NAME; + } + else + { + auto search = get_device_search(m_machine, m_memintf, name); + memory_share *const share = search.first.memshare(search.second); + if (!share || !share->ptr()) + return expression_error::INVALID_MEMORY_NAME; + } + break; + default: return expression_error::NO_SUCH_MEMORY_SPACE; } @@ -1618,6 +1750,7 @@ void parsed_expression::parse_memory_operator(parse_token &token, const char *st case 'r': memspace = EXPSPACE_PRGDIRECT; break; case 'o': memspace = EXPSPACE_OPDIRECT; break; case 'm': memspace = EXPSPACE_REGION; break; + case 's': memspace = EXPSPACE_SHARE; break; default: throw expression_error(expression_error::INVALID_MEMORY_SPACE, token.offset() + (string - startstring)); } diff --git a/src/emu/debug/express.h b/src/emu/debug/express.h index 33ab9c3b472b9..f9e3983c053fc 100644 --- a/src/emu/debug/express.h +++ b/src/emu/debug/express.h @@ -41,7 +41,8 @@ enum expression_space EXPSPACE_OPCODE_PHYSICAL, EXPSPACE_PRGDIRECT, EXPSPACE_OPDIRECT, - EXPSPACE_REGION + EXPSPACE_REGION, + EXPSPACE_SHARE }; @@ -215,8 +216,10 @@ class symbol_table // memory helpers u64 read_program_direct(address_space &space, int opcode, offs_t address, int size); u64 read_memory_region(const char *rgntag, offs_t address, int size); + u64 read_memory_share(const char *shatag, offs_t address, int size); void write_program_direct(address_space &space, int opcode, offs_t address, int size, u64 data); void write_memory_region(const char *rgntag, offs_t address, int size, u64 data); + void write_memory_share(const char *shatag, offs_t address, int size, u64 data); expression_error expression_get_space(const char *tag, int &spacenum, device_memory_interface *&memory); void notify_memory_modified();