git.y1.nz

SameBoy

Accurate GB/GBC emulator
download: https://git.y1.nz/archives/sameboy.tar.gz
README | Files | Log | Refs | LICENSE

commit a9ce9f538f2241ec89338f4b8b19b2d39e1a334b
parent 0b54eea084ac894fb777e63dc0fa82e3b9d18ff4
Author: Lior Halphon <LIJI32@gmail.com>
Date:   Fri,  4 Aug 2023 19:54:14 +0300

Experimental backstepping support

Diffstat:
MCore/debugger.c72+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
MCore/gb.c7+------
MCore/gb.h10++++++++++
MCore/rewind.c15+++++++++++++++
MCore/rewind.h2+-
MCore/save_state.c2+-
MCore/timing.c9+++++++++
7 files changed, 108 insertions(+), 9 deletions(-)

diff --git a/Core/debugger.c b/Core/debugger.c @@ -1399,6 +1399,7 @@ static bool list(GB_gameboy_t *gb, char *arguments, char *modifiers, const debug // Returns the id or 0 static unsigned should_break(GB_gameboy_t *gb, uint16_t addr, bool jump_to) { + if (unlikely(gb->backstep_instructions)) return false; uint16_t bank = bank_for_addr(gb, addr); for (unsigned i = 0; i < gb->n_breakpoints; i++) { struct GB_breakpoint_s *breakpoint = &gb->breakpoints[i]; @@ -2064,6 +2065,59 @@ static bool undo(GB_gameboy_t *gb, char *arguments, char *modifiers, const debug return true; } +#ifndef DISABLE_REWIND +static bool backstep(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) +{ + NO_MODIFIERS + STOPPED_ONLY + + if (strlen(lstrip(arguments))) { + print_usage(gb, command); + return true; + } + + bool didPop = false; +retry:; + typeof(gb->rewind_sequences[0]) *sequence = &gb->rewind_sequences[gb->rewind_pos]; + if (!gb->rewind_sequences || !sequence->key_state) { + if (gb->rewind_buffer_length == 0) { + GB_log(gb, "Backstepping requires enabling rewinding\n"); + } + else { + GB_log(gb, "Reached the end of the rewind buffer\n"); + if (didPop) { + GB_rewind_push(gb); + sequence = &gb->rewind_sequences[gb->rewind_pos]; + sequence->instruction_count[sequence->pos] = 1; + } + } + return true; + } + + gb->backstep_instructions = sequence->instruction_count[sequence->pos] - 2; + if (gb->backstep_instructions == (uint32_t)-1) { // This frame was just pushed, pop it and try again + GB_rewind_pop(gb); + gb->backstep_instructions = 0; + didPop = true; + goto retry; + } + else if (gb->backstep_instructions > 0x20000) { + GB_log(gb, "Backstepping is currently not available\n"); + gb->backstep_instructions = 0; + return true; + } + GB_rewind_pop(gb); + GB_rewind_push(gb); + sequence = &gb->rewind_sequences[gb->rewind_pos]; + sequence->instruction_count[sequence->pos] = 1; + while (gb->backstep_instructions) { + GB_run(gb); + } + GB_cpu_disassemble(gb, gb->pc, 5); + return true; +} +#endif + static bool help(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command); @@ -2078,6 +2132,10 @@ static const debugger_command_t commands[] = { {"next", 1, next, "Run the next instruction, skipping over function calls"}, {"step", 1, step, "Run the next instruction, stepping into function calls"}, {"finish", 1, finish, "Run until the current function returns"}, +#ifndef DISABLE_REWIND + {"backstep", 5, backstep, "Step one instruction backwards, assuming constant inputs"}, + {"bs", 2, }, /* Alias */ +#endif {"undo", 1, undo, "Revert the last command"}, {"registers", 1, registers, "Print values of processor registers and other important registers"}, {"backtrace", 2, backtrace, "Display the current call stack"}, @@ -2205,7 +2263,7 @@ void GB_debugger_call_hook(GB_gameboy_t *gb, uint16_t call_addr) if (gb->backtrace_size < sizeof(gb->backtrace_sps) / sizeof(gb->backtrace_sps[0])) { while (gb->backtrace_size) { - if (gb->backtrace_sps[gb->backtrace_size - 1] < gb->sp) { + if (gb->backtrace_sps[gb->backtrace_size - 1] <= gb->sp) { gb->backtrace_size--; gb->debug_call_depth--; } @@ -2241,6 +2299,7 @@ void GB_debugger_ret_hook(GB_gameboy_t *gb) // Returns the id or 0 static void test_watchpoint(GB_gameboy_t *gb, uint16_t addr, uint8_t flags, uint8_t value) { + if (unlikely(gb->backstep_instructions)) return; uint16_t bank = bank_for_addr(gb, addr); for (unsigned i = 0; i < gb->n_watchpoints; i++) { struct GB_watchpoint_s *watchpoint = &gb->watchpoints[i]; @@ -2470,6 +2529,7 @@ next_command: gb->non_trivial_jump_breakpoint_occured = true; GB_log(gb, "Jumping to breakpoint %u: %s\n", breakpoint_id, value_to_string(gb, gb->pc, true, false)); GB_load_state_from_buffer(gb, gb->nontrivial_jump_state, -1); + GB_rewind_push(gb); GB_cpu_disassemble(gb, gb->pc, 5); GB_debugger_break(gb); } @@ -2516,6 +2576,16 @@ next_command: } void GB_debugger_run(GB_gameboy_t *gb) { +#ifndef DISABLE_REWIND + if (gb->rewind_sequences && gb->rewind_sequences[gb->rewind_pos].key_state) { + typeof(gb->rewind_sequences[0]) *sequence = &gb->rewind_sequences[gb->rewind_pos]; + sequence->instruction_count[sequence->pos]++; + } + if (unlikely(gb->backstep_instructions)) { + gb->backstep_instructions--; + return; + } +#endif if (likely(!gb->debug_active)) return; debugger_run(gb); } diff --git a/Core/gb.c b/Core/gb.c @@ -13,12 +13,6 @@ #include "gb.h" -#ifdef GB_DISABLE_REWIND -#define GB_rewind_reset(...) -#define GB_rewind_push(...) -#endif - - void GB_attributed_logv(GB_gameboy_t *gb, GB_log_attributes attributes, const char *fmt, va_list args) { char *string = NULL; @@ -1795,6 +1789,7 @@ static void GB_reset_internal(GB_gameboy_t *gb, bool quick) gb->magic = GB_state_magic(); request_boot_rom(gb); + GB_rewind_push(gb); } void GB_reset(GB_gameboy_t *gb) diff --git a/Core/gb.h b/Core/gb.h @@ -249,6 +249,13 @@ typedef enum { #define SGB_NTSC_FREQUENCY (21477272 / 5) #define SGB_PAL_FREQUENCY (21281370 / 5) #define DIV_CYCLES (0x100) + +#ifdef GB_DISABLE_REWIND +#define GB_rewind_reset(...) +#define GB_rewind_push(...) +#define GB_rewind_invalidate_for_backstepping(...) +#endif + #endif typedef void (*GB_vblank_callback_t)(GB_gameboy_t *gb, GB_vblank_type_t type); @@ -729,6 +736,7 @@ struct GB_gameboy_internal_s { bool debug_fin_command, debug_next_command; bool debug_active; // Cached value determining if GB_debugger_run does anything bool help_shown; + uint32_t backstep_instructions; /* Breakpoints */ uint16_t n_breakpoints; @@ -774,9 +782,11 @@ struct GB_gameboy_internal_s { struct { uint8_t *key_state; uint8_t *compressed_states[GB_REWIND_FRAMES_PER_KEY]; + uint32_t instruction_count[GB_REWIND_FRAMES_PER_KEY + 1]; unsigned pos; } *rewind_sequences; // lasts about 4 seconds size_t rewind_pos; + bool rewind_disable_invalidation; #endif /* SGB - saved and allocated optionally */ diff --git a/Core/rewind.c b/Core/rewind.c @@ -145,6 +145,7 @@ void GB_rewind_push(GB_gameboy_t *gb) if (!gb->rewind_sequences[gb->rewind_pos].key_state) { gb->rewind_sequences[gb->rewind_pos].key_state = malloc(save_size); + gb->rewind_sequences[gb->rewind_pos].instruction_count[0] = 0; GB_save_state_to_buffer_no_bess(gb, gb->rewind_sequences[gb->rewind_pos].key_state); } else { @@ -153,6 +154,7 @@ void GB_rewind_push(GB_gameboy_t *gb) GB_save_state_to_buffer_no_bess(gb, save_state); gb->rewind_sequences[gb->rewind_pos].compressed_states[gb->rewind_sequences[gb->rewind_pos].pos++] = state_compress(gb->rewind_sequences[gb->rewind_pos].key_state, save_state, save_size); + gb->rewind_sequences[gb->rewind_pos].instruction_count[gb->rewind_sequences[gb->rewind_pos].pos] = 0; free(save_state); } @@ -168,7 +170,9 @@ bool GB_rewind_pop(GB_gameboy_t *gb) const size_t save_size = GB_get_save_state_size_no_bess(gb); if (gb->rewind_sequences[gb->rewind_pos].pos == 0) { + gb->rewind_disable_invalidation = true; GB_load_state_from_buffer(gb, gb->rewind_sequences[gb->rewind_pos].key_state, save_size); + gb->rewind_disable_invalidation = false; free(gb->rewind_sequences[gb->rewind_pos].key_state); gb->rewind_sequences[gb->rewind_pos].key_state = NULL; gb->rewind_pos = gb->rewind_pos == 0? gb->rewind_buffer_length - 1 : gb->rewind_pos - 1; @@ -182,7 +186,9 @@ bool GB_rewind_pop(GB_gameboy_t *gb) save_size); free(gb->rewind_sequences[gb->rewind_pos].compressed_states[gb->rewind_sequences[gb->rewind_pos].pos]); gb->rewind_sequences[gb->rewind_pos].compressed_states[gb->rewind_sequences[gb->rewind_pos].pos] = NULL; + gb->rewind_disable_invalidation = true; GB_load_state_from_buffer(gb, save_state, save_size); + gb->rewind_disable_invalidation = false; free(save_state); return true; } @@ -216,3 +222,12 @@ void GB_set_rewind_length(GB_gameboy_t *gb, double seconds) gb->rewind_buffer_length = (size_t) ceil(seconds * CPU_FREQUENCY / LCDC_PERIOD / GB_REWIND_FRAMES_PER_KEY); } } + +void GB_rewind_invalidate_for_backstepping(GB_gameboy_t *gb) +{ + if (gb->rewind_disable_invalidation) return;; + if (gb->rewind_sequences && gb->rewind_sequences[gb->rewind_pos].key_state) { + typeof(gb->rewind_sequences[0]) *sequence = &gb->rewind_sequences[gb->rewind_pos]; + sequence->instruction_count[sequence->pos] |= 0x80000000; + } +} diff --git a/Core/rewind.h b/Core/rewind.h @@ -6,9 +6,9 @@ #ifdef GB_INTERNAL internal void GB_rewind_push(GB_gameboy_t *gb); +internal void GB_rewind_invalidate_for_backstepping(GB_gameboy_t *gb); #endif bool GB_rewind_pop(GB_gameboy_t *gb); void GB_set_rewind_length(GB_gameboy_t *gb, double seconds); void GB_rewind_reset(GB_gameboy_t *gb); - #endif diff --git a/Core/save_state.c b/Core/save_state.c @@ -1352,7 +1352,7 @@ static int load_state_internal(GB_gameboy_t *gb, virtual_file_t *file) gb->ram_size = orig_ram_size; sanitize_state(gb); - + GB_rewind_invalidate_for_backstepping(gb); return 0; } diff --git a/Core/timing.c b/Core/timing.c @@ -42,6 +42,9 @@ static void nsleep(uint64_t nanoseconds) bool GB_timing_sync_turbo(GB_gameboy_t *gb) { +#ifndef GB_DISABLE_DEBUGGER + if (unlikely(gb->backstep_instructions)) return false; +#endif if (!gb->turbo_dont_skip) { int64_t nanoseconds = get_nanoseconds(); if (nanoseconds <= gb->last_sync + (1000000000LL * LCDC_PERIOD / GB_get_clock_rate(gb))) { @@ -54,6 +57,9 @@ bool GB_timing_sync_turbo(GB_gameboy_t *gb) void GB_timing_sync(GB_gameboy_t *gb) { +#ifndef GB_DISABLE_DEBUGGER + if (unlikely(gb->backstep_instructions)) return; +#endif /* Prevent syncing if not enough time has passed.*/ if (gb->cycles_since_last_sync < LCDC_PERIOD / 3) return; @@ -95,6 +101,9 @@ bool GB_timing_sync_turbo(GB_gameboy_t *gb) void GB_timing_sync(GB_gameboy_t *gb) { +#ifndef GB_DISABLE_DEBUGGER + if (unlikely(gb->backstep_instructions)) return; +#endif if (gb->cycles_since_last_sync < LCDC_PERIOD / 3) return; gb->cycles_since_last_sync = 0;

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.