diff --git a/Core/gb.c b/Core/gb.c index f382c8e..01c049a 100755 --- a/Core/gb.c +++ b/Core/gb.c @@ -476,7 +476,6 @@ void GB_reset(GB_gameboy_t *gb) gb->is_cgb = true; gb->cgb_mode = true; - gb->io_registers[GB_IO_OBP0] = gb->io_registers[GB_IO_OBP1] = 0x00; } else { gb->ram_size = 0x2000; @@ -494,11 +493,14 @@ void GB_reset(GB_gameboy_t *gb) gb->sprite_palettes_rgb[7] = gb->sprite_palettes_rgb[3] = gb->background_palettes_rgb[3] = gb->rgb_encode_callback(gb, 0, 0, 0); } - gb->io_registers[GB_IO_OBP0] = gb->io_registers[GB_IO_OBP1] = 0xFF; } /* The serial interrupt always occur on the 0xF7th cycle of every 0x100 cycle since boot. */ gb->serial_cycles = 0x100-0xF7; gb->io_registers[GB_IO_SC] = 0x7E; + + /* These are not deterministic, but 00 (CGB) and FF (DMG) are the most common initial values by far */ + gb->io_registers[GB_IO_DMA] = gb->io_registers[GB_IO_OBP0] = gb->io_registers[GB_IO_OBP1] = gb->is_cgb? 0x00 : 0xFF; + gb->magic = (uintptr_t)'SAME'; } diff --git a/Core/memory.c b/Core/memory.c index 875ef62..41345da 100644 --- a/Core/memory.c +++ b/Core/memory.c @@ -31,7 +31,7 @@ static GB_bus_t bus_for_addr(GB_gameboy_t *gb, uint16_t addr) static bool is_addr_in_dma_use(GB_gameboy_t *gb, uint16_t addr) { - if (!gb->dma_steps_left || (gb->dma_cycles < 0 && !gb->is_dma_restarting)) return false; + if (!gb->dma_steps_left || (gb->dma_cycles < 0 && !gb->is_dma_restarting) || addr >= 0xFE00) return false; return bus_for_addr(gb, addr) == bus_for_addr(gb, gb->dma_current_src); } @@ -174,6 +174,7 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr) case GB_IO_WX: case GB_IO_SC: case GB_IO_SB: + case GB_IO_DMA: return gb->io_registers[addr & 0xFF]; case GB_IO_TIMA: if (gb->tima_reload_state == GB_TIMA_RELOADING) { @@ -232,9 +233,6 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr) } return ret; } - case GB_IO_DMA: - /* Todo: is this documented? */ - return gb->is_cgb? 0x00 : 0xFF; case GB_IO_UNKNOWN2: case GB_IO_UNKNOWN3: return gb->is_cgb? gb->io_registers[addr & 0xFF] : 0xFF; @@ -516,21 +514,18 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) return; case GB_IO_DMA: - if (value <= 0xE0) { - if (gb->dma_steps_left) { - /* This is not correct emulation, since we're not really delaying the second DMA. - One write that should have happened in the first DMA will not happen. However, - since that byte will be overwritten by the second DMA before it can actually be - read, it doesn't actually matter. */ - gb->is_dma_restarting = true; - } - gb->dma_cycles = -7; - gb->dma_current_dest = 0; - gb->dma_current_src = value << 8; - gb->dma_steps_left = 0xa0; + if (gb->dma_steps_left) { + /* This is not correct emulation, since we're not really delaying the second DMA. + One write that should have happened in the first DMA will not happen. However, + since that byte will be overwritten by the second DMA before it can actually be + read, it doesn't actually matter. */ + gb->is_dma_restarting = true; } - /* else { what? } */ - + gb->dma_cycles = -7; + gb->dma_current_dest = 0; + gb->dma_current_src = value << 8; + gb->dma_steps_left = 0xa0; + gb->io_registers[GB_IO_DMA] = value; return; case GB_IO_SVBK: if (!gb->cgb_mode) { @@ -708,7 +703,14 @@ void GB_dma_run(GB_gameboy_t *gb) /* Todo: measure this value */ gb->dma_cycles -= 4; gb->dma_steps_left--; - gb->oam[gb->dma_current_dest++] = GB_read_memory(gb, gb->dma_current_src); + if (gb->dma_current_src < 0xe000) { + gb->oam[gb->dma_current_dest++] = GB_read_memory(gb, gb->dma_current_src); + } + else { + /* Todo: Not correct on the CGB */ + gb->oam[gb->dma_current_dest++] = GB_read_memory(gb, gb->dma_current_src & ~0x2000); + } + /* dma_current_src must be the correct value during GB_read_memory */ gb->dma_current_src++; if (!gb->dma_steps_left) { diff --git a/libretro/Makefile b/libretro/Makefile index 6350fd1..cde75a0 100644 --- a/libretro/Makefile +++ b/libretro/Makefile @@ -161,7 +161,7 @@ CFLAGS += -Wall -D__LIBRETRO__ $(fpic) $(INCFLAGS) -std=gnu11 -D_GNU_SOURCE -D all: $(TARGET) -$(CORE_DIR)/libretro/%_boot.c: $(CORE_DIR)/BootROMs/prebuilt/%_boot.bin +$(CORE_DIR)/libretro/%_boot.c: $(CORE_DIR)/build/bin/BootROMs/%_boot.bin echo "/* AUTO-GENERATED */" > $@ echo "const unsigned char $(notdir $(@:%.c=%))[] = {" >> $@ ifneq ($(findstring Haiku,$(shell uname -s)),) diff --git a/libretro/libretro.c b/libretro/libretro.c index cbeece9..275df1d 100644 --- a/libretro/libretro.c +++ b/libretro/libretro.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -81,7 +82,7 @@ static retro_input_poll_t input_poll_cb; static retro_input_state_t input_state_cb; static unsigned emulated_devices = 1; -static unsigned pre_init = 1; +static bool initialized = false; static unsigned screen_layout = 0; static unsigned audio_out = 0; @@ -116,6 +117,25 @@ static void fallback_log(enum retro_log_level level, const char *fmt, ...) static struct retro_rumble_interface rumble; +static void extract_basename(char *buf, const char *path, size_t size) +{ + const char *base = strrchr(path, '/'); + if (!base) + base = strrchr(path, '\\'); + if (!base) + base = path; + + if (*base == '\\' || *base == '/') + base++; + + strncpy(buf, base, size - 1); + buf[size - 1] = '\0'; + + char *ext = strrchr(buf, '.'); + if (ext) + *ext = '\0'; +} + static void GB_update_keys_status(GB_gameboy_t *gb, unsigned port) { @@ -201,40 +221,43 @@ static uint32_t rgb_encode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b) static retro_environment_t environ_cb; -static const struct retro_variable vars[] = { - { "sameboy_dual", "Dual Game Boy Mode (restart); disabled|enabled" }, - { "sameboy_color_correction_mode", "Color Correction; off|correct curves|emulate hardware|preserve brightness" }, - { "sameboy_high_pass_filter_mode", "High Pass Filter; off|accurate|remove dc offset" }, - { "sameboy_model", "Emulated Model; Game Boy Color|Game Boy Advance|Auto|Game Boy" }, +/* variables for single cart mode */ +static const struct retro_variable vars_single[] = { + { "sameboy_dual", "Single cart dual mode (reload); disabled|enabled" }, + { "sameboy_color_correction_mode", "Color correction; off|correct curves|emulate hardware|preserve brightness" }, + { "sameboy_high_pass_filter_mode", "High-pass filter; off|accurate|remove dc offset" }, + { "sameboy_model", "Emulated model (reload); Game Boy Color|Game Boy Advance|Auto|Game Boy" }, { NULL } }; -static const struct retro_variable vars_sameboy_dual[] = { - { "sameboy_dual", "Dual Game Boy Mode (restart); disabled|enabled" }, - { "sameboy_link", "Link Cable Emulation; enabled|disabled" }, +/* variables for single cart dual gameboy mode */ +static const struct retro_variable vars_single_dual[] = { + { "sameboy_dual", "Single cart dual mode (reload); disabled|enabled" }, + { "sameboy_link", "Link cable emulation; enabled|disabled" }, /*{ "sameboy_ir", "Infrared Sensor Emulation; disabled|enabled" },*/ - { "sameboy_screen_layout", "Screen Layout; top-down|left-right" }, + { "sameboy_screen_layout", "Screen layout; top-down|left-right" }, { "sameboy_audio_output", "Audio output; Game Boy #1|Game Boy #2" }, - { "sameboy_model_1", "Emulated Model for Game Boy #1; Game Boy Color|Game Boy Advance|Auto|Game Boy" }, - { "sameboy_model_2", "Emulated Model for Game Boy #2; Game Boy Color|Game Boy Advance|Auto|Game Boy" }, - { "sameboy_color_correction_mode_1", "Color Correction for Game Boy #1; off|correct curves|emulate hardware|preserve brightness" }, - { "sameboy_color_correction_mode_2", "Color Correction for Game Boy #2; off|correct curves|emulate hardware|preserve brightness" }, - { "sameboy_high_pass_filter_mode_1", "High Pass Filter for Game Boy #1; off|accurate|remove dc offset" }, - { "sameboy_high_pass_filter_mode_2", "High Pass Filter for Game Boy #2; off|accurate|remove dc offset" }, + { "sameboy_model_1", "Emulated model for Game Boy #1 (reload); Game Boy Color|Game Boy Advance|Auto|Game Boy" }, + { "sameboy_model_2", "Emulated model for Game Boy #2 (reload); Game Boy Color|Game Boy Advance|Auto|Game Boy" }, + { "sameboy_color_correction_mode_1", "Color correction for Game Boy #1; off|correct curves|emulate hardware|preserve brightness" }, + { "sameboy_color_correction_mode_2", "Color correction for Game Boy #2; off|correct curves|emulate hardware|preserve brightness" }, + { "sameboy_high_pass_filter_mode_1", "High-pass filter for Game Boy #1; off|accurate|remove dc offset" }, + { "sameboy_high_pass_filter_mode_2", "High-pass filter for Game Boy #2; off|accurate|remove dc offset" }, { NULL } }; -static const struct retro_variable vars_link_dual[] = { - { "sameboy_link", "Link Cable Emulation; enabled|disabled" }, +/* variables for dual cart dual gameboy mode */ +static const struct retro_variable vars_dual[] = { + { "sameboy_link", "Link cable emulation; enabled|disabled" }, /*{ "sameboy_ir", "Infrared Sensor Emulation; disabled|enabled" },*/ - { "sameboy_screen_layout", "Screen Layout; top-down|left-right" }, + { "sameboy_screen_layout", "Screen layout; top-down|left-right" }, { "sameboy_audio_output", "Audio output; Game Boy #1|Game Boy #2" }, - { "sameboy_model_1", "Emulated Model for Game Boy #1; Game Boy Color|Game Boy Advance|Auto|Game Boy" }, - { "sameboy_model_2", "Emulated Model for Game Boy #2; Game Boy Color|Game Boy Advance|Auto|Game Boy" }, - { "sameboy_color_correction_mode_1", "Color Correction for Game Boy #1; off|correct curves|emulate hardware|preserve brightness" }, - { "sameboy_color_correction_mode_2", "Color Correction for Game Boy #2; off|correct curves|emulate hardware|preserve brightness" }, - { "sameboy_high_pass_filter_mode_1", "High Pass Filter for Game Boy #1; off|accurate|remove dc offset" }, - { "sameboy_high_pass_filter_mode_2", "High Pass Filter for Game Boy #2; off|accurate|remove dc offset" }, + { "sameboy_model_1", "Emulated model for Game Boy #1 (reload); Game Boy Color|Game Boy Advance|Auto|Game Boy" }, + { "sameboy_model_2", "Emulated model for Game Boy #2 (reload); Game Boy Color|Game Boy Advance|Auto|Game Boy" }, + { "sameboy_color_correction_mode_1", "Color correction for Game Boy #1; off|correct curves|emulate hardware|preserve brightness" }, + { "sameboy_color_correction_mode_2", "Color correction for Game Boy #2; off|correct curves|emulate hardware|preserve brightness" }, + { "sameboy_high_pass_filter_mode_1", "High-pass filter for Game Boy #1; off|accurate|remove dc offset" }, + { "sameboy_high_pass_filter_mode_2", "High-pass filter for Game Boy #2; off|accurate|remove dc offset" }, { NULL } }; @@ -418,10 +441,6 @@ static void check_variables(bool link) else new_model = MODEL_AUTO; - if (GB_is_inited(&gameboy[0]) && new_model != model[0]) { - model[0] = new_model; - init_for_current_model(); - } model[0] = new_model; } } @@ -492,11 +511,7 @@ static void check_variables(bool link) new_model = MODEL_AGB; else new_model = MODEL_AUTO; - - if (GB_is_inited(&gameboy[0]) && new_model != model[0]) { - model[0] = new_model; - init_for_current_model(); - } + model[0] = new_model; } @@ -513,11 +528,7 @@ static void check_variables(bool link) new_model = MODEL_AGB; else new_model = MODEL_AUTO; - - if (GB_is_inited(&gameboy[1]) && new_model != model[1]) { - model[1] = new_model; - init_for_current_model(); - } + model[1] = new_model; } @@ -715,9 +726,10 @@ void retro_reset(void) void retro_run(void) { + bool updated = false; - if (pre_init) + if (!initialized) geometry_updated = false; if (geometry_updated) { @@ -727,8 +739,6 @@ void retro_run(void) environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &info.geometry); } - pre_init = 0; - if (!frame_buf) return; @@ -769,17 +779,19 @@ void retro_run(void) video_cb(frame_buf_copy, VIDEO_WIDTH * emulated_devices, VIDEO_HEIGHT, VIDEO_WIDTH * emulated_devices * sizeof(uint32_t)); } + + initialized = true; } bool retro_load_game(const struct retro_game_info *info) { - environ_cb(RETRO_ENVIRONMENT_SET_VARIABLES, (void *)vars); + environ_cb(RETRO_ENVIRONMENT_SET_VARIABLES, (void *)vars_single); check_variables(false); if (sameboy_dual) { emulated_devices = 2; mode = MODE_SINGLE_GAME_DUAL; - environ_cb(RETRO_ENVIRONMENT_SET_VARIABLES, (void *)vars_sameboy_dual); + environ_cb(RETRO_ENVIRONMENT_SET_VARIABLES, (void *)vars_single_dual); check_variables(true); } else @@ -821,11 +833,36 @@ bool retro_load_game(const struct retro_game_info *info) log_cb(RETRO_LOG_INFO, "Rumble environment not supported\n"); check_variables(emulated_devices == 2 ? true : false); + + /* hack: use upstream's file based I/O for Game Boy 2 battery in single cart mode */ + if (mode == MODE_SINGLE_GAME_DUAL) + { + char path[PATH_MAX]; + char file[PATH_MAX]; + + extract_basename(file, retro_game_path, sizeof(file)); + snprintf (path, sizeof(path), "%s%c%s.srm.2", retro_save_directory, slash, file); + log_cb(RETRO_LOG_INFO, "Loading battery for Game Boy 2 from: %s\n", path); + GB_load_battery(&gameboy[1], path); + } + return true; } void retro_unload_game(void) { + /* hack: use upstream's file based I/O for Game Boy 2 battery in single cart mode */ + if (mode == MODE_SINGLE_GAME_DUAL) + { + char path[PATH_MAX]; + char file[PATH_MAX]; + + extract_basename(file, retro_game_path, sizeof(file)); + snprintf (path, sizeof(path), "%s%c%s.srm.2", retro_save_directory, slash, file); + log_cb(RETRO_LOG_INFO, "Saving battery for Game Boy 2 to: %s\n", path); + GB_save_battery(&gameboy[1], path); + } + for (int i = 0; i < emulated_devices; i++) GB_free(&gameboy[i]); } @@ -846,7 +883,7 @@ bool retro_load_game_special(unsigned type, const struct retro_game_info *info, else return false; /* all other types are unhandled for now */ - environ_cb(RETRO_ENVIRONMENT_SET_VARIABLES, (void *)vars_link_dual); + environ_cb(RETRO_ENVIRONMENT_SET_VARIABLES, (void *)vars_dual); check_variables(true); frame_buf = (uint32_t*)malloc(emulated_devices * VIDEO_PIXELS * sizeof(uint32_t)); @@ -897,18 +934,21 @@ size_t retro_serialize_size(void) bool retro_serialize(void *data, size_t size) { - if (pre_init == 1) + + if (!initialized) return false; void* save_data[2]; size_t state_size[2]; + size_t offset = 0; for (int i = 0; i < emulated_devices; i++) { state_size[i] = GB_get_save_state_size(&gameboy[i]); save_data[i] = (uint8_t*)malloc(state_size[i]); GB_save_state_to_buffer(&gameboy[i], (uint8_t*) save_data[i]); - memcpy(data + (state_size[i] * i), save_data[i], state_size[i]); + memcpy(data + offset, save_data[i], state_size[i]); + offset += state_size[i]; free(save_data[i]); } @@ -973,7 +1013,7 @@ void *retro_get_memory_data(unsigned type) } } break; - case MODE_DUAL_GAME: /* todo: hook up other memory types */ + case MODE_DUAL_GAME: { switch (type) { @@ -989,6 +1029,18 @@ void *retro_get_memory_data(unsigned type) else data = NULL; break; + case RETRO_MEMORY_GAMEBOY_1_RTC: + if(gameboy[0].cartridge_type->has_battery) + data = &gameboy[0].rtc_real; + else + data = NULL; + break; + case RETRO_MEMORY_GAMEBOY_2_RTC: + if(gameboy[1].cartridge_type->has_battery) + data = &gameboy[1].rtc_real; + else + data = NULL; + break; default: break; } @@ -1033,7 +1085,7 @@ size_t retro_get_memory_size(unsigned type) } } break; - case MODE_DUAL_GAME: /* todo: hook up other memory types */ + case MODE_DUAL_GAME: { switch (type) { @@ -1049,8 +1101,16 @@ size_t retro_get_memory_size(unsigned type) else size = 0; break; + case RETRO_MEMORY_GAMEBOY_1_RTC: + if(gameboy[0].cartridge_type->has_battery) + size = sizeof (gameboy[0].rtc_real); + break; + case RETRO_MEMORY_GAMEBOY_2_RTC: + if(gameboy[1].cartridge_type->has_battery) + size = sizeof (gameboy[1].rtc_real); + break; default: - break;; + break; } } break;