git.y1.nz

SameBoy

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

Core/rewind.c

      1 #include "gb.h"
      2 #include <stdint.h>
      3 #include <stddef.h>
      4 #include <stdlib.h>
      5 #include <math.h>
      6 #include <assert.h>
      7 #include <string.h>
      8 
      9 static uint8_t *state_compress(const uint8_t *prev, const uint8_t *data, size_t uncompressed_size)
     10 {
     11     size_t malloc_size = 0x1000;
     12     uint8_t *compressed = malloc(malloc_size);
     13     size_t counter_pos = 0;
     14     size_t data_pos = sizeof(uint16_t);
     15     bool prev_mode = true;
     16     *(uint16_t *)compressed = 0;
     17 #define COUNTER (*(uint16_t *)&compressed[counter_pos])
     18 #define DATA (compressed[data_pos])
     19     
     20     while (uncompressed_size) {
     21         if (prev_mode) {
     22             if (*data == *prev && COUNTER != 0xFFFF) {
     23                 COUNTER++;
     24                 data++;
     25                 prev++;
     26                 uncompressed_size--;
     27             }
     28             else {
     29                 prev_mode = false;
     30                 counter_pos += sizeof(uint16_t);
     31                 data_pos = counter_pos + sizeof(uint16_t);
     32                 if (data_pos >= malloc_size) {
     33                     malloc_size *= 2;
     34                     compressed = realloc(compressed, malloc_size);
     35                 }
     36                 COUNTER = 0;
     37             }
     38         }
     39         else {
     40             if (*data != *prev && COUNTER != 0xFFFF) {
     41                 COUNTER++;
     42                 DATA = *data;
     43                 data_pos++;
     44                 data++;
     45                 prev++;
     46                 uncompressed_size--;
     47                 if (data_pos >= malloc_size) {
     48                     malloc_size *= 2;
     49                     compressed = realloc(compressed, malloc_size);
     50                 }
     51             }
     52             else {
     53                 prev_mode = true;
     54                 counter_pos = data_pos;
     55                 data_pos = counter_pos + sizeof(uint16_t);
     56                 if (counter_pos >= malloc_size - 1) {
     57                     malloc_size *= 2;
     58                     compressed = realloc(compressed, malloc_size);
     59                 }
     60                 COUNTER = 0;
     61             }
     62         }
     63     }
     64     
     65     return  realloc(compressed, data_pos);
     66 #undef DATA
     67 #undef COUNTER
     68 }
     69 
     70 
     71 static void state_decompress(const uint8_t *prev, uint8_t *data, uint8_t *dest, size_t uncompressed_size)
     72 {
     73     size_t counter_pos = 0;
     74     size_t data_pos = sizeof(uint16_t);
     75     bool prev_mode = true;
     76 #define COUNTER (*(uint16_t *)&data[counter_pos])
     77 #define DATA (data[data_pos])
     78     
     79     while (uncompressed_size) {
     80         if (prev_mode) {
     81             if (COUNTER) {
     82                 COUNTER--;
     83                 *(dest++) = *(prev++);
     84                 uncompressed_size--;
     85             }
     86             else {
     87                 prev_mode = false;
     88                 counter_pos += sizeof(uint16_t);
     89                 data_pos = counter_pos + sizeof(uint16_t);
     90             }
     91         }
     92         else {
     93             if (COUNTER) {
     94                 COUNTER--;
     95                 *(dest++) = DATA;
     96                 data_pos++;
     97                 prev++;
     98                 uncompressed_size--;
     99             }
    100             else {
    101                 prev_mode = true;
    102                 counter_pos = data_pos;
    103                 data_pos += sizeof(uint16_t);
    104             }
    105         }
    106     }
    107 #undef DATA
    108 #undef COUNTER
    109 }
    110 
    111 void GB_rewind_push(GB_gameboy_t *gb)
    112 {
    113     const size_t save_size = GB_get_save_state_size_no_bess(gb);
    114     if (gb->rewind_state_size != save_size) {
    115         GB_rewind_reset(gb);
    116         gb->rewind_state_size = save_size;
    117     }
    118     if (!gb->rewind_sequences) {
    119         if (gb->rewind_buffer_length) {
    120             gb->rewind_sequences = malloc(sizeof(*gb->rewind_sequences) * gb->rewind_buffer_length);
    121             memset(gb->rewind_sequences, 0, sizeof(*gb->rewind_sequences) * gb->rewind_buffer_length);
    122             gb->rewind_pos = 0;
    123         }
    124         else {
    125             return;
    126         }
    127     }
    128     
    129     if (gb->rewind_sequences[gb->rewind_pos].pos == GB_REWIND_FRAMES_PER_KEY) {
    130         gb->rewind_pos++;
    131         if (gb->rewind_pos == gb->rewind_buffer_length) {
    132             gb->rewind_pos = 0;
    133         }
    134         if (gb->rewind_sequences[gb->rewind_pos].key_state) {
    135             free(gb->rewind_sequences[gb->rewind_pos].key_state);
    136             gb->rewind_sequences[gb->rewind_pos].key_state = NULL;
    137         }
    138         for (unsigned i = 0; i < GB_REWIND_FRAMES_PER_KEY; i++) {
    139             if (gb->rewind_sequences[gb->rewind_pos].compressed_states[i]) {
    140                 free(gb->rewind_sequences[gb->rewind_pos].compressed_states[i]);
    141                 gb->rewind_sequences[gb->rewind_pos].compressed_states[i] = NULL;
    142             }
    143         }
    144         gb->rewind_sequences[gb->rewind_pos].pos = 0;
    145     }
    146     
    147     if (!gb->rewind_sequences[gb->rewind_pos].key_state) {
    148         gb->rewind_sequences[gb->rewind_pos].key_state = malloc(save_size);
    149         gb->rewind_sequences[gb->rewind_pos].instruction_count[0] = 0;
    150         GB_save_state_to_buffer_no_bess(gb, gb->rewind_sequences[gb->rewind_pos].key_state);
    151     }
    152     else {
    153         uint8_t *save_state = malloc(save_size);
    154         assert(gb->rewind_sequences[gb->rewind_pos].key_state);
    155         GB_save_state_to_buffer_no_bess(gb, save_state);
    156         gb->rewind_sequences[gb->rewind_pos].compressed_states[gb->rewind_sequences[gb->rewind_pos].pos++] =
    157             state_compress(gb->rewind_sequences[gb->rewind_pos].key_state, save_state, save_size);
    158         gb->rewind_sequences[gb->rewind_pos].instruction_count[gb->rewind_sequences[gb->rewind_pos].pos] = 0;
    159         free(save_state);
    160     }
    161     
    162 }
    163 
    164 bool GB_rewind_pop(GB_gameboy_t *gb)
    165 {
    166     GB_ASSERT_NOT_RUNNING(gb)
    167     
    168     if (!gb->rewind_sequences || !gb->rewind_sequences[gb->rewind_pos].key_state) {
    169         return false;
    170     }
    171     
    172     const size_t save_size = GB_get_save_state_size_no_bess(gb);
    173     if (gb->rewind_sequences[gb->rewind_pos].pos == 0) {
    174         gb->rewind_disable_invalidation = true;
    175         GB_load_state_from_buffer(gb, gb->rewind_sequences[gb->rewind_pos].key_state, save_size);
    176         gb->rewind_disable_invalidation = false;
    177         free(gb->rewind_sequences[gb->rewind_pos].key_state);
    178         gb->rewind_sequences[gb->rewind_pos].key_state = NULL;
    179         gb->rewind_pos = gb->rewind_pos == 0? gb->rewind_buffer_length - 1 : gb->rewind_pos - 1;
    180         return true;
    181     }
    182     
    183     uint8_t *save_state = malloc(save_size);
    184     state_decompress(gb->rewind_sequences[gb->rewind_pos].key_state,
    185                      gb->rewind_sequences[gb->rewind_pos].compressed_states[--gb->rewind_sequences[gb->rewind_pos].pos],
    186                      save_state,
    187                      save_size);
    188     free(gb->rewind_sequences[gb->rewind_pos].compressed_states[gb->rewind_sequences[gb->rewind_pos].pos]);
    189     gb->rewind_sequences[gb->rewind_pos].compressed_states[gb->rewind_sequences[gb->rewind_pos].pos] = NULL;
    190     gb->rewind_disable_invalidation = true;
    191     GB_load_state_from_buffer(gb, save_state, save_size);
    192     gb->rewind_disable_invalidation = false;
    193     free(save_state);
    194     return true;
    195 }
    196 
    197 void GB_rewind_reset(GB_gameboy_t *gb)
    198 {
    199     GB_ASSERT_NOT_RUNNING_OTHER_THREAD(gb)
    200     
    201     if (!gb->rewind_sequences) return;
    202     for (unsigned i = 0; i < gb->rewind_buffer_length; i++) {
    203         if (gb->rewind_sequences[i].key_state) {
    204             free(gb->rewind_sequences[i].key_state);
    205         }
    206         for (unsigned j = 0; j < GB_REWIND_FRAMES_PER_KEY; j++) {
    207             if (gb->rewind_sequences[i].compressed_states[j]) {
    208                 free(gb->rewind_sequences[i].compressed_states[j]);
    209             }
    210         }
    211     }
    212     free(gb->rewind_sequences);
    213     gb->rewind_sequences = NULL;
    214 }
    215 
    216 void GB_set_rewind_length(GB_gameboy_t *gb, double seconds)
    217 {
    218     GB_rewind_reset(gb);
    219     if (seconds == 0) {
    220         gb->rewind_buffer_length = 0;
    221     }
    222     else {
    223         gb->rewind_buffer_length = (size_t) ceil(seconds * CPU_FREQUENCY / LCDC_PERIOD / GB_REWIND_FRAMES_PER_KEY);
    224     }
    225 }
    226 
    227 void GB_rewind_invalidate_for_backstepping(GB_gameboy_t *gb)
    228 {
    229     if (gb->rewind_disable_invalidation) return;
    230     if (gb->rewind_sequences && gb->rewind_sequences[gb->rewind_pos].key_state) {
    231         typeof(gb->rewind_sequences[0]) *sequence = &gb->rewind_sequences[gb->rewind_pos];
    232         sequence->instruction_count[sequence->pos] |= 0x80000000;
    233     }
    234 }

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