Added modifier syntax to debugger: Changed watch's syntax, added format modifier to print/eval, added count option to examine command.

This commit is contained in:
Lior Halphon 2016-10-19 23:48:46 +03:00
parent 18ec502cfe
commit ee51dec20e
1 changed files with 149 additions and 48 deletions

View File

@ -593,7 +593,7 @@ value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
} }
struct debugger_command_s; struct debugger_command_s;
typedef bool debugger_command_imp_t(GB_gameboy_t *gb, char *arguments, const struct debugger_command_s *command); typedef bool debugger_command_imp_t(GB_gameboy_t *gb, char *arguments, char *modifiers, const struct debugger_command_s *command);
typedef struct debugger_command_s { typedef struct debugger_command_s {
const char *command; const char *command;
@ -601,6 +601,7 @@ typedef struct debugger_command_s {
debugger_command_imp_t *implementation; debugger_command_imp_t *implementation;
const char *help_string; // Null if should not appear in help const char *help_string; // Null if should not appear in help
const char *arguments_format; // For usage message const char *arguments_format; // For usage message
const char *modifiers_format; // For usage message
} debugger_command_t; } debugger_command_t;
static const char *lstrip(const char *str) static const char *lstrip(const char *str)
@ -617,18 +618,30 @@ GB_log(gb, "Program is running. \n"); \
return false; \ return false; \
} }
static void print_usage(GB_gameboy_t *gb, const debugger_command_t *command) #define NO_MODIFIERS \
{ if (modifiers) { \
if (command->arguments_format) { print_usage(gb, command); \
GB_log(gb, "Usage: %s %s\n", command->command, command->arguments_format); return true; \
}
else {
GB_log(gb, "Usage: %s\n", command->command);
}
} }
static bool cont(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command) static void print_usage(GB_gameboy_t *gb, const debugger_command_t *command)
{ {
GB_log(gb, "Usage: %s", command->command);
if (command->arguments_format) {
GB_log(gb, "[/%s]", command->modifiers_format);
}
if (command->arguments_format) {
GB_log(gb, " %s", command->arguments_format);
}
GB_log(gb, "\n");
}
static bool cont(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
{
NO_MODIFIERS
STOPPED_ONLY STOPPED_ONLY
if (strlen(lstrip(arguments))) { if (strlen(lstrip(arguments))) {
@ -640,8 +653,9 @@ static bool cont(GB_gameboy_t *gb, char *arguments, const debugger_command_t *co
return false; return false;
} }
static bool next(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command) static bool next(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
{ {
NO_MODIFIERS
STOPPED_ONLY STOPPED_ONLY
if (strlen(lstrip(arguments))) { if (strlen(lstrip(arguments))) {
@ -655,8 +669,9 @@ static bool next(GB_gameboy_t *gb, char *arguments, const debugger_command_t *co
return false; return false;
} }
static bool step(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command) static bool step(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
{ {
NO_MODIFIERS
STOPPED_ONLY STOPPED_ONLY
if (strlen(lstrip(arguments))) { if (strlen(lstrip(arguments))) {
@ -667,8 +682,9 @@ static bool step(GB_gameboy_t *gb, char *arguments, const debugger_command_t *co
return false; return false;
} }
static bool finish(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command) static bool finish(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
{ {
NO_MODIFIERS
STOPPED_ONLY STOPPED_ONLY
if (strlen(lstrip(arguments))) { if (strlen(lstrip(arguments))) {
@ -682,8 +698,9 @@ static bool finish(GB_gameboy_t *gb, char *arguments, const debugger_command_t *
return false; return false;
} }
static bool stack_leak_detection(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command) static bool stack_leak_detection(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
{ {
NO_MODIFIERS
STOPPED_ONLY STOPPED_ONLY
if (strlen(lstrip(arguments))) { if (strlen(lstrip(arguments))) {
@ -697,8 +714,9 @@ static bool stack_leak_detection(GB_gameboy_t *gb, char *arguments, const debugg
return false; return false;
} }
static bool registers(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command) static bool registers(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
{ {
NO_MODIFIERS
if (strlen(lstrip(arguments))) { if (strlen(lstrip(arguments))) {
print_usage(gb, command); print_usage(gb, command);
return true; return true;
@ -739,8 +757,9 @@ static uint16_t find_breakpoint(GB_gameboy_t *gb, value_t addr)
return (uint16_t) min; return (uint16_t) min;
} }
static bool breakpoint(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command) static bool breakpoint(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
{ {
NO_MODIFIERS
if (strlen(lstrip(arguments)) == 0) { if (strlen(lstrip(arguments)) == 0) {
print_usage(gb, command); print_usage(gb, command);
return true; return true;
@ -804,8 +823,9 @@ static bool breakpoint(GB_gameboy_t *gb, char *arguments, const debugger_command
return true; return true;
} }
static bool delete(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command) static bool delete(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
{ {
NO_MODIFIERS
if (strlen(lstrip(arguments)) == 0) { if (strlen(lstrip(arguments)) == 0) {
for (unsigned i = gb->n_breakpoints; i--;) { for (unsigned i = gb->n_breakpoints; i--;) {
if (gb->breakpoints[i].condition) { if (gb->breakpoints[i].condition) {
@ -864,7 +884,7 @@ static uint16_t find_watchpoint(GB_gameboy_t *gb, value_t addr)
return (uint16_t) min; return (uint16_t) min;
} }
static bool watch(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command) static bool watch(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
{ {
if (strlen(lstrip(arguments)) == 0) { if (strlen(lstrip(arguments)) == 0) {
print_usage: print_usage:
@ -877,9 +897,13 @@ print_usage:
return true; return true;
} }
if (!modifiers) {
modifiers = "w";
}
uint8_t flags = 0; uint8_t flags = 0;
while (*arguments != ' ' && *arguments) { while (*modifiers) {
switch (*arguments) { switch (*modifiers) {
case 'r': case 'r':
flags |= GB_WATCHPOINT_R; flags |= GB_WATCHPOINT_R;
break; break;
@ -889,7 +913,7 @@ print_usage:
default: default:
goto print_usage; goto print_usage;
} }
arguments++; modifiers++;
} }
if (!flags) { if (!flags) {
@ -956,8 +980,9 @@ print_usage:
return true; return true;
} }
static bool unwatch(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command) static bool unwatch(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
{ {
NO_MODIFIERS
if (strlen(lstrip(arguments)) == 0) { if (strlen(lstrip(arguments)) == 0) {
for (unsigned i = gb->n_watchpoints; i--;) { for (unsigned i = gb->n_watchpoints; i--;) {
if (gb->watchpoints[i].condition) { if (gb->watchpoints[i].condition) {
@ -994,8 +1019,9 @@ static bool unwatch(GB_gameboy_t *gb, char *arguments, const debugger_command_t
return true; return true;
} }
static bool list(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command) static bool list(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
{ {
NO_MODIFIERS
if (strlen(lstrip(arguments))) { if (strlen(lstrip(arguments))) {
print_usage(gb, command); print_usage(gb, command);
return true; return true;
@ -1077,22 +1103,61 @@ static bool should_break(GB_gameboy_t *gb, uint16_t addr)
return _should_break(gb, full_addr); return _should_break(gb, full_addr);
} }
static bool print(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command) static bool print(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
{ {
if (strlen(lstrip(arguments)) == 0) { if (strlen(lstrip(arguments)) == 0) {
print_usage(gb, command); print_usage(gb, command);
return true; return true;
} }
if (!modifiers || !modifiers[0]) {
modifiers = "a";
}
else if (modifiers[1]) {
print_usage(gb, command);
return true;
}
bool error; bool error;
value_t result = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL); value_t result = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL);
if (!error) { if (!error) {
GB_log(gb, "=%s\n", debugger_value_to_string(gb, result, false)); switch (modifiers[0]) {
case 'a':
GB_log(gb, "=%s\n", debugger_value_to_string(gb, result, false));
break;
case 'd':
GB_log(gb, "=%d\n", result.value);
break;
case 'x':
GB_log(gb, "=$%x\n", result.value);
break;
case 'o':
GB_log(gb, "=0%o\n", result.value);
break;
case 'b':
{
if (!result.value) {
GB_log(gb, "=%%0\n");
break;
}
char binary[17];
binary[16] = 0;
char *ptr = &binary[16];
while (result.value) {
*(--ptr) = (result.value & 1)? '1' : '0';
result.value >>= 1;
}
GB_log(gb, "=%%%s\n", ptr);
break;
}
default:
break;
}
} }
return true; return true;
} }
static bool examine(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command) static bool examine(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
{ {
if (strlen(lstrip(arguments)) == 0) { if (strlen(lstrip(arguments)) == 0) {
print_usage(gb, command); print_usage(gb, command);
@ -1101,33 +1166,54 @@ static bool examine(GB_gameboy_t *gb, char *arguments, const debugger_command_t
bool error; bool error;
value_t addr = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL); value_t addr = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL);
uint16_t count = 32;
if (modifiers) {
char *end;
count = (uint16_t) (strtol(modifiers, &end, 10));
if (*end) {
print_usage(gb, command);
return true;
}
}
if (!error) { if (!error) {
if (addr.has_bank) { if (addr.has_bank) {
banking_state_t old_state; banking_state_t old_state;
save_banking_state(gb, &old_state); save_banking_state(gb, &old_state);
switch_banking_state(gb, addr.bank); switch_banking_state(gb, addr.bank);
GB_log(gb, "%02x:%04x: ", addr.bank, addr.value); while (count) {
for (int i = 0; i < 16; i++) { GB_log(gb, "%02x:%04x: ", addr.bank, addr.value);
GB_log(gb, "%02x ", GB_read_memory(gb, addr.value + i)); for (int i = 0; i < 16 && count; i++) {
GB_log(gb, "%02x ", GB_read_memory(gb, addr.value + i));
count--;
}
addr.value += 16;
GB_log(gb, "\n");
} }
GB_log(gb, "\n");
restore_banking_state(gb, &old_state); restore_banking_state(gb, &old_state);
} }
else { else {
GB_log(gb, "%04x: ", addr.value); while (count) {
for (int i = 0; i < 16; i++) { GB_log(gb, "%04x: ", addr.value);
GB_log(gb, "%02x ", GB_read_memory(gb, addr.value + i)); for (int i = 0; i < 16 && count; i++) {
GB_log(gb, "%02x ", GB_read_memory(gb, addr.value + i));
count--;
}
addr.value += 16;
GB_log(gb, "\n");
} }
GB_log(gb, "\n");
} }
} }
return true; return true;
} }
static bool mbc(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command) static bool mbc(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
{ {
NO_MODIFIERS
if (strlen(lstrip(arguments))) { if (strlen(lstrip(arguments))) {
print_usage(gb, command); print_usage(gb, command);
return true; return true;
@ -1181,8 +1267,10 @@ static bool mbc(GB_gameboy_t *gb, char *arguments, const debugger_command_t *com
return true; return true;
} }
static bool backtrace(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command) static bool backtrace(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
{ {
NO_MODIFIERS
if (strlen(lstrip(arguments))) { if (strlen(lstrip(arguments))) {
print_usage(gb, command); print_usage(gb, command);
return true; return true;
@ -1196,8 +1284,9 @@ static bool backtrace(GB_gameboy_t *gb, char *arguments, const debugger_command_
return true; return true;
} }
static bool ticks(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command) static bool ticks(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
{ {
NO_MODIFIERS
STOPPED_ONLY STOPPED_ONLY
if (strlen(lstrip(arguments))) { if (strlen(lstrip(arguments))) {
@ -1212,8 +1301,9 @@ static bool ticks(GB_gameboy_t *gb, char *arguments, const debugger_command_t *c
} }
static bool palettes(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command) static bool palettes(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
{ {
NO_MODIFIERS
if (strlen(lstrip(arguments))) { if (strlen(lstrip(arguments))) {
print_usage(gb, command); print_usage(gb, command);
return true; return true;
@ -1242,7 +1332,7 @@ static bool palettes(GB_gameboy_t *gb, char *arguments, const debugger_command_t
return true; return true;
} }
static bool help(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command); static bool help(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command);
#define HELP_NEWLINE "\n " #define HELP_NEWLINE "\n "
@ -1254,24 +1344,28 @@ static const debugger_command_t commands[] = {
{"finish", 1, finish, "Run until the current function returns"}, {"finish", 1, finish, "Run until the current function returns"},
{"backtrace", 2, backtrace, "Display the current call stack"}, {"backtrace", 2, backtrace, "Display the current call stack"},
{"bt", 2, }, /* Alias */ {"bt", 2, }, /* Alias */
{"sld", 3, stack_leak_detection, "Like finish, but stops if a stack leak is detected. (Experimental)"}, {"sld", 3, stack_leak_detection, "Like finish, but stops if a stack leak is detected (Experimental)"},
{"ticks", 2, ticks, "Display the number of CPU ticks since the last time 'ticks' was used. "}, {"ticks", 2, ticks, "Display the number of CPU ticks since the last time 'ticks' was used"},
{"registers", 1, registers, "Print values of processor registers and other important registers"}, {"registers", 1, registers, "Print values of processor registers and other important registers"},
{"cartridge", 2, mbc, "Displays information about the MBC and cartridge"}, {"cartridge", 2, mbc, "Displays information about the MBC and cartridge"},
{"mbc", 3, }, /* Alias */ {"mbc", 3, }, /* Alias */
{"palettes", 3, palettes, "Displays the current CGB palettes"}, {"palettes", 3, palettes, "Displays the current CGB palettes"},
{"breakpoint", 1, breakpoint, "Add a new breakpoint at the specified address/expression." HELP_NEWLINE {"breakpoint", 1, breakpoint, "Add a new breakpoint at the specified address/expression" HELP_NEWLINE
"Can also modify the condition of existing breakpoints.", "Can also modify the condition of existing breakpoints.",
"<expression>[ if <condition expression>]"}, "<expression>[ if <condition expression>]"},
{"delete", 2, delete, "Delete a breakpoint by its address, or all breakpoints", "[<expression>]"}, {"delete", 2, delete, "Delete a breakpoint by its address, or all breakpoints", "[<expression>]"},
{"watch", 1, watch, "Add a new watchpoint at the specified address/expression." HELP_NEWLINE {"watch", 1, watch, "Add a new watchpoint at the specified address/expression." HELP_NEWLINE
"Can also modify the condition and type of existing watchpoints.", "Can also modify the condition and type of existing watchpoints." HELP_NEWLINE
" (r|w|rw) <expression>[ if <condition expression>]"}, "Default watchpoint type is write-only.",
"<expression>[ if <condition expression>]", "(r|w|rw)"},
{"unwatch", 3, unwatch, "Delete a watchpoint by its address, or all watchpoints", "[<expression>]"}, {"unwatch", 3, unwatch, "Delete a watchpoint by its address, or all watchpoints", "[<expression>]"},
{"list", 1, list, "List all set breakpoints and watchpoints"}, {"list", 1, list, "List all set breakpoints and watchpoints"},
{"print", 1, print, "Evaluate and print an expression", "<expression>"}, {"print", 1, print, "Evaluate and print an expression" HELP_NEWLINE
"Use modifier to format as an address (a, default) or as a number in" HELP_NEWLINE
"decimal (d), hexadecimal (x), octal (o) or binary (b).",
"<expression>", "format"},
{"eval", 2, }, /* Alias */ {"eval", 2, }, /* Alias */
{"examine", 2, examine, "Examine values at address", "<expression>"}, {"examine", 2, examine, "Examine values at address", "<expression>", "count"},
{"x", 1, }, /* Alias */ {"x", 1, }, /* Alias */
{"help", 1, help, "List available commands or show help for the specified command", "[<command>]"}, {"help", 1, help, "List available commands or show help for the specified command", "[<command>]"},
@ -1308,7 +1402,7 @@ static void print_command_description(GB_gameboy_t *gb, const debugger_command_t
GB_log(gb, (const char *)&" %s\n" + strlen(command->command), command->help_string); GB_log(gb, (const char *)&" %s\n" + strlen(command->command), command->help_string);
} }
static bool help(GB_gameboy_t *gb, char *arguments, const debugger_command_t *ignored) static bool help(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *ignored)
{ {
const debugger_command_t *command = find_command(arguments); const debugger_command_t *command = find_command(arguments);
if (command) { if (command) {
@ -1512,9 +1606,16 @@ bool GB_debugger_do_command(GB_gameboy_t *gb, char *input)
arguments = ""; arguments = "";
} }
char *modifiers = strchr(command_string, '/');
if (modifiers) {
/* Actually "split" the string. */
modifiers[0] = 0;
modifiers++;
}
const debugger_command_t *command = find_command(command_string); const debugger_command_t *command = find_command(command_string);
if (command) { if (command) {
return command->implementation(gb, arguments, command); return command->implementation(gb, arguments, modifiers, command);
} }
else { else {
GB_log(gb, "%s: no such command.\n", command_string); GB_log(gb, "%s: no such command.\n", command_string);