diff --git a/Core/apu.h b/Core/apu.h index 7a0d6ee..042299e 100644 --- a/Core/apu.h +++ b/Core/apu.h @@ -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 + diff --git a/Core/gb.c b/Core/gb.c index 61f3da3..d2d8806 100644 --- a/Core/gb.c +++ b/Core/gb.c @@ -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) diff --git a/Core/gb.h b/Core/gb.h index 7a12fd5..50d6f05 100644 --- a/Core/gb.h +++ b/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 */ diff --git a/Core/memory.c b/Core/memory.c index b46cf5e..62383c5 100644 --- a/Core/memory.c +++ b/Core/memory.c @@ -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; diff --git a/Core/printer.c b/Core/printer.c index dfd6f08..93f51fa 100644 --- a/Core/printer.c +++ b/Core/printer.c @@ -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; } diff --git a/Core/printer.h b/Core/printer.h index a2ccbf9..7a41776 100644 --- a/Core/printer.h +++ b/Core/printer.h @@ -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; diff --git a/Core/save_state.c b/Core/save_state.c index 7733a93..78b9dd5 100644 --- a/Core/save_state.c +++ b/Core/save_state.c @@ -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)); +} diff --git a/Core/save_state.h b/Core/save_state.h index cb98e26..43b0fef 100644 --- a/Core/save_state.h +++ b/Core/save_state.h @@ -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); diff --git a/Core/timing.h b/Core/timing.h index 50d7727..9e44cb7 100644 --- a/Core/timing.h +++ b/Core/timing.h @@ -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 {\ diff --git a/Core/workboy.c b/Core/workboy.c index 3b10379..8144022 100644 --- a/Core/workboy.c +++ b/Core/workboy.c @@ -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)