+void emu_sound_start(void)\r
+{\r
+ PicoIn.sndOut = NULL;\r
+\r
+ if (currentConfig.EmuOpt & EOPT_EN_SOUND)\r
+ {\r
+ int is_stereo = (PicoIn.opt & POPT_EN_STEREO) ? 1 : 0;\r
+\r
+ PsndRerate(Pico.m.frame_count ? 1 : 0);\r
+\r
+ printf("starting audio: %i len: %i stereo: %i, pal: %i\n",\r
+ PicoIn.sndRate, Pico.snd.len, is_stereo, Pico.m.pal);\r
+ sndout_start(PicoIn.sndRate, is_stereo);\r
+ PicoIn.writeSound = snd_write_nonblocking;\r
+ plat_update_volume(0, 0);\r
+ memset(sndBuffer, 0, sizeof(sndBuffer));\r
+ PicoIn.sndOut = sndBuffer;\r
+ }\r
+}\r
+\r
+void emu_sound_stop(void)\r
+{\r
+ sndout_stop();\r
+}\r
+\r
+void emu_sound_wait(void)\r
+{\r
+ sndout_wait();\r
+}\r
+\r
+static void emu_loop_prep(void)\r
+{\r
+ static int pal_old = -1;\r
+ static int filter_old = -1;\r
+\r
+ if (currentConfig.CPUclock != plat_target_cpu_clock_get())\r
+ plat_target_cpu_clock_set(currentConfig.CPUclock);\r
+\r
+ if (Pico.m.pal != pal_old) {\r
+ plat_target_lcdrate_set(Pico.m.pal);\r
+ pal_old = Pico.m.pal;\r
+ }\r
+\r
+ if (currentConfig.filter != filter_old) {\r
+ plat_target_hwfilter_set(currentConfig.filter);\r
+ filter_old = currentConfig.filter;\r
+ }\r
+\r
+ plat_target_gamma_set(currentConfig.gamma, 0);\r
+\r
+ pemu_loop_prep();\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 frames_done, frames_shown; /* actual frames for fps counter */\r
+ int target_frametime_x3;\r
+ unsigned int timestamp_x3 = 0;\r
+ unsigned int timestamp_aim_x3 = 0;\r
+ unsigned int timestamp_fps_x3 = 0;\r
+ char *notice_msg = NULL;\r
+ char fpsbuff[24];\r
+ int fskip_cnt = 0;\r
+\r
+ fpsbuff[0] = 0;\r
+\r
+ PicoLoopPrepare();\r
+\r
+ plat_video_loop_prepare();\r
+ emu_loop_prep();\r
+ pemu_sound_start();\r
+\r
+ /* number of ticks per frame */\r
+ if (Pico.m.pal)\r
+ target_frametime_x3 = 3 * ms_to_ticks(1000) / 50;\r
+ else\r
+ target_frametime_x3 = 3 * ms_to_ticks(1000) / 60;\r
+\r
+ reset_timing = 1;\r
+ frames_done = frames_shown = 0;\r
+\r
+ /* loop with resync every 1 sec. */\r
+ while (engineState == PGS_Running)\r
+ {\r
+ int skip = 0;\r
+ int diff;\r
+\r
+ pprof_start(main);\r
+\r
+ if (reset_timing) {\r
+ reset_timing = 0;\r
+ plat_video_wait_vsync();\r
+ timestamp_aim_x3 = get_ticks() * 3;\r
+ timestamp_fps_x3 = timestamp_aim_x3;\r
+ fskip_cnt = 0;\r
+ }\r
+ else if (currentConfig.EmuOpt & EOPT_NO_FRMLIMIT) {\r
+ timestamp_aim_x3 = get_ticks() * 3;\r
+ }\r
+\r
+ timestamp_x3 = get_ticks() * 3;\r
+\r
+ // show notice_msg message?\r
+ if (notice_msg_time != 0)\r
+ {\r
+ static int noticeMsgSum;\r
+ if (timestamp_x3 - ms_to_ticks(notice_msg_time) * 3\r
+ > ms_to_ticks(STATUS_MSG_TIMEOUT) * 3)\r
+ {\r
+ notice_msg_time = 0;\r
+ plat_status_msg_clear();\r
+ plat_video_flip();\r
+ plat_status_msg_clear(); /* Do it again in case of double buffering */\r
+ notice_msg = NULL;\r
+ }\r
+ else {\r
+ int sum = noticeMsg[0] + noticeMsg[1] + noticeMsg[2];\r
+ if (sum != noticeMsgSum) {\r
+ plat_status_msg_clear();\r
+ noticeMsgSum = sum;\r
+ }\r
+ notice_msg = noticeMsg;\r
+ }\r
+ }\r
+\r
+ // second changed?\r
+ if (timestamp_x3 - timestamp_fps_x3 >= ms_to_ticks(1000) * 3)\r
+ {\r
+#ifdef BENCHMARK\r
+ static int bench = 0, bench_fps = 0, bench_fps_s = 0, bfp = 0, bf[4];\r
+ if (++bench == 10) {\r
+ bench = 0;\r
+ bench_fps_s = bench_fps;\r
+ bf[bfp++ & 3] = bench_fps;\r
+ bench_fps = 0;\r
+ }\r
+ bench_fps += frames_shown;\r
+ sprintf(fpsbuff, "%02i/%02i/%02i", frames_shown, bench_fps_s, (bf[0]+bf[1]+bf[2]+bf[3])>>2);\r
+ printf("%s\n", fpsbuff);\r
+#else\r
+ if (currentConfig.EmuOpt & EOPT_SHOW_FPS)\r
+ snprintf(fpsbuff, 8, "%02i/%02i ", frames_shown, frames_done);\r
+#endif\r
+ frames_shown = frames_done = 0;\r
+ timestamp_fps_x3 += ms_to_ticks(1000) * 3;\r
+ }\r
+#ifdef PFRAMES\r
+ sprintf(fpsbuff, "%i", Pico.m.frame_count);\r
+#endif\r
+\r
+ diff = timestamp_aim_x3 - timestamp_x3;\r
+\r
+ if (currentConfig.Frameskip >= 0) // frameskip enabled (or 0)\r
+ {\r
+ if (fskip_cnt < currentConfig.Frameskip) {\r
+ fskip_cnt++;\r
+ skip = 1;\r
+ }\r
+ else {\r
+ fskip_cnt = 0;\r
+ }\r
+ }\r
+ else if (diff < -target_frametime_x3)\r
+ {\r
+ /* no time left for this frame - skip */\r
+ /* limit auto frameskip to 8 */\r
+ if (frames_done / 8 <= frames_shown)\r
+ skip = 1;\r
+ }\r
+\r
+ // don't go in debt too much\r
+ while (diff < -target_frametime_x3 * 3) {\r
+ timestamp_aim_x3 += target_frametime_x3;\r
+ diff = timestamp_aim_x3 - timestamp_x3;\r
+ }\r
+\r
+ emu_update_input();\r
+ if (skip) {\r
+ int do_audio = diff > -target_frametime_x3 * 2;\r
+ PicoIn.skipFrame = do_audio ? 1 : 2;\r
+ PicoFrame();\r
+ PicoIn.skipFrame = 0;\r
+ }\r
+ else {\r
+ PicoFrame();\r
+ pemu_finalize_frame(fpsbuff, notice_msg);\r
+ frames_shown++;\r
+ }\r
+ frames_done++;\r
+ timestamp_aim_x3 += target_frametime_x3;\r
+\r
+ if (!skip && !flip_after_sync)\r
+ plat_video_flip();\r
+\r
+ /* frame limiter */\r
+ if (!skip && !reset_timing\r
+ && !(currentConfig.EmuOpt & (EOPT_NO_FRMLIMIT|EOPT_EXT_FRMLIMIT)))\r
+ {\r
+ unsigned int timestamp = get_ticks();\r
+ diff = timestamp_aim_x3 - timestamp * 3;\r
+\r
+ // sleep or vsync if we are still too fast\r
+ if (diff > target_frametime_x3 && (currentConfig.EmuOpt & EOPT_VSYNC)) {\r
+ // we are too fast\r
+ plat_video_wait_vsync();\r
+ timestamp = get_ticks();\r
+ diff = timestamp * 3 - timestamp_aim_x3;\r
+ }\r
+ if (diff > target_frametime_x3) {\r
+ // still too fast\r
+ plat_wait_till_us(timestamp + (diff - target_frametime_x3) / 3);\r
+ }\r
+ }\r
+\r
+ if (!skip && flip_after_sync)\r
+ plat_video_flip();\r
+\r
+ pprof_end(main);\r
+ }\r
+\r
+ emu_set_fastforward(0);\r
+\r
+ // save SRAM\r
+ if ((currentConfig.EmuOpt & EOPT_EN_SRAM) && Pico.sv.changed) {\r
+ plat_status_msg_busy_first("Writing SRAM/BRAM...");\r
+ emu_save_load_game(0, 1);\r
+ Pico.sv.changed = 0;\r
+ }\r
+\r
+ pemu_loop_end();\r
+ emu_sound_stop();\r
+}\r