Improvements to the help command and general debugger usability.

This commit is contained in:
Lior Halphon 2016-08-13 22:52:41 +03:00
parent e79ddee705
commit f9236d12bf
2 changed files with 137 additions and 93 deletions

View File

@ -49,30 +49,30 @@
<window title="Debug Console" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="21F-Ah-yHX" customClass="NSPanel"> <window title="Debug Console" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="21F-Ah-yHX" customClass="NSPanel">
<windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES" utility="YES" HUD="YES"/> <windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES" utility="YES" HUD="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/> <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="272" y="172" width="320" height="240"/> <rect key="contentRect" x="272" y="172" width="600" height="400"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/> <rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
<value key="minSize" type="size" width="320" height="240"/> <value key="minSize" type="size" width="600" height="400"/>
<view key="contentView" id="dCP-E5-7Fi"> <view key="contentView" id="dCP-E5-7Fi">
<rect key="frame" x="0.0" y="0.0" width="320" height="240"/> <rect key="frame" x="0.0" y="0.0" width="600" height="400"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<scrollView borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" id="oTo-zx-o6N"> <scrollView borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" id="oTo-zx-o6N">
<rect key="frame" x="0.0" y="25" width="320" height="216"/> <rect key="frame" x="0.0" y="25" width="600" height="376"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="EQe-Ad-L7S"> <clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="EQe-Ad-L7S">
<rect key="frame" x="0.0" y="0.0" width="320" height="216"/> <rect key="frame" x="0.0" y="0.0" width="600" height="376"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<textView editable="NO" drawsBackground="NO" importsGraphics="NO" richText="NO" baseWritingDirection="leftToRight" findStyle="bar" verticallyResizable="YES" allowsNonContiguousLayout="YES" id="doS-dM-hnl"> <textView editable="NO" drawsBackground="NO" importsGraphics="NO" richText="NO" baseWritingDirection="leftToRight" findStyle="bar" verticallyResizable="YES" allowsNonContiguousLayout="YES" id="doS-dM-hnl">
<rect key="frame" x="0.0" y="0.0" width="320" height="216"/> <rect key="frame" x="0.0" y="0.0" width="600" height="376"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/> <color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<color key="backgroundColor" red="0.14901960784313725" green="0.14901960784313725" blue="0.14901960784313725" alpha="1" colorSpace="calibratedRGB"/> <color key="backgroundColor" red="0.14901960784313725" green="0.14901960784313725" blue="0.14901960784313725" alpha="1" colorSpace="calibratedRGB"/>
<size key="minSize" width="320" height="216"/> <size key="minSize" width="600" height="376"/>
<size key="maxSize" width="463" height="10000000"/> <size key="maxSize" width="600" height="10000000"/>
<color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> <color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<size key="minSize" width="320" height="216"/> <size key="minSize" width="600" height="376"/>
<size key="maxSize" width="463" height="10000000"/> <size key="maxSize" width="600" height="10000000"/>
<allowedInputSourceLocales> <allowedInputSourceLocales>
<string>NSAllRomanInputSourcesLocaleIdentifier</string> <string>NSAllRomanInputSourcesLocaleIdentifier</string>
</allowedInputSourceLocales> </allowedInputSourceLocales>
@ -85,12 +85,12 @@
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
</scroller> </scroller>
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="cwi-6E-rbh"> <scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="cwi-6E-rbh">
<rect key="frame" x="304" y="0.0" width="16" height="216"/> <rect key="frame" x="584" y="0.0" width="16" height="376"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
</scroller> </scroller>
</scrollView> </scrollView>
<textField focusRingType="none" verticalHuggingPriority="750" mirrorLayoutDirectionWhenInternationalizing="never" id="l22-S8-uji"> <textField focusRingType="none" verticalHuggingPriority="750" mirrorLayoutDirectionWhenInternationalizing="never" id="l22-S8-uji">
<rect key="frame" x="0.0" y="0.0" width="320" height="24"/> <rect key="frame" x="0.0" y="0.0" width="600" height="24"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" focusRingType="none" id="p3j-nS-44f"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" focusRingType="none" id="p3j-nS-44f">
<font key="font" metaFont="fixedUser" size="11"/> <font key="font" metaFont="fixedUser" size="11"/>
@ -105,7 +105,7 @@
</connections> </connections>
</textField> </textField>
<box verticalHuggingPriority="750" title="Box" boxType="separator" titlePosition="noTitle" id="960-dL-7ZY"> <box verticalHuggingPriority="750" title="Box" boxType="separator" titlePosition="noTitle" id="960-dL-7ZY">
<rect key="frame" x="0.0" y="23" width="320" height="5"/> <rect key="frame" x="0.0" y="23" width="600" height="5"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<color key="borderColor" white="0.0" alpha="0.41999999999999998" colorSpace="calibratedWhite"/> <color key="borderColor" white="0.0" alpha="0.41999999999999998" colorSpace="calibratedWhite"/>
<color key="fillColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/> <color key="fillColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>

View File

@ -588,13 +588,15 @@ value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
return VALUE_16(literal); return VALUE_16(literal);
} }
typedef bool debugger_command_imp_t(GB_gameboy_t *gb, char *arguments); struct debugger_command_s;
typedef bool debugger_command_imp_t(GB_gameboy_t *gb, char *arguments, const struct debugger_command_s *command);
typedef struct { typedef struct debugger_command_s {
const char *command; const char *command;
uint8_t min_length; uint8_t min_length;
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
} debugger_command_t; } debugger_command_t;
static const char *lstrip(const char *str) static const char *lstrip(const char *str)
@ -611,12 +613,22 @@ GB_log(gb, "Program is running. \n"); \
return false; \ return false; \
} }
static bool cont(GB_gameboy_t *gb, char *arguments) static void print_usage(GB_gameboy_t *gb, const debugger_command_t *command)
{
if (command->arguments_format) {
GB_log(gb, "Usage: %s %s\n", command->command, command->arguments_format);
}
else {
GB_log(gb, "Usage: %s\n", command->command);
}
}
static bool cont(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command)
{ {
STOPPED_ONLY STOPPED_ONLY
if (strlen(lstrip(arguments))) { if (strlen(lstrip(arguments))) {
GB_log(gb, "Usage: continue\n"); print_usage(gb, command);
return true; return true;
} }
@ -624,12 +636,12 @@ static bool cont(GB_gameboy_t *gb, char *arguments)
return false; return false;
} }
static bool next(GB_gameboy_t *gb, char *arguments) static bool next(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command)
{ {
STOPPED_ONLY STOPPED_ONLY
if (strlen(lstrip(arguments))) { if (strlen(lstrip(arguments))) {
GB_log(gb, "Usage: next\n"); print_usage(gb, command);
return true; return true;
} }
@ -639,24 +651,24 @@ static bool next(GB_gameboy_t *gb, char *arguments)
return false; return false;
} }
static bool step(GB_gameboy_t *gb, char *arguments) static bool step(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command)
{ {
STOPPED_ONLY STOPPED_ONLY
if (strlen(lstrip(arguments))) { if (strlen(lstrip(arguments))) {
GB_log(gb, "Usage: step\n"); print_usage(gb, command);
return true; return true;
} }
return false; return false;
} }
static bool finish(GB_gameboy_t *gb, char *arguments) static bool finish(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command)
{ {
STOPPED_ONLY STOPPED_ONLY
if (strlen(lstrip(arguments))) { if (strlen(lstrip(arguments))) {
GB_log(gb, "Usage: finish\n"); print_usage(gb, command);
return true; return true;
} }
@ -666,12 +678,12 @@ static bool finish(GB_gameboy_t *gb, char *arguments)
return false; return false;
} }
static bool stack_leak_detection(GB_gameboy_t *gb, char *arguments) static bool stack_leak_detection(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command)
{ {
STOPPED_ONLY STOPPED_ONLY
if (strlen(lstrip(arguments))) { if (strlen(lstrip(arguments))) {
GB_log(gb, "Usage: sld\n"); print_usage(gb, command);
return true; return true;
} }
@ -681,10 +693,10 @@ static bool stack_leak_detection(GB_gameboy_t *gb, char *arguments)
return false; return false;
} }
static bool registers(GB_gameboy_t *gb, char *arguments) static bool registers(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command)
{ {
if (strlen(lstrip(arguments))) { if (strlen(lstrip(arguments))) {
GB_log(gb, "Usage: registers\n"); print_usage(gb, command);
return true; return true;
} }
@ -723,10 +735,10 @@ 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) static bool breakpoint(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command)
{ {
if (strlen(lstrip(arguments)) == 0) { if (strlen(lstrip(arguments)) == 0) {
GB_log(gb, "Usage: breakpoint <expression>[ if <condition expression>]\n"); print_usage(gb, command);
return true; return true;
} }
@ -788,12 +800,9 @@ static bool breakpoint(GB_gameboy_t *gb, char *arguments)
return true; return true;
} }
static bool delete(GB_gameboy_t *gb, char *arguments) static bool delete(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command)
{ {
if (strlen(lstrip(arguments)) == 0) { if (strlen(lstrip(arguments)) == 0) {
GB_log(gb, "Delete all breakpoints? ");
char *answer = gb->input_callback(gb);
if (answer[0] == 'Y' || answer[0] == 'y') {
for (unsigned i = gb->n_breakpoints; i--;) { for (unsigned i = gb->n_breakpoints; i--;) {
if (gb->breakpoints[i].condition) { if (gb->breakpoints[i].condition) {
free(gb->breakpoints[i].condition); free(gb->breakpoints[i].condition);
@ -802,7 +811,6 @@ static bool delete(GB_gameboy_t *gb, char *arguments)
free(gb->breakpoints); free(gb->breakpoints);
gb->breakpoints = NULL; gb->breakpoints = NULL;
gb->n_breakpoints = 0; gb->n_breakpoints = 0;
}
return true; return true;
} }
@ -852,11 +860,11 @@ 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) static bool watch(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command)
{ {
if (strlen(lstrip(arguments)) == 0) { if (strlen(lstrip(arguments)) == 0) {
print_usage: print_usage:
GB_log(gb, "Usage: watch (r|w|rw) <expression>[ if <condition expression>]\n"); print_usage(gb, command);
return true; return true;
} }
@ -944,12 +952,9 @@ print_usage:
return true; return true;
} }
static bool unwatch(GB_gameboy_t *gb, char *arguments) static bool unwatch(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command)
{ {
if (strlen(lstrip(arguments)) == 0) { if (strlen(lstrip(arguments)) == 0) {
GB_log(gb, "Delete all watchpoints? ");
char *answer = gb->input_callback(gb);
if (answer[0] == 'Y' || answer[0] == 'y') {
for (unsigned i = gb->n_watchpoints; i--;) { for (unsigned i = gb->n_watchpoints; i--;) {
if (gb->watchpoints[i].condition) { if (gb->watchpoints[i].condition) {
free(gb->watchpoints[i].condition); free(gb->watchpoints[i].condition);
@ -958,7 +963,6 @@ static bool unwatch(GB_gameboy_t *gb, char *arguments)
free(gb->watchpoints); free(gb->watchpoints);
gb->watchpoints = NULL; gb->watchpoints = NULL;
gb->n_watchpoints = 0; gb->n_watchpoints = 0;
}
return true; return true;
} }
@ -986,10 +990,10 @@ static bool unwatch(GB_gameboy_t *gb, char *arguments)
return true; return true;
} }
static bool list(GB_gameboy_t *gb, char *arguments) static bool list(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command)
{ {
if (strlen(lstrip(arguments))) { if (strlen(lstrip(arguments))) {
GB_log(gb, "Usage: list\n"); print_usage(gb, command);
return true; return true;
} }
@ -1069,10 +1073,10 @@ 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) static bool print(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command)
{ {
if (strlen(lstrip(arguments)) == 0) { if (strlen(lstrip(arguments)) == 0) {
GB_log(gb, "Usage: print <expression>\n"); print_usage(gb, command);
return true; return true;
} }
@ -1084,10 +1088,10 @@ static bool print(GB_gameboy_t *gb, char *arguments)
return true; return true;
} }
static bool examine(GB_gameboy_t *gb, char *arguments) static bool examine(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command)
{ {
if (strlen(lstrip(arguments)) == 0) { if (strlen(lstrip(arguments)) == 0) {
GB_log(gb, "Usage: examine <expression>\n"); print_usage(gb, command);
return true; return true;
} }
@ -1118,17 +1122,17 @@ static bool examine(GB_gameboy_t *gb, char *arguments)
return true; return true;
} }
static bool mbc(GB_gameboy_t *gb, char *arguments) static bool mbc(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command)
{ {
if (strlen(lstrip(arguments))) { if (strlen(lstrip(arguments))) {
GB_log(gb, "Usage: mbc\n"); print_usage(gb, command);
return true; return true;
} }
const GB_cartridge_t *cartridge = gb->cartridge_type; const GB_cartridge_t *cartridge = gb->cartridge_type;
if (cartridge->has_ram) { if (cartridge->has_ram) {
GB_log(gb, "Cartrdige includes%s RAM: %x\n", cartridge->has_battery? " battery-backed": "", gb->mbc_ram_size); GB_log(gb, "Cartrdige includes%s RAM: $%x bytes\n", cartridge->has_battery? " battery-backed": "", gb->mbc_ram_size);
} }
else { else {
GB_log(gb, "No cartridge RAM\n"); GB_log(gb, "No cartridge RAM\n");
@ -1166,10 +1170,10 @@ static bool mbc(GB_gameboy_t *gb, char *arguments)
return true; return true;
} }
static bool backtrace(GB_gameboy_t *gb, char *arguments) static bool backtrace(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command)
{ {
if (strlen(lstrip(arguments))) { if (strlen(lstrip(arguments))) {
GB_log(gb, "Usage: backtrace\n"); print_usage(gb, command);
return true; return true;
} }
@ -1181,51 +1185,50 @@ static bool backtrace(GB_gameboy_t *gb, char *arguments)
return true; return true;
} }
static bool help(GB_gameboy_t *gb, char *arguments); static bool help(GB_gameboy_t *gb, char *arguments, const debugger_command_t *command);
#define HELP_NEWLINE "\n "
/* Commands without implementations are aliases of the previous non-alias commands */
static const debugger_command_t commands[] = { static const debugger_command_t commands[] = {
{"continue", 1, cont, "Continue running until next stop"}, {"continue", 1, cont, "Continue running until next stop"},
{"next", 1, next, "Run the next instruction, skipping over function calls"}, {"next", 1, next, "Run the next instruction, skipping over function calls"},
{"step", 1, step, "Run the next instruction, stepping into function calls"}, {"step", 1, step, "Run the next instruction, stepping into function calls"},
{"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, backtrace, NULL}, {"bt", 2, }, /* Alias */
{"sld", 3, stack_leak_detection, "Run until the current function returns, or a stack leak is detected (Experimental)"}, {"sld", 3, stack_leak_detection, "Like finish, but stops if a stack leak is detected. (Experimental)"},
{"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, mbc, NULL}, {"mbc", 3, }, /* Alias */
{"breakpoint", 1, breakpoint, "Add a new breakpoint at the specified address/expression. Can also modify the condition of existing breakpoints."}, {"breakpoint", 1, breakpoint, "Add a new breakpoint at the specified address/expression." HELP_NEWLINE
{"delete", 2, delete, "Delete a breakpoint by its address, or all breakpoints"}, "Can also modify the condition of existing breakpoints.",
{"watch", 1, watch, "Add a new watchpoint at the specified address/expression. Can also modify the condition and type of existing watchpoints."}, "<expression>[ if <condition expression>]"},
{"unwatch", 3, unwatch, "Delete a watchpoint by its address, or all watchpoints"}, {"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
"Can also modify the condition and type of existing watchpoints.",
" (r|w|rw) <expression>[ if <condition 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"}, {"print", 1, print, "Evaluate and print an expression", "<expression>"},
{"eval", 2, print, NULL}, {"eval", 2, }, /* Alias */
{"examine", 2, examine, "Examine values at address"}, {"examine", 2, examine, "Examine values at address", "<expression>"},
{"x", 1, examine, NULL}, {"x", 1, }, /* Alias */
{"help", 1, help, "List available commands"}, {"help", 1, help, "List available commands or show help for the specified command", "[<command>]"},
{NULL,}, /* Null terminator */
}; };
static bool help(GB_gameboy_t *gb, char *arguments)
{
/* Todo: command specific help */
const debugger_command_t *command = commands;
for (size_t i = sizeof(commands) / sizeof(*command); i--; command++) {
if (command->help_string) {
GB_attributed_log(gb, GB_LOG_BOLD, "%s", command->command);
GB_log(gb, ": %s\n", command->help_string);
}
}
return true;
}
static const debugger_command_t *find_command(const char *string) static const debugger_command_t *find_command(const char *string)
{ {
const debugger_command_t *command = commands;
size_t length = strlen(string); size_t length = strlen(string);
for (size_t i = sizeof(commands) / sizeof(*command); i--; command++) { for (const debugger_command_t *command = commands; command->command; command++) {
if (command->min_length > length) continue; if (command->min_length > length) continue;
if (memcmp(command->command, string, length) == 0) { /* Is a substring? */ if (memcmp(command->command, string, length) == 0) { /* Is a substring? */
/* Aliases */
while (!command->implementation) {
command--;
}
return command; return command;
} }
} }
@ -1233,6 +1236,47 @@ static const debugger_command_t *find_command(const char *string)
return NULL; return NULL;
} }
static void print_command_shortcut(GB_gameboy_t *gb, const debugger_command_t *command)
{
GB_attributed_log(gb, GB_LOG_BOLD | GB_LOG_UNDERLINE, "%.*s", command->min_length, command->command);
GB_attributed_log(gb, GB_LOG_BOLD , "%s", command->command + command->min_length);
}
static void print_command_description(GB_gameboy_t *gb, const debugger_command_t *command)
{
print_command_shortcut(gb, command);
GB_log(gb, ": ");
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)
{
const debugger_command_t *command = find_command(arguments);
if (command) {
print_command_description(gb, command);
GB_log(gb, "\n");
print_usage(gb, command);
command++;
if (command->command && !command->implementation) { /* Command has aliases*/
GB_log(gb, "\nAliases: ");
do {
print_command_shortcut(gb, command);
GB_log(gb, " ");
command++;
} while (command->command && !command->implementation);
GB_log(gb, "\n");
}
return true;
}
for (const debugger_command_t *command = commands; command->command; command++) {
if (command->help_string) {
print_command_description(gb, command);
}
}
return true;
}
void GB_debugger_call_hook(GB_gameboy_t *gb, uint16_t call_addr) void GB_debugger_call_hook(GB_gameboy_t *gb, uint16_t call_addr)
{ {
/* Called just after the CPU calls a function/enters an interrupt/etc... */ /* Called just after the CPU calls a function/enters an interrupt/etc... */
@ -1409,7 +1453,7 @@ bool GB_debugger_do_command(GB_gameboy_t *gb, char *input)
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); return command->implementation(gb, arguments, command);
} }
else { else {
GB_log(gb, "%s: no such command.\n", command_string); GB_log(gb, "%s: no such command.\n", command_string);