diff --git a/Makefile.common b/Makefile.common index 7e70627..0dcbedc 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1,6 +1,6 @@ INCFLAGS := -I$(CORE_DIR)/libretro -I$(CORE_DIR)/src -SOURCES_ASM := +SOURCES_ASM := $(CORE_DIR)/bios_data.S ifeq ($(HAVE_GRIFFIN), 1) SOURCES_C := $(CORE_DIR)/gpsp_griffin.c diff --git a/bios/README.md b/bios/README.md new file mode 100644 index 0000000..64d6679 --- /dev/null +++ b/bios/README.md @@ -0,0 +1,7 @@ + +This BIOS is an open source replacement for Nintendo's official BIOS. +It was written originally by Normmatt and the VBA/VBA-M team, and its source +code can be found at https://github.com/Nebuleon/ReGBA/tree/master/bios + +It is distributed under the GPL2 license (see repo) + diff --git a/bios/open_gba_bios.bin b/bios/open_gba_bios.bin new file mode 100644 index 0000000..802982e Binary files /dev/null and b/bios/open_gba_bios.bin differ diff --git a/bios_data.S b/bios_data.S new file mode 100644 index 0000000..9de9d8a --- /dev/null +++ b/bios_data.S @@ -0,0 +1,7 @@ + +.globl open_gba_bios_rom + +.data +open_gba_bios_rom: + .incbin "bios/open_gba_bios.bin" + diff --git a/cpu.c b/cpu.c index 2c93f38..11c947f 100644 --- a/cpu.c +++ b/cpu.c @@ -4297,13 +4297,23 @@ void init_cpu(void) for(i = 0; i < 16; i++) reg[i] = 0; - reg[REG_SP] = 0x03007F00; - reg[REG_PC] = 0x08000000; - reg[REG_CPSR] = 0x0000001F; reg[CPU_HALT_STATE] = CPU_ACTIVE; - reg[CPU_MODE] = MODE_USER; reg[CHANGED_PC_STATUS] = 0; + if (selected_boot_mode == boot_game) { + reg[REG_SP] = 0x03007F00; + reg[REG_PC] = 0x08000000; + reg[REG_CPSR] = 0x0000001F; // system mode + reg[CPU_MODE] = MODE_USER; + } else { + reg[REG_SP] = 0x03007F00; + reg[REG_PC] = 0x00000000; + reg[REG_CPSR] = 0x00000013 | 0xC0; // supervisor + reg[CPU_MODE] = MODE_SUPERVISOR; + } + + // Stack pointers are set by BIOS, we set them + // nevertheless, should we not boot from BIOS reg_mode[MODE_USER][5] = 0x03007F00; reg_mode[MODE_IRQ][5] = 0x03007FA0; reg_mode[MODE_FIQ][5] = 0x03007FA0; diff --git a/gba_memory.h b/gba_memory.h index 9352e88..eaa5bab 100644 --- a/gba_memory.h +++ b/gba_memory.h @@ -201,6 +201,7 @@ extern u32 gbc_sound_update; extern u32 gbc_sound_wave_update; extern dma_transfer_type dma[4]; +extern u8 open_gba_bios_rom[1024*16]; extern u32 bios_read_protect; extern u16 palette_ram[512]; extern u16 oam_ram[512]; diff --git a/libretro.c b/libretro.c index bc61977..334864c 100644 --- a/libretro.c +++ b/libretro.c @@ -65,6 +65,7 @@ static unsigned audio_buff_occupancy = 0; static bool audio_buff_underrun = false; static unsigned audio_latency = 0; static bool update_audio_latency = false; +static bios_type selected_bios = auto_detect; static retro_log_printf_t log_cb; static retro_video_refresh_t video_cb; @@ -75,6 +76,7 @@ struct retro_perf_callback perf_cb; int dynarec_enable; int use_libretro_save_method = 0; +boot_mode selected_boot_mode = boot_game; u32 idle_loop_target_pc = 0xFFFFFFFF; u32 iwram_stack_optimize = 1; @@ -114,6 +116,25 @@ static void info_msg(const char* text) log_cb(RETRO_LOG_INFO, "[gpSP]: %s\n", text); } +static void show_warning_message(const char* text, unsigned durationms) { + unsigned ifversion = 0; + if (!environ_cb(RETRO_ENVIRONMENT_GET_MESSAGE_INTERFACE_VERSION, &ifversion) || ifversion >= 1) { + /* Use the new API to display messages */ + struct retro_message_ext msg = { + .msg = text, .duration = durationms, + .priority = 2, .level = RETRO_LOG_WARN, + .target = RETRO_MESSAGE_TARGET_ALL, + .type = RETRO_MESSAGE_TYPE_NOTIFICATION, + .progress = -1, + }; + environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE_EXT, &msg); + } + else { + struct retro_message msg = {.msg = text, .frames = durationms / 17}; + environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE, &msg); + } +} + /* Frameskip START */ static void audio_buff_status_cb( @@ -348,8 +369,8 @@ static void video_run(void) sceGuTexMode(GU_PSM_5650, 0, 0, GU_FALSE); sceGuCopyImage(GU_PSM_5650, 0, 0, GBA_SCREEN_WIDTH, GBA_SCREEN_HEIGHT, GBA_SCREEN_WIDTH, gba_screen_pixels_buf, 0, 0, GBA_SCREEN_WIDTH, texture_vram_p); - sceGuTexImage(0, next_pow2(GBA_SCREEN_WIDTH), next_pow2(GBA_SCREEN_HEIGHT), GBA_SCREEN_WIDTH, texture_vram_p); - sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGB); + sceGuTexImage(0, next_pow2(GBA_SCREEN_WIDTH), next_pow2(GBA_SCREEN_HEIGHT), GBA_SCREEN_WIDTH, texture_vram_p); + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGB); sceGuDisable(GU_BLEND); sceGuFinish(); @@ -484,6 +505,8 @@ void retro_init(void) audio_buff_underrun = false; audio_latency = 0; update_audio_latency = false; + selected_bios = auto_detect; + selected_boot_mode = boot_game; } void retro_deinit(void) @@ -660,6 +683,32 @@ static void check_variables(int started_from_load) dynarec_enable = 0; #endif + if (started_from_load) { + var.key = "gpsp_bios"; + var.value = 0; + + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + { + if (!strcmp(var.value, "auto")) + selected_bios = auto_detect; + else if (!strcmp(var.value, "builtin")) + selected_bios = builtin_bios; + else if (!strcmp(var.value, "official")) + selected_bios = official_bios; + } + + var.key = "gpsp_boot_mode"; + var.value = 0; + + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + { + if (!strcmp(var.value, "game")) + selected_boot_mode = boot_game; + else if (!strcmp(var.value, "bios")) + selected_boot_mode = boot_bios; + } + } + var.key = "gpsp_frameskip"; var.value = 0; frameskip_type_prev = current_frameskip_type; @@ -795,30 +844,41 @@ bool retro_load_game(const struct retro_game_info* info) extract_directory(main_path, info->path, sizeof(main_path)); - if (environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir) && dir) - strcpy(filename_bios, dir); - else - strcpy(filename_bios, main_path); - - strcat(filename_bios, "/gba_bios.bin"); - - if (environ_cb(RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY, &dir) && dir) strcpy(save_path, dir); else strcpy(save_path, main_path); - if (load_bios(filename_bios) != 0) + if (environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir) && dir) + strcpy(filename_bios, dir); + else + strcpy(filename_bios, main_path); + + bool bios_loaded = false; + printf("USE %d\n", (int)selected_bios); + if (selected_bios == auto_detect || selected_bios == official_bios) { - error_msg("Could not load BIOS image file."); - return false; + bios_loaded = true; + strcat(filename_bios, "/gba_bios.bin"); + + if (load_bios(filename_bios) != 0) + { + if (selected_bios == official_bios) + show_warning_message("Could not load BIOS image file, using built-in BIOS", 2500); + bios_loaded = false; + } + + if (bios_loaded && bios_rom[0] != 0x18) + { + if (selected_bios == official_bios) + show_warning_message("BIOS image seems incorrect, using built-in BIOS", 2500); + bios_loaded = false; + } } - if (bios_rom[0] != 0x18) - { - info_msg("You have an incorrect BIOS image."); - info_msg("While many games will work fine, some will not."); - info_msg("It is strongly recommended that you obtain the correct BIOS file."); + if (!bios_loaded) { + /* Load the built-in BIOS */ + memcpy(bios_rom, open_gba_bios_rom, sizeof(bios_rom)); } memset(gamepak_backup, -1, sizeof(gamepak_backup)); @@ -921,8 +981,8 @@ void retro_run(void) input_poll_cb(); - /* Check whether current frame should - * be skipped */ + /* Check whether current frame should + * be skipped */ skip_next_frame = 0; if (current_frameskip_type != no_frameskip) diff --git a/libretro_core_options.h b/libretro_core_options.h index b5b138d..d2b95fa 100644 --- a/libretro_core_options.h +++ b/libretro_core_options.h @@ -53,6 +53,29 @@ extern "C" { * frontend language definition */ struct retro_core_option_definition option_defs_us[] = { + { + "gpsp_bios", + "BIOS", + "Choose the BIOS image to use. The official BIOS must be provided by the user. Using a non-official (or builtin) BIOS might result in incompatibility problems with some games. Best results are to be achieved with the official Nintendo BIOS.", + { + { "auto", "Auto select" }, + { "builtin", "Builtin BIOS" }, + { "official", "Original BIOS" }, + { NULL, NULL }, + }, + "auto" + }, + { + "gpsp_boot_mode", + "Boot mode", + "Choose whether to boot the BIOS before the game or not. There's not much difference in either modes.", + { + { "game", "Boot to game" }, + { "bios", "Boot to BIOS" }, + { NULL, NULL }, + }, + "game" + }, { "gpsp_frameskip", "Frameskip", diff --git a/main.h b/main.h index dbb839f..bdb29e3 100644 --- a/main.h +++ b/main.h @@ -64,6 +64,19 @@ typedef enum fixed_interval_frameskip } frameskip_type; +typedef enum +{ + auto_detect = 0, + builtin_bios, + official_bios +} bios_type; + +typedef enum +{ + boot_game = 0, + boot_bios +} boot_mode; + extern u32 cpu_ticks; extern u32 execute_cycles; extern u32 global_cycles_per_instruction; @@ -98,6 +111,7 @@ u32 file_length(FILE *fp); extern u32 num_skipped_frames; extern int dynarec_enable; +extern boot_mode selected_boot_mode; void change_ext(const char *src, char *buffer, const char *extension);