Added debugger "undo" command. Closes #156
This commit is contained in:
parent
bdd27ce50d
commit
027cecde24
@ -1889,6 +1889,29 @@ static bool wave(GB_gameboy_t *gb, char *arguments, char *modifiers, const debug
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool undo(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
|
||||||
|
{
|
||||||
|
NO_MODIFIERS
|
||||||
|
if (strlen(lstrip(arguments))) {
|
||||||
|
print_usage(gb, command);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gb->undo_label) {
|
||||||
|
GB_log(gb, "No undo state available\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
uint16_t pc = gb->pc;
|
||||||
|
GB_load_state_from_buffer(gb, gb->undo_state, GB_get_save_state_size(gb));
|
||||||
|
GB_log(gb, "Reverted a \"%s\" command.\n", gb->undo_label);
|
||||||
|
if (pc != gb->pc) {
|
||||||
|
GB_cpu_disassemble(gb, gb->pc, 5);
|
||||||
|
}
|
||||||
|
gb->undo_label = NULL;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool help(GB_gameboy_t *gb, char *arguments, char *modifiers, 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 "
|
||||||
@ -1899,6 +1922,7 @@ static const debugger_command_t commands[] = {
|
|||||||
{"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"},
|
||||||
|
{"undo", 1, undo, "Reverts the last command"},
|
||||||
{"backtrace", 2, backtrace, "Displays the current call stack"},
|
{"backtrace", 2, backtrace, "Displays 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"},
|
{"sld", 3, stack_leak_detection, "Like finish, but stops if a stack leak is detected"},
|
||||||
@ -2184,7 +2208,30 @@ bool GB_debugger_execute_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, modifiers, command);
|
uint8_t *old_state = malloc(GB_get_save_state_size(gb));
|
||||||
|
GB_save_state_to_buffer(gb, old_state);
|
||||||
|
bool ret = command->implementation(gb, arguments, modifiers, command);
|
||||||
|
if (!ret) { // Command continues, save state in any case
|
||||||
|
free(gb->undo_state);
|
||||||
|
gb->undo_state = old_state;
|
||||||
|
gb->undo_label = command->command;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
uint8_t *new_state = malloc(GB_get_save_state_size(gb));
|
||||||
|
GB_save_state_to_buffer(gb, new_state);
|
||||||
|
if (memcmp(new_state, old_state, GB_get_save_state_size(gb)) != 0) {
|
||||||
|
// State changed, save the old state as the new undo state
|
||||||
|
free(gb->undo_state);
|
||||||
|
gb->undo_state = old_state;
|
||||||
|
gb->undo_label = command->command;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Nothing changed, just free the old state
|
||||||
|
free(old_state);
|
||||||
|
}
|
||||||
|
free(new_state);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
GB_log(gb, "%s: no such command.\n", command_string);
|
GB_log(gb, "%s: no such command.\n", command_string);
|
||||||
@ -2261,6 +2308,11 @@ void GB_debugger_run(GB_gameboy_t *gb)
|
|||||||
{
|
{
|
||||||
if (gb->debug_disable) return;
|
if (gb->debug_disable) return;
|
||||||
|
|
||||||
|
if (!gb->undo_state) {
|
||||||
|
gb->undo_state = malloc(GB_get_save_state_size(gb));
|
||||||
|
GB_save_state_to_buffer(gb, gb->undo_state);
|
||||||
|
}
|
||||||
|
|
||||||
char *input = NULL;
|
char *input = NULL;
|
||||||
if (gb->debug_next_command && gb->debug_call_depth <= 0 && !gb->halted) {
|
if (gb->debug_next_command && gb->debug_call_depth <= 0 && !gb->halted) {
|
||||||
gb->debug_stopped = true;
|
gb->debug_stopped = true;
|
||||||
|
@ -202,6 +202,9 @@ void GB_free(GB_gameboy_t *gb)
|
|||||||
if (gb->nontrivial_jump_state) {
|
if (gb->nontrivial_jump_state) {
|
||||||
free(gb->nontrivial_jump_state);
|
free(gb->nontrivial_jump_state);
|
||||||
}
|
}
|
||||||
|
if (gb->undo_state) {
|
||||||
|
free(gb->undo_state);
|
||||||
|
}
|
||||||
#ifndef GB_DISABLE_DEBUGGER
|
#ifndef GB_DISABLE_DEBUGGER
|
||||||
GB_debugger_clear_symbols(gb);
|
GB_debugger_clear_symbols(gb);
|
||||||
#endif
|
#endif
|
||||||
@ -1434,6 +1437,10 @@ void GB_switch_model_and_reset(GB_gameboy_t *gb, GB_model_t model)
|
|||||||
gb->ram = realloc(gb->ram, gb->ram_size = 0x2000);
|
gb->ram = realloc(gb->ram, gb->ram_size = 0x2000);
|
||||||
gb->vram = realloc(gb->vram, gb->vram_size = 0x2000);
|
gb->vram = realloc(gb->vram, gb->vram_size = 0x2000);
|
||||||
}
|
}
|
||||||
|
if (gb->undo_state) {
|
||||||
|
free(gb->undo_state);
|
||||||
|
gb->undo_state = NULL;
|
||||||
|
}
|
||||||
GB_rewind_free(gb);
|
GB_rewind_free(gb);
|
||||||
GB_reset(gb);
|
GB_reset(gb);
|
||||||
load_default_border(gb);
|
load_default_border(gb);
|
||||||
|
@ -646,6 +646,10 @@ struct GB_gameboy_internal_s {
|
|||||||
/* Ticks command */
|
/* Ticks command */
|
||||||
uint64_t debugger_ticks;
|
uint64_t debugger_ticks;
|
||||||
|
|
||||||
|
/* Undo */
|
||||||
|
uint8_t *undo_state;
|
||||||
|
const char *undo_label;
|
||||||
|
|
||||||
/* Rewind */
|
/* Rewind */
|
||||||
#define GB_REWIND_FRAMES_PER_KEY 255
|
#define GB_REWIND_FRAMES_PER_KEY 255
|
||||||
size_t rewind_buffer_length;
|
size_t rewind_buffer_length;
|
||||||
|
Loading…
Reference in New Issue
Block a user