Break save state compatibility. Windows save states should now work with non-Windows save states again.

This commit is contained in:
Lior Halphon 2023-02-04 02:13:01 +02:00
parent 2b6cf0c8f1
commit 830f2ddc38
10 changed files with 99 additions and 41 deletions

View File

@ -51,7 +51,7 @@ typedef struct
bool locked:1; // Represents FYNO's output on channel 4
bool clock:1; // Represents FOSY on channel 4
bool should_lock:1; // Represents FYNO's input on channel 4
unsigned padding:5;
uint8_t padding:5;
} GB_envelope_clock_t;
typedef void (*GB_sample_callback_t)(GB_gameboy_t *gb, GB_sample_t *sample);
@ -129,11 +129,11 @@ typedef struct
GB_envelope_clock_t envelope_clock;
} noise_channel;
enum {
GB_ENUM(uint8_t, {
GB_SKIP_DIV_EVENT_INACTIVE,
GB_SKIP_DIV_EVENT_SKIPPED,
GB_SKIP_DIV_EVENT_SKIP,
} skip_div_event:8;
}) skip_div_event;
uint8_t pcm_mask[2]; // For CGB-0 to CGB-C PCM read glitch
} GB_apu_t;
@ -199,3 +199,4 @@ internal void GB_apu_run(GB_gameboy_t *gb, bool force);
#endif
#endif

View File

@ -1358,8 +1358,7 @@ void GB_disconnect_serial(GB_gameboy_t *gb)
gb->serial_transfer_bit_end_callback = NULL;
/* Reset any internally-emulated device. */
memset(&gb->printer, 0, sizeof(gb->printer));
memset(&gb->workboy, 0, sizeof(gb->workboy));
memset(GB_GET_SECTION(gb, accessory), 0, GB_SECTION_SIZE(accessory));
}
bool GB_is_inited(GB_gameboy_t *gb)

View File

@ -28,7 +28,7 @@
#include "workboy.h"
#include "random.h"
#define GB_STRUCT_VERSION 14
#define GB_STRUCT_VERSION 15
#define GB_REWIND_FRAMES_PER_KEY 255
@ -390,10 +390,8 @@ struct GB_gameboy_internal_s {
/* Misc state */
bool infrared_input;
GB_printer_t printer;
uint8_t extra_oam[0xFF00 - 0xFEA0];
uint32_t ram_size; // Different between CGB and DMG
GB_workboy_t workboy;
int32_t ir_sensor;
bool effective_ir_input;
@ -416,7 +414,6 @@ struct GB_gameboy_internal_s {
int8_t dma_cycles_modulo;
bool dma_ppu_vram_conflict;
uint16_t dma_ppu_vram_conflict_addr;
GB_PADDING(uint8_t, hdma_open_bus);
bool allow_hdma_on_wake;
bool dma_restarting;
)
@ -452,9 +449,9 @@ struct GB_gameboy_internal_s {
} mbc5; // Also used for GB_CAMERA
struct {
uint8_t rom_bank;
uint16_t x_latch;
uint16_t y_latch;
uint8_t rom_bank;
bool latch_ready:1;
bool eeprom_do:1;
bool eeprom_di:1;
@ -487,17 +484,17 @@ struct GB_gameboy_internal_s {
struct {
uint8_t bank_low:6;
uint8_t bank_high:3;
bool ir_mode;
bool ir_mode:1;
} huc1;
struct {
uint8_t rom_bank:7;
uint8_t padding:1;
uint8_t ram_bank:4;
uint8_t mode;
uint8_t access_index;
uint8_t mode:4;
uint16_t minutes, days;
uint16_t alarm_minutes, alarm_days;
uint8_t access_index;
bool alarm_enabled;
uint8_t read;
uint8_t access_flags;
@ -509,10 +506,11 @@ struct GB_gameboy_internal_s {
uint8_t mode;
} tpp1;
};
bool camera_registers_mapped;
uint8_t camera_registers[0x36];
uint8_t rumble_strength;
bool cart_ir;
bool camera_registers_mapped;
uint8_t camera_registers[0x36];
uint8_t camera_alignment;
int32_t camera_countdown;
)
@ -528,7 +526,11 @@ struct GB_gameboy_internal_s {
GB_UNIT(display);
GB_UNIT(div);
uint16_t div_counter;
uint8_t tima_reload_state; /* After TIMA overflows, it becomes 0 for 4 cycles before actually reloading. */
GB_ENUM(uint8_t, {
GB_TIMA_RUNNING = 0,
GB_TIMA_RELOADING = 1,
GB_TIMA_RELOADED = 2
}) tima_reload_state; /* After TIMA overflows, it becomes 0 for 4 cycles before actually reloading. */
bool serial_master_clock;
uint8_t serial_mask;
uint8_t double_speed_alignment;
@ -574,13 +576,12 @@ struct GB_gameboy_internal_s {
See https://www.reddit.com/r/EmuDev/comments/6exyxu/ */
/* TODO: Drop this and properly emulate the dropped vreset signal*/
enum {
GB_ENUM(uint8_t, {
GB_FRAMESKIP_LCD_TURNED_ON, // On a DMG, the LCD renders a blank screen during this state,
// on a CGB, the previous frame is repeated (which might be
// blank if the LCD was off for more than a few cycles)
GB_FRAMESKIP_FIRST_FRAME_SKIPPED__DEPRECATED,
GB_FRAMESKIP_FIRST_FRAME_RENDERED,
} frame_skip_state;
}) frame_skip_state;
bool oam_read_blocked;
bool vram_read_blocked;
bool oam_write_blocked;
@ -628,13 +629,24 @@ struct GB_gameboy_internal_s {
bool is_odd_frame;
uint16_t last_tile_data_address;
uint16_t last_tile_index_address;
GB_PADDING(bool, cgb_repeated_a_frame);
uint8_t data_for_sel_glitch;
bool delayed_glitch_hblank_interrupt;
uint32_t frame_repeat_countdown;
bool disable_window_pixel_insertion_glitch;
bool insert_bg_pixel;
)
GB_SECTION(accessory,
GB_ENUM(uint8_t, {
GB_ACCESSORY_NONE,
GB_ACCESSORY_PRINTER,
GB_ACCESSORY_WORKBOY,
}) accessory;
union {
GB_printer_t printer;
GB_workboy_t workboy;
};
)
/* Unsaved data. This includes all pointers, as well as everything that shouldn't be on a save state */
/* This data is reserved on reset and must come last in the struct */

View File

@ -920,7 +920,7 @@ static void write_mbc(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
case GB_HUC3:
switch (addr & 0xF000) {
case 0x0000: case 0x1000:
gb->huc3.mode = value & 0xF;
gb->huc3.mode = value;
gb->mbc_ram_enable = gb->huc3.mode == 0xA;
break;
case 0x2000: case 0x3000: gb->huc3.rom_bank = value; break;

View File

@ -218,4 +218,5 @@ void GB_connect_printer(GB_gameboy_t *gb, GB_print_image_callback_t callback, GB
GB_set_serial_transfer_bit_end_callback(gb, serial_end);
gb->printer_callback = callback;
gb->printer_done_callback = done_callback;
gb->accessory = GB_ACCESSORY_PRINTER;
}

View File

@ -15,12 +15,11 @@ typedef void (*GB_print_image_callback_t)(GB_gameboy_t *gb,
typedef void (*GB_printer_done_callback_t)(GB_gameboy_t *gb);
typedef struct
{
/* Communication state machine */
enum {
GB_ENUM(uint8_t, {
GB_PRINTER_COMMAND_MAGIC1,
GB_PRINTER_COMMAND_MAGIC2,
GB_PRINTER_COMMAND_ID,
@ -32,13 +31,13 @@ typedef struct
GB_PRINTER_COMMAND_CHECKSUM_HIGH,
GB_PRINTER_COMMAND_ACTIVE,
GB_PRINTER_COMMAND_STATUS,
} command_state : 8;
enum {
}) command_state;
GB_ENUM(uint8_t, {
GB_PRINTER_INIT_COMMAND = 1,
GB_PRINTER_START_COMMAND = 2,
GB_PRINTER_DATA_COMMAND = 4,
GB_PRINTER_NOP_COMMAND = 0xF,
} command_id : 8;
}) command_id;
bool compression;
uint16_t length_left;
uint8_t command_data[GB_PRINTER_MAX_COMMAND_LENGTH];
@ -50,14 +49,8 @@ typedef struct
uint8_t image[160 * 200];
uint16_t image_offset;
// TODO: fix this when breaking backwards compatibility
#ifdef GB_BIG_ENDIAN
uint32_t time_remaining;
uint32_t idle_time;
#else
uint32_t idle_time;
uint32_t time_remaining;
#endif
uint8_t compression_run_lenth;
bool compression_run_is_compressed;

View File

@ -17,6 +17,7 @@ _Static_assert((GB_SECTION_OFFSET(timing) & 7) == 0, "Section timing is not alig
_Static_assert((GB_SECTION_OFFSET(apu) & 7) == 0, "Section apu is not aligned");
_Static_assert((GB_SECTION_OFFSET(rtc) & 7) == 0, "Section rtc is not aligned");
_Static_assert((GB_SECTION_OFFSET(video) & 7) == 0, "Section video is not aligned");
_Static_assert((GB_SECTION_OFFSET(accessory) & 7) == 0, "Section accessory is not aligned");
typedef struct __attribute__((packed)) {
uint32_t magic;
@ -280,6 +281,7 @@ size_t GB_get_save_state_size_no_bess(GB_gameboy_t *gb)
+ GB_SECTION_SIZE(apu ) + sizeof(uint32_t)
+ GB_SECTION_SIZE(rtc ) + sizeof(uint32_t)
+ GB_SECTION_SIZE(video ) + sizeof(uint32_t)
+ GB_SECTION_SIZE(accessory ) + sizeof(uint32_t)
+ (GB_is_hle_sgb(gb)? sizeof(*gb->sgb) + sizeof(uint32_t) : 0)
+ gb->mbc_ram_size
+ gb->ram_size
@ -336,6 +338,10 @@ static bool verify_and_update_state_compatibility(GB_gameboy_t *gb, GB_gameboy_t
return false;
}
if (gb->accessory != save->accessory) {
memset(GB_GET_SECTION(save, accessory), 0, GB_SECTION_SIZE(accessory));
}
switch (save->model) {
case GB_MODEL_DMG_B: return true;
case GB_MODEL_SGB_NTSC: return true;
@ -358,6 +364,7 @@ static bool verify_and_update_state_compatibility(GB_gameboy_t *gb, GB_gameboy_t
save->model = gb->model;
return true;
}
GB_log(gb, "This save state is for an unknown Game Boy model\n");
return false;
}
@ -544,7 +551,8 @@ static int save_state_internal(GB_gameboy_t *gb, virtual_file_t *file, bool appe
if (!DUMP_SECTION(gb, file, rtc )) goto error;
uint32_t video_offset = file->tell(file) + 4;
if (!DUMP_SECTION(gb, file, video )) goto error;
if (!DUMP_SECTION(gb, file, accessory )) goto error;
uint32_t sgb_offset = 0;
if (GB_is_hle_sgb(gb)) {
@ -1304,7 +1312,8 @@ static int load_state_internal(GB_gameboy_t *gb, virtual_file_t *file)
if (!READ_SECTION(&save, file, apu )) return errno ?: EIO;
if (!READ_SECTION(&save, file, rtc )) return errno ?: EIO;
if (!READ_SECTION(&save, file, video )) return errno ?: EIO;
if (!READ_SECTION(&save, file, accessory )) return errno ?: EIO;
bool attempt_bess = false;
if (!verify_and_update_state_compatibility(gb, &save, &attempt_bess)) {
@ -1510,3 +1519,47 @@ exit:
fclose(f);
return ret;
}
static void __attribute__((constructor)) wtf(void)
{
printf("base offset %lx\n", GB_SECTION_OFFSET(apu) - offsetof(GB_gameboy_t, apu));
printf("global_enable: %lx\n", offsetof(GB_apu_t, global_enable));
printf("apu_cycles: %lx\n", offsetof(GB_apu_t, apu_cycles));
printf("samples: %lx\n", offsetof(GB_apu_t, samples));
printf("is_active: %lx\n", offsetof(GB_apu_t, is_active));
printf("div_divider: %lx\n", offsetof(GB_apu_t, div_divider));
printf("lf_div: %lx\n", offsetof(GB_apu_t, lf_div));
printf("square_sweep_countdown: %lx\n", offsetof(GB_apu_t, square_sweep_countdown));
printf("square_sweep_calculate_countdown: %lx\n", offsetof(GB_apu_t, square_sweep_calculate_countdown));
printf("sweep_length_addend: %lx\n", offsetof(GB_apu_t, sweep_length_addend));
printf("shadow_sweep_sample_length: %lx\n", offsetof(GB_apu_t, shadow_sweep_sample_length));
printf("unshifted_sweep: %lx\n", offsetof(GB_apu_t, unshifted_sweep));
printf("enable_zombie_calculate_stepping: %lx\n", offsetof(GB_apu_t, enable_zombie_calculate_stepping));
printf("channel_1_restart_hold: %lx\n", offsetof(GB_apu_t, channel_1_restart_hold));
printf("channel1_completed_addend: %lx\n", offsetof(GB_apu_t, channel1_completed_addend));
printf("square_channels: %lx\n", offsetof(GB_apu_t, square_channels));
printf("wave_channel: %lx\n", offsetof(GB_apu_t, wave_channel));
printf("noise_channel: %lx\n", offsetof(GB_apu_t, noise_channel));
printf("pcm_mask: %lx\n", offsetof(GB_apu_t, pcm_mask));
GB_gameboy_t *gb;
printf("pulse_length %lx\n", offsetof(typeof(gb->apu.square_channels[0]), pulse_length));
printf("current_volume %lx\n", offsetof(typeof(gb->apu.square_channels[0]), current_volume));
printf("volume_countdown %lx\n", offsetof(typeof(gb->apu.square_channels[0]), volume_countdown));
printf("current_sample_index %lx\n", offsetof(typeof(gb->apu.square_channels[0]), current_sample_index));
printf("sample_surpressed %lx\n", offsetof(typeof(gb->apu.square_channels[0]), sample_surpressed));
printf("sample_countdown %lx\n", offsetof(typeof(gb->apu.square_channels[0]), sample_countdown));
printf("sample_length %lx\n", offsetof(typeof(gb->apu.square_channels[0]), sample_length));
printf("length_enabled %lx\n", offsetof(typeof(gb->apu.square_channels[0]), length_enabled));
printf("envelope_clock %lx\n", offsetof(typeof(gb->apu.square_channels[0]), envelope_clock));
printf("delay %lx\n", offsetof(typeof(gb->apu.square_channels[0]), delay));
printf("did_tick %lx\n", offsetof(typeof(gb->apu.square_channels[0]), did_tick));
}

View File

@ -21,8 +21,11 @@
#define GB_GET_SECTION(gb, name) ((void*)&((gb)->name##_section_start))
#endif
#define GB_aligned_double __attribute__ ((aligned (8))) double
#if __clang_major__ >= 8 || __GNUC__ >= 13
#define GB_ENUM(type, ...) enum : type __VA_ARGS__
#else
#define GB_ENUM(type, ...) typeof((type)((enum __VA_ARGS__)0))
#endif
/* Public calls related to save states */
int GB_save_state(GB_gameboy_t *gb, const char *path);

View File

@ -20,11 +20,6 @@ internal bool GB_timing_sync_turbo(GB_gameboy_t *gb); /* Returns true if should
internal void GB_timing_sync(GB_gameboy_t *gb);
internal void GB_set_internal_div_counter(GB_gameboy_t *gb, uint16_t value);
internal void GB_serial_master_edge(GB_gameboy_t *gb);
enum {
GB_TIMA_RUNNING = 0,
GB_TIMA_RELOADING = 1,
GB_TIMA_RELOADED = 2
};
#define GB_SLEEP(gb, unit, state, cycles) do {\

View File

@ -147,6 +147,7 @@ void GB_connect_workboy(GB_gameboy_t *gb,
GB_set_serial_transfer_bit_end_callback(gb, serial_end);
gb->workboy_set_time_callback = set_time_callback;
gb->workboy_get_time_callback = get_time_callback;
gb->accessory = GB_ACCESSORY_WORKBOY;
}
bool GB_workboy_is_enabled(GB_gameboy_t *gb)