emu/debug/debugcmd.cpp: Add NUL-terminated string argument support to printf/logerror. (#12124)

* Use %s to read NUL-terminated strings from emulated memory.  Precision sets maximum length.
* Also added left-justification option for numeric and string formats.
* Made documentation more consistent and removed duplication.
This commit is contained in:
Patrick Mackinlay 2024-03-20 23:12:11 +07:00 committed by GitHub
parent 80e9125a5b
commit f93a04010a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 98 additions and 43 deletions

View File

@ -190,18 +190,22 @@ available:
%c %c
Prints the corresponding argument as an 8-bit character. Prints the corresponding argument as an 8-bit character.
%[0][<n>]d %[-][0][<n>]d
Prints the corresponding argument as a decimal number with optional Prints the corresponding argument as a decimal number with optional
minimum field width and zero fill. left justification, zero fill and minimum field width.
%[0][<n>]o %[-][0][<n>]o
Prints the corresponding argument as an octal number with optional Prints the corresponding argument as an octal number with optional
minimum field width and zero fill using lowercase letters. left justification, zero fill and minimum field width.
%[0][<n>]x %[-][0][<n>]x
Prints the corresponding argument as a hexadecimal number with Prints the corresponding argument as a lowercase hexadecimal number
optional minimum field width and zero fill using lowercase letters. with optional left justification, zero fill and minimum field width.
%[0][<n>]X %[-][0][<n>]X
Prints the corresponding argument as a hexadecimal number with Prints the corresponding argument as an uppercase hexadecimal number
optional minimum field width and zero fill using uppercase letters. with optional left justification, zero fill and minimum field width.
%[-][<n>][.[<n>]]s
Prints a null-terminated string of 8-bit characters from the address
and address space given by the corresponding argument, with optional
left justification, minimum and maximum field widths.
\%% \%%
Prints a literal percent symbol. Prints a literal percent symbol.
\\n \\n

View File

@ -471,7 +471,7 @@ void debugger_commands::execute_print(const std::vector<std::string_view> &param
bool debugger_commands::mini_printf(std::ostream &stream, const std::vector<std::string_view> &params) bool debugger_commands::mini_printf(std::ostream &stream, const std::vector<std::string_view> &params)
{ {
std::string_view format(params[0]); std::string_view const format(params[0]);
auto f = format.begin(); auto f = format.begin();
int param = 1; int param = 1;
@ -499,21 +499,48 @@ bool debugger_commands::mini_printf(std::ostream &stream, const std::vector<std:
// formatting // formatting
else if (c == '%') else if (c == '%')
{ {
bool left_justify = false;
bool zero_fill = false;
int width = 0; int width = 0;
int zerofill = 0; int precision = 0;
// parse out the width // parse optional left justification flag
while (f != format.end() && *f >= '0' && *f <= '9') if (f != format.end() && *f == '-')
{ {
c = *f++; left_justify = true;
if (c == '0' && width == 0) f++;
zerofill = 1; }
width = width * 10 + (c - '0');
// 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 (f == format.end()) break;
// get the format
c = *f++;
switch (c) switch (c)
{ {
case '%': case '%':
@ -522,7 +549,7 @@ bool debugger_commands::mini_printf(std::ostream &stream, const std::vector<std:
case 'X': case 'X':
if (param < params.size() && m_console.validate_number_parameter(params[param++], number)) if (param < params.size() && m_console.validate_number_parameter(params[param++], number))
util::stream_format(stream, zerofill ? "%0*X" : "%*X", width, number); util::stream_format(stream, zero_fill ? "%0*X" : "%*X", width, number);
else else
{ {
m_console.printf("Not enough parameters for format!\n"); m_console.printf("Not enough parameters for format!\n");
@ -531,7 +558,7 @@ bool debugger_commands::mini_printf(std::ostream &stream, const std::vector<std:
break; break;
case 'x': case 'x':
if (param < params.size() && m_console.validate_number_parameter(params[param++], number)) if (param < params.size() && m_console.validate_number_parameter(params[param++], number))
util::stream_format(stream, zerofill ? "%0*x" : "%*x", width, number); util::stream_format(stream, zero_fill ? "%0*x" : "%*x", width, number);
else else
{ {
m_console.printf("Not enough parameters for format!\n"); m_console.printf("Not enough parameters for format!\n");
@ -542,7 +569,7 @@ bool debugger_commands::mini_printf(std::ostream &stream, const std::vector<std:
case 'O': case 'O':
case 'o': case 'o':
if (param < params.size() && m_console.validate_number_parameter(params[param++], number)) if (param < params.size() && m_console.validate_number_parameter(params[param++], number))
util::stream_format(stream, zerofill ? "%0*o" : "%*o", width, number); util::stream_format(stream, zero_fill ? "%0*o" : "%*o", width, number);
else else
{ {
m_console.printf("Not enough parameters for format!\n"); m_console.printf("Not enough parameters for format!\n");
@ -553,7 +580,7 @@ bool debugger_commands::mini_printf(std::ostream &stream, const std::vector<std:
case 'D': case 'D':
case 'd': case 'd':
if (param < params.size() && m_console.validate_number_parameter(params[param++], number)) if (param < params.size() && m_console.validate_number_parameter(params[param++], number))
util::stream_format(stream, zerofill ? "%0*d" : "%*d", width, number); util::stream_format(stream, zero_fill ? "%0*d" : "%*d", width, number);
else else
{ {
m_console.printf("Not enough parameters for format!\n"); m_console.printf("Not enough parameters for format!\n");
@ -572,6 +599,38 @@ bool debugger_commands::mini_printf(std::ostream &stream, const std::vector<std:
} }
break; 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;
} }
} }

View File

@ -359,11 +359,12 @@ 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 " "The printf command performs a C-style printf to the debugger console. Only a very limited set of "
"formatting options are available:\n" "formatting options are available:\n"
"\n" "\n"
" %c -- 8-bit character\n" " %c -- 8-bit character\n"
" %[0][<n>]d -- decimal number with optional digit count and zero-fill\n" " %[-][0][<n>]d -- decimal number with optional left justification, zero fill and minimum width\n"
" %[0][<n>]o -- octal number with optional digit count and zero-fill\n" " %[-][0][<n>]o -- octal number with optional left justification, zero fill and minimum width\n"
" %[0][<n>]x -- hexadecimal number with optional digit count and zero-fill (lowercase digits)\n" " %[-][0][<n>]x -- lowercase hexadecimal number with optional left justification, zero fill and minimum width\n"
" %[0][<n>]X -- hexadecimal number with optional digit count and zero-fill (uppercase digits)\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" "\n"
"All remaining formatting options are ignored. Use %% to output a % character. Multiple lines can be " "All remaining formatting options are ignored. Use %% to output a % character. Multiple lines can be "
"printed by embedding a \\n in the text.\n" "printed by embedding a \\n in the text.\n"
@ -371,7 +372,7 @@ const help_item f_static_help_list[] =
"Examples:\n" "Examples:\n"
"\n" "\n"
"printf \"PC=%04X\",pc\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" "\n"
"printf \"A=%d, B=%d\\nC=%d\",a,b,a+b\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" " Prints A=<aval>, B=<bval> on one line, and C=<a+bval> on a second line.\n"
@ -382,21 +383,12 @@ const help_item f_static_help_list[] =
" logerror <format>[,<item>[,...]]\n" " logerror <format>[,<item>[,...]]\n"
"\n" "\n"
"The logerror command performs a C-style printf to the error log. Only a very limited set of " "The logerror command performs a C-style printf to the error log. Only a very limited set of "
"formatting options are available:\n" "formatting options are available. See the 'printf' help for details.\n"
"\n"
" %c -- 8-bit character\n"
" %[0][<n>]d -- decimal number with optional digit count and zero-fill\n"
" %[0][<n>]o -- octal number with optional digit count and zero-fill\n"
" %[0][<n>]x -- hexadecimal number with optional digit count and zero-fill (lowercase digits)\n"
" %[0][<n>]X -- hexadecimal number with optional digit count and zero-fill (uppercase digits)\n"
"\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" "\n"
"Examples:\n" "Examples:\n"
"\n" "\n"
"logerror \"PC=%04X\",pc\n" "logerror \"PC=%04x\",pc\n"
" Logs PC=<pcval> where <pcval> is displayed in hexadecimal with 4 digits with zero-fill.\n" " Logs PC=<pcval> where <pcval> is displayed in lowercase hexadecimal with 4 digits and zero fill.\n"
"\n" "\n"
"logerror \"A=%d, B=%d\\nC=%d\",a,b,a+b\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" " Logs A=<aval>, B=<bval> on one line, and C=<a+bval> on a second line.\n"