+ ret = PicoState(saveFname, !load);\r
+ if (!ret) {\r
+#ifndef NO_SYNC\r
+ if (!load) sync();\r
+#endif\r
+ emu_status_msg(load ? "STATE LOADED" : "STATE SAVED");\r
+ } else {\r
+ emu_status_msg(load ? "LOAD FAILED" : "SAVE FAILED");\r
+ ret = -1;\r
+ }\r
+\r
+ return ret;\r
+ }\r
+}\r
+\r
+void emu_set_fastforward(int set_on)\r
+{\r
+ static void *set_PsndOut = NULL;\r
+ static int set_Frameskip, set_EmuOpt, is_on = 0;\r
+\r
+ if (set_on && !is_on) {\r
+ set_PsndOut = PsndOut;\r
+ set_Frameskip = currentConfig.Frameskip;\r
+ set_EmuOpt = currentConfig.EmuOpt;\r
+ PsndOut = NULL;\r
+ currentConfig.Frameskip = 8;\r
+ currentConfig.EmuOpt &= ~4;\r
+ currentConfig.EmuOpt |= 0x40000;\r
+ is_on = 1;\r
+ emu_status_msg("FAST FORWARD");\r
+ }\r
+ else if (!set_on && is_on) {\r
+ PsndOut = set_PsndOut;\r
+ currentConfig.Frameskip = set_Frameskip;\r
+ currentConfig.EmuOpt = set_EmuOpt;\r
+ PsndRerate(1);\r
+ is_on = 0;\r
+ }\r
+}\r
+\r
+static void emu_tray_open(void)\r
+{\r
+ engineState = PGS_TrayMenu;\r
+}\r
+\r
+static void emu_tray_close(void)\r
+{\r
+ emu_status_msg("CD tray closed.");\r
+}\r
+\r
+void emu_32x_startup(void)\r
+{\r
+ plat_video_toggle_renderer(0, 0);\r
+ system_announce();\r
+}\r
+\r
+void emu_reset_game(void)\r
+{\r
+ PicoReset();\r
+ reset_timing = 1;\r
+}\r
+\r
+void run_events_pico(unsigned int events)\r
+{\r
+ int lim_x;\r
+\r
+ if (events & PEV_PICO_SWINP) {\r
+ pico_inp_mode++;\r
+ if (pico_inp_mode > 2)\r
+ pico_inp_mode = 0;\r
+ switch (pico_inp_mode) {\r
+ case 2: emu_status_msg("Input: Pen on Pad"); break;\r
+ case 1: emu_status_msg("Input: Pen on Storyware"); break;\r
+ case 0: emu_status_msg("Input: Joystick");\r
+ PicoPicohw.pen_pos[0] = PicoPicohw.pen_pos[1] = 0x8000;\r
+ break;\r
+ }\r
+ }\r
+ if (events & PEV_PICO_PPREV) {\r
+ PicoPicohw.page--;\r
+ if (PicoPicohw.page < 0)\r
+ PicoPicohw.page = 0;\r
+ emu_status_msg("Page %i", PicoPicohw.page);\r
+ }\r
+ if (events & PEV_PICO_PNEXT) {\r
+ PicoPicohw.page++;\r
+ if (PicoPicohw.page > 6)\r
+ PicoPicohw.page = 6;\r
+ emu_status_msg("Page %i", PicoPicohw.page);\r
+ }\r
+\r
+ if (pico_inp_mode == 0)\r
+ return;\r
+\r
+ /* handle other input modes */\r
+ if (PicoPad[0] & 1) pico_pen_y--;\r
+ if (PicoPad[0] & 2) pico_pen_y++;\r
+ if (PicoPad[0] & 4) pico_pen_x--;\r
+ if (PicoPad[0] & 8) pico_pen_x++;\r
+ PicoPad[0] &= ~0x0f; // release UDLR\r
+\r
+ lim_x = (Pico.video.reg[12]&1) ? 319 : 255;\r
+ if (pico_pen_y < 8)\r
+ pico_pen_y = 8;\r
+ if (pico_pen_y > 224 - PICO_PEN_ADJUST_Y)\r
+ pico_pen_y = 224 - PICO_PEN_ADJUST_Y;\r
+ if (pico_pen_x < 0)\r
+ pico_pen_x = 0;\r
+ if (pico_pen_x > lim_x - PICO_PEN_ADJUST_X)\r
+ pico_pen_x = lim_x - PICO_PEN_ADJUST_X;\r
+\r
+ PicoPicohw.pen_pos[0] = pico_pen_x;\r
+ if (!(Pico.video.reg[12] & 1))\r
+ PicoPicohw.pen_pos[0] += pico_pen_x / 4;\r
+ PicoPicohw.pen_pos[0] += 0x3c;\r
+ PicoPicohw.pen_pos[1] = pico_inp_mode == 1 ? (0x2f8 + pico_pen_y) : (0x1fc + pico_pen_y);\r
+}\r
+\r
+static void do_turbo(int *pad, int acts)\r
+{\r
+ static int turbo_pad = 0;\r
+ static unsigned char turbo_cnt[3] = { 0, 0, 0 };\r
+ int inc = currentConfig.turbo_rate * 2;\r
+\r
+ if (acts & 0x1000) {\r
+ turbo_cnt[0] += inc;\r
+ if (turbo_cnt[0] >= 60)\r
+ turbo_pad ^= 0x10, turbo_cnt[0] = 0;\r
+ }\r
+ if (acts & 0x2000) {\r
+ turbo_cnt[1] += inc;\r
+ if (turbo_cnt[1] >= 60)\r
+ turbo_pad ^= 0x20, turbo_cnt[1] = 0;\r
+ }\r
+ if (acts & 0x4000) {\r
+ turbo_cnt[2] += inc;\r
+ if (turbo_cnt[2] >= 60)\r
+ turbo_pad ^= 0x40, turbo_cnt[2] = 0;\r
+ }\r
+ *pad |= turbo_pad & (acts >> 8);\r
+}\r
+\r
+static void run_events_ui(unsigned int which)\r
+{\r
+ if (which & (PEV_STATE_LOAD|PEV_STATE_SAVE))\r
+ {\r
+ int do_it = 1;\r
+ if ( emu_check_save_file(state_slot) &&\r
+ (((which & PEV_STATE_LOAD) && (currentConfig.EmuOpt & EOPT_CONFIRM_LOAD)) ||\r
+ ((which & PEV_STATE_SAVE) && (currentConfig.EmuOpt & EOPT_CONFIRM_SAVE))) )\r
+ {\r
+ const char *nm;\r
+ char tmp[64];\r
+ int keys, len;\r
+\r
+ strcpy(tmp, (which & PEV_STATE_LOAD) ? "LOAD STATE?" : "OVERWRITE SAVE?");\r
+ len = strlen(tmp);\r
+ nm = in_get_key_name(-1, -PBTN_MA3);\r
+ snprintf(tmp + len, sizeof(tmp) - len, "(%s=yes, ", nm);\r
+ len = strlen(tmp);\r
+ nm = in_get_key_name(-1, -PBTN_MBACK);\r
+ snprintf(tmp + len, sizeof(tmp) - len, "%s=no)", nm);\r
+\r
+ plat_status_msg_busy_first(tmp);\r
+\r
+ in_set_blocking(1);\r
+ while (in_menu_wait_any(50) & (PBTN_MA3|PBTN_MBACK))\r
+ ;\r
+ while ( !((keys = in_menu_wait_any(50)) & (PBTN_MA3|PBTN_MBACK)) )\r
+ ;\r
+ if (keys & PBTN_MBACK)\r
+ do_it = 0;\r
+ while (in_menu_wait_any(50) & (PBTN_MA3|PBTN_MBACK))\r
+ ;\r
+ in_set_blocking(0);\r
+ }\r
+ if (do_it) {\r
+ plat_status_msg_busy_first((which & PEV_STATE_LOAD) ? "LOADING STATE" : "SAVING STATE");\r
+ PicoStateProgressCB = plat_status_msg_busy_next;\r
+ emu_save_load_game((which & PEV_STATE_LOAD) ? 1 : 0, 0);\r
+ PicoStateProgressCB = NULL;\r
+ }\r
+ }\r
+ if (which & PEV_SWITCH_RND)\r
+ {\r
+ plat_video_toggle_renderer(1, 0);\r
+ }\r
+ if (which & (PEV_SSLOT_PREV|PEV_SSLOT_NEXT))\r
+ {\r
+ if (which & PEV_SSLOT_PREV) {\r
+ state_slot -= 1;\r
+ if (state_slot < 0)\r
+ state_slot = 9;\r
+ } else {\r
+ state_slot += 1;\r
+ if (state_slot > 9)\r
+ state_slot = 0;\r
+ }\r
+\r
+ emu_status_msg("SAVE SLOT %i [%s]", state_slot,\r
+ emu_check_save_file(state_slot) ? "USED" : "FREE");\r
+ }\r
+ if (which & PEV_MENU)\r
+ engineState = PGS_Menu;\r
+}\r
+\r
+void emu_update_input(void)\r
+{\r
+ static int prev_events = 0;\r
+ int actions[IN_BINDTYPE_COUNT] = { 0, };\r
+ int pl_actions[2];\r
+ int events;\r
+\r
+ in_update(actions);\r
+\r
+ pl_actions[0] = actions[IN_BINDTYPE_PLAYER12];\r
+ pl_actions[1] = actions[IN_BINDTYPE_PLAYER12] >> 16;\r
+\r
+ PicoPad[0] = pl_actions[0] & 0xfff;\r
+ PicoPad[1] = pl_actions[1] & 0xfff;\r
+\r
+ if (pl_actions[0] & 0x7000)\r
+ do_turbo(&PicoPad[0], pl_actions[0]);\r
+ if (pl_actions[1] & 0x7000)\r
+ do_turbo(&PicoPad[1], pl_actions[1]);\r
+\r
+ events = actions[IN_BINDTYPE_EMU] & PEV_MASK;\r
+\r
+ // volume is treated in special way and triggered every frame\r
+ if (events & (PEV_VOL_DOWN|PEV_VOL_UP))\r
+ plat_update_volume(1, events & PEV_VOL_UP);\r
+\r
+ if ((events ^ prev_events) & PEV_FF) {\r
+ emu_set_fastforward(events & PEV_FF);\r
+ plat_update_volume(0, 0);\r
+ reset_timing = 1;\r
+ }\r
+\r
+ events &= ~prev_events;\r
+\r
+ if (PicoAHW == PAHW_PICO)\r
+ run_events_pico(events);\r
+ if (events)\r
+ run_events_ui(events);\r
+ if (movie_data)\r
+ update_movie();\r
+\r
+ prev_events = actions[IN_BINDTYPE_EMU] & PEV_MASK;\r
+}\r
+\r
+static void mkdir_path(char *path_with_reserve, int pos, const char *name)\r
+{\r
+ strcpy(path_with_reserve + pos, name);\r
+ if (plat_is_dir(path_with_reserve))\r
+ return;\r
+ if (mkdir(path_with_reserve, 0777) < 0)\r
+ lprintf("failed to create: %s\n", path_with_reserve);\r
+}\r
+\r
+void emu_init(void)\r
+{\r
+ char path[512];\r
+ int pos;\r
+\r
+#if 0\r
+ // FIXME: handle through menu, etc\r
+ FILE *f;\r
+ f = fopen("32X_M_BIOS.BIN", "rb");\r
+ p32x_bios_m = malloc(2048);\r
+ fread(p32x_bios_m, 1, 2048, f);\r
+ fclose(f);\r
+ f = fopen("32X_S_BIOS.BIN", "rb");\r
+ p32x_bios_s = malloc(1024);\r
+ fread(p32x_bios_s, 1, 1024, f);\r
+ fclose(f);\r
+#endif\r
+\r
+ /* make dirs for saves */\r
+ pos = plat_get_root_dir(path, sizeof(path) - 4);\r
+ mkdir_path(path, pos, "mds");\r
+ mkdir_path(path, pos, "srm");\r
+ mkdir_path(path, pos, "brm");\r
+\r
+ pprof_init();\r
+\r
+ make_config_cfg(path);\r
+ config_readlrom(path);\r
+\r
+ PicoInit();\r
+ PicoMessage = plat_status_msg_busy_next;\r
+ PicoMCDopenTray = emu_tray_open;\r
+ PicoMCDcloseTray = emu_tray_close;\r
+}\r
+\r
+void emu_finish(void)\r
+{\r
+ // save SRAM\r
+ if ((currentConfig.EmuOpt & EOPT_EN_SRAM) && SRam.changed) {\r
+ emu_save_load_game(0, 1);\r
+ SRam.changed = 0;\r
+ }\r
+\r
+ if (!(currentConfig.EmuOpt & EOPT_NO_AUTOSVCFG)) {\r
+ char cfg[512];\r
+ make_config_cfg(cfg);\r
+ config_writelrom(cfg);\r
+#ifndef NO_SYNC\r
+ sync();\r
+#endif\r
+ }\r
+\r
+ pprof_finish();\r
+\r
+ PicoExit();\r
+}\r
+\r
+static void skip_frame(int do_audio)\r
+{\r
+ PicoSkipFrame = do_audio ? 1 : 2;\r
+ PicoFrame();\r
+ PicoSkipFrame = 0;\r
+}\r
+\r
+/* our tick here is 1 us right now */\r
+#define ms_to_ticks(x) (unsigned int)(x * 1000)\r
+#define get_ticks() plat_get_ticks_us()\r
+\r
+void emu_loop(void)\r
+{\r
+ int pframes_done; /* "period" frames, used for sync */\r
+ int frames_done, frames_shown; /* actual frames for fps counter */\r
+ int target_fps, target_frametime;\r
+ unsigned int timestamp_base = 0, timestamp_fps;\r
+ char *notice_msg = NULL;\r
+ char fpsbuff[24];\r
+ int i;\r
+\r
+ fpsbuff[0] = 0;\r
+\r
+ /* make sure we are in correct mode */\r
+ Pico.m.dirtyPal = 1;\r
+ rendstatus_old = -1;\r
+\r
+ /* number of ticks per frame */\r
+ if (Pico.m.pal) {\r
+ target_fps = 50;\r
+ target_frametime = ms_to_ticks(1000) / 50;\r
+ } else {\r
+ target_fps = 60;\r
+ target_frametime = ms_to_ticks(1000) / 60 + 1;\r
+ }\r
+\r
+ // prepare CD buffer\r
+ if (PicoAHW & PAHW_MCD)\r
+ PicoCDBufferInit();\r
+ PicoLoopPrepare();\r
+\r
+ pemu_loop_prep();\r
+\r
+ timestamp_fps = get_ticks();\r
+ reset_timing = 1;\r
+\r
+ frames_done = frames_shown = pframes_done = 0;\r
+\r
+ plat_video_wait_vsync();\r
+\r
+ /* loop with resync every 1 sec. */\r
+ while (engineState == PGS_Running)\r
+ {\r
+ unsigned int timestamp;\r
+ int diff, diff_lim;\r
+\r
+ pprof_start(main);\r
+\r
+ timestamp = get_ticks();\r
+ if (reset_timing) {\r
+ reset_timing = 0;\r
+ timestamp_base = timestamp;\r
+ pframes_done = 0;\r
+ }\r
+\r
+ // show notice_msg message?\r
+ if (notice_msg_time != 0)\r