mirror of https://github.com/LIJI32/SameBoy.git
Break save state compatibility. Windows save states should now work with non-Windows save states again.
This commit is contained in:
parent
2b6cf0c8f1
commit
830f2ddc38
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
42
Core/gb.h
42
Core/gb.h
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {\
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue