Improvements to the help command and general debugger usability.
This commit is contained in:
parent
e79ddee705
commit
f9236d12bf
@ -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"/>
|
||||||
|
204
Core/debugger.c
204
Core/debugger.c
@ -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,21 +800,17 @@ 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? ");
|
for (unsigned i = gb->n_breakpoints; i--;) {
|
||||||
char *answer = gb->input_callback(gb);
|
if (gb->breakpoints[i].condition) {
|
||||||
if (answer[0] == 'Y' || answer[0] == 'y') {
|
free(gb->breakpoints[i].condition);
|
||||||
for (unsigned i = gb->n_breakpoints; i--;) {
|
|
||||||
if (gb->breakpoints[i].condition) {
|
|
||||||
free(gb->breakpoints[i].condition);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
free(gb->breakpoints);
|
|
||||||
gb->breakpoints = NULL;
|
|
||||||
gb->n_breakpoints = 0;
|
|
||||||
}
|
}
|
||||||
|
free(gb->breakpoints);
|
||||||
|
gb->breakpoints = NULL;
|
||||||
|
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,21 +952,17 @@ 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? ");
|
for (unsigned i = gb->n_watchpoints; i--;) {
|
||||||
char *answer = gb->input_callback(gb);
|
if (gb->watchpoints[i].condition) {
|
||||||
if (answer[0] == 'Y' || answer[0] == 'y') {
|
free(gb->watchpoints[i].condition);
|
||||||
for (unsigned i = gb->n_watchpoints; i--;) {
|
|
||||||
if (gb->watchpoints[i].condition) {
|
|
||||||
free(gb->watchpoints[i].condition);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
free(gb->watchpoints);
|
|
||||||
gb->watchpoints = NULL;
|
|
||||||
gb->n_watchpoints = 0;
|
|
||||||
}
|
}
|
||||||
|
free(gb->watchpoints);
|
||||||
|
gb->watchpoints = NULL;
|
||||||
|
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);
|
||||||
|
Loading…
Reference in New Issue
Block a user