move emu loop to common; redo timing; add pollux timer
[libpicofe.git] / common / emu.c
index 1c7d1b7..5bd34e8 100644 (file)
@@ -5,6 +5,7 @@
 \r
 #include <stdio.h>\r
 #include <stdlib.h>\r
+#include <stdarg.h>\r
 #ifndef NO_SYNC\r
 #include <unistd.h>\r
 #endif\r
@@ -44,6 +45,8 @@ char rom_fname_reload[512] = { 0, };
 char rom_fname_loaded[512] = { 0, };\r
 int rom_loaded = 0;\r
 int reset_timing = 0;\r
+static unsigned int notice_msg_time;   /* when started showing */\r
+static char noticeMsg[40];\r
 \r
 unsigned char *movie_data = NULL;\r
 static int movie_size = 0;\r
@@ -86,6 +89,17 @@ static void get_ext(char *file, char *ext)
        strlwr_(ext);\r
 }\r
 \r
+void emu_status_msg(const char *format, ...)\r
+{\r
+       va_list vl;\r
+\r
+       va_start(vl, format);\r
+       vsnprintf(noticeMsg, sizeof(noticeMsg), format, vl);\r
+       va_end(vl);\r
+\r
+       notice_msg_time = plat_get_ticks_ms();\r
+}\r
+\r
 static const char *biosfiles_us[] = { "us_scd1_9210", "us_scd2_9306", "SegaCDBIOS9303" };\r
 static const char *biosfiles_eu[] = { "eu_mcd1_9210", "eu_mcd2_9306", "eu_mcd2_9303"   };\r
 static const char *biosfiles_jp[] = { "jp_mcd1_9112", "jp_mcd1_9111" };\r
@@ -485,12 +499,12 @@ int emu_reload_rom(char *rom_fname)
                        // TODO: bits 6 & 5\r
                }\r
                movie_data[0x18+30] = 0;\r
-               plat_status_msg("MOVIE: %s", (char *) &movie_data[0x18]);\r
+               emu_status_msg("MOVIE: %s", (char *) &movie_data[0x18]);\r
        }\r
        else\r
        {\r
                PicoOpt &= ~POPT_DIS_VDP_FIFO;\r
-               plat_status_msg(Pico.m.pal ? "PAL SYSTEM / 50 FPS" : "NTSC SYSTEM / 60 FPS");\r
+               emu_status_msg(Pico.m.pal ? "PAL SYSTEM / 50 FPS" : "NTSC SYSTEM / 60 FPS");\r
        }\r
 \r
        // load SRAM for this ROM\r
@@ -702,7 +716,7 @@ void update_movie(void)
        if (offs+3 > movie_size) {\r
                free(movie_data);\r
                movie_data = 0;\r
-               plat_status_msg("END OF MOVIE.");\r
+               emu_status_msg("END OF MOVIE.");\r
                lprintf("END OF MOVIE.\n");\r
        } else {\r
                // MXYZ SACB RLDU\r
@@ -824,7 +838,7 @@ int emu_save_load_game(int load, int sram)
        saveFname = emu_get_save_fname(load, sram, state_slot);\r
        if (saveFname == NULL) {\r
                if (!sram)\r
-                       plat_status_msg(load ? "LOAD FAILED (missing file)" : "SAVE FAILED");\r
+                       emu_status_msg(load ? "LOAD FAILED (missing file)" : "SAVE FAILED");\r
                return -1;\r
        }\r
 \r
@@ -910,10 +924,10 @@ int emu_save_load_game(int load, int sram)
                }\r
                else    ret = -1;\r
                if (!ret)\r
-                       plat_status_msg(load ? "GAME LOADED" : "GAME SAVED");\r
+                       emu_status_msg(load ? "GAME LOADED" : "GAME SAVED");\r
                else\r
                {\r
-                       plat_status_msg(load ? "LOAD FAILED" : "SAVE FAILED");\r
+                       emu_status_msg(load ? "LOAD FAILED" : "SAVE FAILED");\r
                        ret = -1;\r
                }\r
 \r
@@ -935,7 +949,7 @@ void emu_set_fastforward(int set_on)
                currentConfig.EmuOpt &= ~4;\r
                currentConfig.EmuOpt |= 0x40000;\r
                is_on = 1;\r
-               plat_status_msg("FAST FORWARD");\r
+               emu_status_msg("FAST FORWARD");\r
        }\r
        else if (!set_on && is_on) {\r
                PsndOut = set_PsndOut;\r
@@ -948,7 +962,7 @@ void emu_set_fastforward(int set_on)
 \r
 static void emu_msg_tray_open(void)\r
 {\r
-       plat_status_msg("CD tray opened");\r
+       emu_status_msg("CD tray opened");\r
 }\r
 \r
 void emu_reset_game(void)\r
@@ -966,9 +980,9 @@ void run_events_pico(unsigned int events)
                if (pico_inp_mode > 2)\r
                        pico_inp_mode = 0;\r
                switch (pico_inp_mode) {\r
-                       case 2: plat_status_msg("Input: Pen on Pad"); break;\r
-                       case 1: plat_status_msg("Input: Pen on Storyware"); break;\r
-                       case 0: plat_status_msg("Input: Joystick");\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
@@ -977,13 +991,13 @@ void run_events_pico(unsigned int events)
                PicoPicohw.page--;\r
                if (PicoPicohw.page < 0)\r
                        PicoPicohw.page = 0;\r
-               plat_status_msg("Page %i", PicoPicohw.page);\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
-               plat_status_msg("Page %i", PicoPicohw.page);\r
+               emu_status_msg("Page %i", PicoPicohw.page);\r
        }\r
 \r
        if (pico_inp_mode == 0)\r
@@ -1094,7 +1108,7 @@ static void run_events_ui(unsigned int which)
                                state_slot = 0;\r
                }\r
 \r
-               plat_status_msg("SAVE SLOT %i [%s]", state_slot,\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
@@ -1194,3 +1208,205 @@ void emu_finish(void)
        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 oldmodes, 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
+       oldmodes = ((Pico.video.reg[12]&1)<<2) ^ 0xc;\r
+       Pico.m.dirtyPal = 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
+\r
+       if (currentConfig.EmuOpt & EOPT_PSYNC)\r
+               plat_video_wait_vsync();\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
+       /* loop with resync every 1 sec. */\r
+       while (engineState == PGS_Running)\r
+       {\r
+               unsigned int timestamp;\r
+               int diff, diff_lim;\r
+               int modes;\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
+               {\r
+                       static int noticeMsgSum;\r
+                       if (timestamp - ms_to_ticks(notice_msg_time) > ms_to_ticks(2000)) {\r
+                               notice_msg_time = 0;\r
+                               plat_status_msg_clear();\r
+                               notice_msg = NULL;\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
+               // check for mode changes\r
+               modes = ((Pico.video.reg[12]&1)<<2) | (Pico.video.reg[1]&8);\r
+               if (modes != oldmodes) {\r
+                       oldmodes = modes;\r
+                       pemu_video_mode_change(!(modes & 4), (modes & 8));\r
+               }\r
+\r
+               // second changed?\r
+               if (timestamp - timestamp_fps >= ms_to_ticks(1000))\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
+#else\r
+                       if (currentConfig.EmuOpt & EOPT_SHOW_FPS) {\r
+                               sprintf(fpsbuff, "%02i/%02i", frames_shown, frames_done);\r
+                               if (fpsbuff[5] == 0) { fpsbuff[5] = fpsbuff[6] = ' '; fpsbuff[7] = 0; }\r
+                       }\r
+#endif\r
+                       frames_shown = frames_done = 0;\r
+                       timestamp_fps += ms_to_ticks(1000);\r
+               }\r
+#ifdef PFRAMES\r
+               sprintf(fpsbuff, "%i", Pico.m.frame_count);\r
+#endif\r
+\r
+               if (timestamp - timestamp_base >= ms_to_ticks(1000))\r
+               {\r
+                       if (PsndOut == 0 && currentConfig.Frameskip >= 0)\r
+                               pframes_done = 0;\r
+                       else\r
+                               pframes_done -= target_fps;\r
+                       timestamp_base += ms_to_ticks(1000);\r
+               }\r
+\r
+               diff = timestamp - timestamp_base;\r
+               diff_lim = (pframes_done + 1) * target_frametime;\r
+\r
+               if (currentConfig.Frameskip >= 0) // frameskip enabled\r
+               {\r
+                       for (i = 0; i < currentConfig.Frameskip; i++) {\r
+                               emu_update_input();\r
+                               skip_frame(1);\r
+                               pframes_done++; frames_done++;\r
+                               diff_lim += target_frametime;\r
+\r
+                               if (PsndOut && !reset_timing) { // do framelimitting if sound is enabled\r
+                                       timestamp = get_ticks();\r
+                                       diff = timestamp - timestamp_base;\r
+                                       if (diff < diff_lim) // we are too fast\r
+                                               plat_wait_till_us(timestamp_base + diff_lim);\r
+                               }\r
+                       }\r
+               }\r
+               else if (diff > diff_lim)\r
+               {\r
+                       /* no time left for this frame - skip */\r
+                       if (diff - diff_lim >= ms_to_ticks(300)) {\r
+                               /* if too much behind, reset instead */\r
+                               reset_timing = 1;\r
+                               continue;\r
+                       }\r
+                       emu_update_input();\r
+                       skip_frame(diff < diff_lim + target_frametime * 2);\r
+                       pframes_done++; frames_done++;\r
+                       continue;\r
+               }\r
+\r
+               emu_update_input();\r
+               PicoFrame();\r
+\r
+               /* frame limiter */\r
+               if (!reset_timing && (PsndOut != NULL || currentConfig.Frameskip < 0))\r
+               {\r
+                       timestamp = get_ticks();\r
+                       diff = timestamp - timestamp_base;\r
+\r
+                       // sleep or vsync if we are still too fast\r
+                       if (diff < diff_lim)\r
+                       {\r
+                               // we are too fast\r
+                               if (currentConfig.EmuOpt & EOPT_PSYNC) {\r
+                                       if (diff_lim - diff > target_frametime/2)\r
+                                               plat_wait_till_us(timestamp_base + target_frametime/4);\r
+                                       plat_video_wait_vsync();\r
+                               } else\r
+                                       plat_wait_till_us(timestamp_base + diff_lim);\r
+                       }\r
+               }\r
+\r
+               pemu_update_display(fpsbuff, notice_msg);\r
+\r
+               pframes_done++; frames_done++; frames_shown++;\r
+       }\r
+\r
+       emu_set_fastforward(0);\r
+\r
+       if (PicoAHW & PAHW_MCD)\r
+               PicoCDBufferFree();\r
+\r
+       // save SRAM\r
+       if ((currentConfig.EmuOpt & EOPT_EN_SRAM) && SRam.changed) {\r
+               plat_status_msg_busy_first("Writing SRAM/BRAM...");\r
+               emu_save_load_game(0, 1);\r
+               SRam.changed = 0;\r
+       }\r
+\r
+       // do menu background to be sure it's right\r
+       pemu_forced_frame(POPT_EN_SOFTSCALE);\r
+\r
+       pemu_loop_end();\r
+}\r
+\r