diff options
Diffstat (limited to 'frontend/main.c')
-rw-r--r-- | frontend/main.c | 442 |
1 files changed, 442 insertions, 0 deletions
diff --git a/frontend/main.c b/frontend/main.c new file mode 100644 index 0000000..f303201 --- /dev/null +++ b/frontend/main.c @@ -0,0 +1,442 @@ +#include <stdio.h> +#include <string.h> +#include "common.h" +#include "main.h" +#include "memmap.h" +#include "frontend/menu.h" +#include "frontend/plat.h" +#include "frontend/libpicofe/plat.h" + +/* Percentage of free space allowed in the audio buffer before + * skipping frames. Lower numbers mean more skipping but smoother + * audio, since the buffer will stay closer to filled. */ +#define FRAMESKIP_UNDERRUN_THRESHOLD 0.5 + +int should_quit = 0; + +u32 idle_loop_target_pc = 0xFFFFFFFF; +u32 iwram_stack_optimize = 1; +u32 translation_gate_target_pc[MAX_TRANSLATION_GATES]; +u32 translation_gate_targets = 0; + +uint16_t *gba_screen_pixels_prev = NULL; +uint16_t *gba_processed_pixels = NULL; + +int use_libretro_save_method = 0; +bios_type selected_bios = auto_detect; +boot_mode selected_boot_mode = boot_game; + +u32 skip_next_frame = 0; + +int dynarec_enable; +int state_slot; +frameskip_style_t frameskip_style; +scaling_mode_t scaling_mode; +int max_frameskip; +int color_correct; +int lcd_blend; +int show_fps; +int limit_frames; + +static float vsyncsps = 0.0; +static float rendersps = 0.0; + +void quit(); + +void gamepak_related_name(char *buf, size_t len, char *new_extension) +{ + char root_dir[512]; + char filename[512]; + char *p; + + plat_get_root_dir(root_dir, len); + p = strrchr(gamepak_filename, PATH_SEPARATOR_CHAR); + + if (p) + p++; + else + p = gamepak_filename; + strncpy(filename, p, sizeof(filename)); + filename[sizeof(filename) - 1] = 0; + p = strrchr(filename, '.'); + if (p) + *p = 0; + + snprintf(buf, len, "%s%s%s", root_dir, filename, new_extension); +} + +void toggle_fast_forward(int force_off) +{ + static frameskip_style_t frameskip_style_was; + static int max_frameskip_was; + static int limit_frames_was; + static int global_process_audio_was; + static int fast_forward; + + if (force_off && !fast_forward) + return; + + fast_forward = !fast_forward; + + if (fast_forward) { + frameskip_style_was = frameskip_style; + max_frameskip_was = max_frameskip; + limit_frames_was = limit_frames; + global_process_audio_was = global_process_audio; + + frameskip_style = FRAMESKIP_MANUAL; + max_frameskip = 5; + limit_frames = 0; + global_process_audio = 0; + } else { + frameskip_style = frameskip_style_was; + max_frameskip = max_frameskip_was; + limit_frames = limit_frames_was; + global_process_audio = global_process_audio_was; + } +} + +void state_file_name(char *buf, size_t len, unsigned state_slot) +{ + char ext[20]; + snprintf(ext, sizeof(ext), ".st%d", state_slot); + + gamepak_related_name(buf, len, ext); +} + +void config_file_name(char *buf, size_t len, int is_game) +{ + char root_dir[MAXPATHLEN]; + + if (is_game) { + gamepak_related_name(buf, len, ".cfg"); + } else { + plat_get_root_dir(root_dir, MAXPATHLEN); + snprintf(buf, len, "%s%s", root_dir, "picogpsp.cfg"); + } +} + +void handle_emu_action(emu_action action) +{ + static frameskip_style_t prev_frameskip_style; + static emu_action prev_action = EACTION_NONE; + if (prev_action != EACTION_NONE && prev_action == action) return; + + switch (action) + { + case EACTION_NONE: + break; + case EACTION_QUIT: + should_quit = 1; + break; + case EACTION_TOGGLE_FPS: + show_fps = !show_fps; + /* Force the hud to clear */ + plat_video_set_msg(" "); + break; + case EACTION_SAVE_STATE: + save_state_file(0); + break; + case EACTION_LOAD_STATE: + load_state_file(0); + break; + case EACTION_TOGGLE_FSKIP: + if (prev_frameskip_style == FRAMESKIP_NONE) + prev_frameskip_style = FRAMESKIP_AUTO; + + if (frameskip_style == FRAMESKIP_NONE) { + frameskip_style = prev_frameskip_style; + } else { + prev_frameskip_style = frameskip_style; + frameskip_style = FRAMESKIP_NONE; + } + break; + case EACTION_TOGGLE_FF: + toggle_fast_forward(0); + break; + case EACTION_MENU: + toggle_fast_forward(1); + update_backup(); + menu_loop(); + break; + default: + break; + } + + prev_action = action; +} + +void synchronize(void) +{ + static uint32_t vsyncs = 0; + static uint32_t renders = 0; + static uint32_t nextsec = 0; + static uint32_t skipped_frames = 0; + unsigned int ticks = 0; + + float capacity = plat_sound_capacity(); + + switch (frameskip_style) + { + case FRAMESKIP_AUTO: + skip_next_frame = 0; + + if (capacity > FRAMESKIP_UNDERRUN_THRESHOLD) { + skip_next_frame = 1; + skipped_frames++; + } + break; + case FRAMESKIP_MANUAL: + skip_next_frame = 1; + skipped_frames++; + break; + default: + skip_next_frame = 0; + break; + } + + if (skipped_frames > max_frameskip) { + skip_next_frame = 0; + skipped_frames = 0; + } + + if (show_fps) { + ticks = plat_get_ticks_ms(); + if (ticks > nextsec) { + vsyncsps = vsyncs; + rendersps = renders; + vsyncs = 0; + renders = 0; + nextsec = ticks + 1000; + } + vsyncs++; + if (!skip_next_frame) renders++; + } +} + +void print_hud() +{ + char msg[HUD_LEN]; + if (show_fps) { + snprintf(msg, HUD_LEN, "FPS: %2.0f (%4.1f)", rendersps, vsyncsps); + plat_video_set_msg(msg); + } +} + +int save_state_file(unsigned state_slot) +{ + char state_filename[MAXPATHLEN]; + void *data; + FILE *f; + int ret = 0; + state_file_name(state_filename, MAXPATHLEN, state_slot); + + f = fopen(state_filename, "wb"); + + if (!f) + return -1; + + data = calloc(1, GBA_STATE_MEM_SIZE); + if (!data) { + ret = -1; + goto fail; + } + + gba_save_state(data); + + if (fwrite(data, 1, GBA_STATE_MEM_SIZE, f) != GBA_STATE_MEM_SIZE) { + ret = -1; + goto fail; + } + +fail: + if (data) + free(data); + if (f) + fclose(f); + + return ret; +} + +int load_state_file(unsigned state_slot) +{ + char state_filename[MAXPATHLEN]; + void *data; + FILE *f; + int ret = 0; + state_file_name(state_filename, MAXPATHLEN, state_slot); + + f = fopen(state_filename, "rb"); + + if (!f) + return -1; + + data = calloc(1, GBA_STATE_MEM_SIZE); + if (!data) { + ret = -1; + goto fail; + } + + + if (fread(data, 1, GBA_STATE_MEM_SIZE, f) != GBA_STATE_MEM_SIZE) { + ret = -1; + goto fail; + } + + gba_load_state(data); + +fail: + if (data) + free(data); + if (f) + fclose(f); + + return ret; +} + +int main(int argc, char *argv[]) +{ + bool bios_loaded = false; + char bios_filename[MAXPATHLEN]; + char filename[MAXPATHLEN]; + char path[MAXPATHLEN]; + + if (argc < 2) { + printf("Usage: picogpsp FILE"); + return 0; + }; + + strncpy(filename, argv[1], MAXPATHLEN); + if (filename[0] != '/') { + getcwd(path, MAXPATHLEN); + if (strlen(path) + strlen(filename) + 1 < MAXPATHLEN) { + strcat(path, "/"); + strcat(path, filename); + strcpy(filename, path); + } else + filename[0] = 0; + } + + if (selected_bios == auto_detect || selected_bios == official_bios) { + bios_loaded = true; + getcwd(bios_filename, MAXPATHLEN); + strncat(bios_filename, "/gba_bios.bin", MAXPATHLEN - strlen(bios_filename)); + + if (load_bios(bios_filename)) { + if (selected_bios == official_bios) + printf("Could not load BIOS image file\n"); + bios_loaded = false; + } + + if (bios_loaded && bios_rom[0] != 0x18) { + if (selected_bios == official_bios) + printf("BIOS image seems incorrect\n"); + bios_loaded = false; + } + } + + if (bios_loaded) { + printf("Using official BIOS\n"); + } else { + /* Load the built-in BIOS */ + memcpy(bios_rom, open_gba_bios_rom, sizeof(bios_rom)); + printf("Using built-in BIOS\n"); + } + + getcwd(main_path, 512); + plat_get_root_dir(save_path, 512); + + if (!gamepak_rom) + init_gamepak_buffer(); + + if(!gba_screen_pixels) + gba_screen_pixels = (uint16_t*)calloc(GBA_SCREEN_PITCH * GBA_SCREEN_HEIGHT, sizeof(uint16_t)); + + if (plat_init()) { + return -1; + }; + + if (load_gamepak(filename) != 0) + { + fprintf(stderr, "Could not load the game file.\n"); + return -1; + } + + init_main(); + init_sound(1); + menu_init(); + +#if defined(HAVE_DYNAREC) + if (dynarec_enable) + { +#ifdef HAVE_MMAP + rom_translation_cache = mmap(NULL, ROM_TRANSLATION_CACHE_SIZE, + PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0); + ram_translation_cache = mmap(NULL, RAM_TRANSLATION_CACHE_SIZE, + PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0); + bios_translation_cache = mmap(NULL, BIOS_TRANSLATION_CACHE_SIZE, + PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0); +#endif + } + else + dynarec_enable = 0; +#else + dynarec_enable = 0; +#endif + + reset_gba(); + + do { + update_input(); + + synchronize(); + +#ifdef HAVE_DYNAREC + if (dynarec_enable) + execute_arm_translate(execute_cycles); + else +#endif + execute_arm(execute_cycles); + + render_audio(); + + print_hud(); + + if (!skip_next_frame) + plat_video_flip(); + } while (!should_quit); + + quit(); + return 0; +} + +void quit() +{ + update_backup(); + + memory_term(); + + if (gba_screen_pixels_prev) { + free(gba_screen_pixels_prev); + gba_screen_pixels_prev = NULL; + } + + if (gba_processed_pixels) { + free(gba_processed_pixels); + gba_processed_pixels = NULL; + } + + free(gba_screen_pixels); + gba_screen_pixels = NULL; + +#if defined(HAVE_MMAP) && defined(HAVE_DYNAREC) + munmap(rom_translation_cache, ROM_TRANSLATION_CACHE_SIZE); + munmap(ram_translation_cache, RAM_TRANSLATION_CACHE_SIZE); + munmap(bios_translation_cache, BIOS_TRANSLATION_CACHE_SIZE); +#endif + + menu_finish(); + plat_finish(); + + exit(0); +} |