unify helix mp3 code, some sound adjustments
[picodrive.git] / platform / gizmondo / emu.c
1 #include <windows.h>
2 #include <string.h>
3
4 #include <sys/stat.h>  // mkdir
5 #include <sys/types.h>
6
7 #include "kgsdk/Framework.h"
8 #include "kgsdk/Framework2D.h"
9 #include "kgsdk/FrameworkAudio.h"
10 #include "../common/emu.h"
11 #include "../common/lprintf.h"
12 #include "../common/arm_utils.h"
13 #include "../common/config.h"
14 #include "emu.h"
15 #include "menu.h"
16 #include "giz.h"
17 #include "asm_utils.h"
18
19 #include <pico/pico_int.h>
20
21 #ifdef BENCHMARK
22 #define OSD_FPS_X 220
23 #else
24 #define OSD_FPS_X 260
25 #endif
26
27 // main 300K gfx-related buffer. Used by menu and renderers.
28 unsigned char gfx_buffer[321*240*2*2];
29 unsigned char *PicoDraw2FB = gfx_buffer;  // temporary buffer for alt renderer ( (8+320)*(8+240+8) )
30
31 static short *snd_cbuff = NULL;
32 static int snd_cbuf_samples = 0, snd_all_samples = 0;
33
34
35 static void blit(const char *fps, const char *notice);
36 static void clearArea(int full);
37
38 int plat_get_root_dir(char *dst, int len)
39 {
40         if (len > 0) *dst = 0;
41
42         return 0;
43 }
44
45 static void emu_msg_cb(const char *msg)
46 {
47         if (giz_screen != NULL) fb_unlock();
48         giz_screen = fb_lock(1);
49
50         memset32((int *)((char *)giz_screen + 321*232*2), 0, 321*8*2/4);
51         emu_text_out16(4, 232, msg);
52         noticeMsgTime = GetTickCount() - 2000;
53
54         /* assumption: emu_msg_cb gets called only when something slow is about to happen */
55         reset_timing = 1;
56
57         fb_unlock();
58         giz_screen = fb_lock((currentConfig.EmuOpt&0x8000) ? 0 : 1);
59 }
60
61 void emu_stateCb(const char *str)
62 {
63         if (giz_screen != NULL) fb_unlock();
64         giz_screen = fb_lock(1);
65
66         clearArea(0);
67         blit("", str);
68
69         fb_unlock();
70         giz_screen = NULL;
71
72         Sleep(0); /* yield the CPU, the system may need it */
73 }
74
75 void pemu_prep_defconfig(void)
76 {
77         memset(&defaultConfig, 0, sizeof(defaultConfig));
78         defaultConfig.EmuOpt    = 0x1d | 0x680; // | confirm_save, cd_leds, 16bit rend
79         defaultConfig.s_PicoOpt = 0x0f | POPT_EN_MCD_PCM|POPT_EN_MCD_CDDA|POPT_EN_SVP_DRC|POPT_ACC_SPRITES;
80         defaultConfig.s_PsndRate = 22050;
81         defaultConfig.s_PicoRegion = 0; // auto
82         defaultConfig.s_PicoAutoRgnOrder = 0x184; // US, EU, JP
83         defaultConfig.s_PicoCDBuffers = 0;
84         defaultConfig.Frameskip = -1; // auto
85         defaultConfig.volume = 50;
86         defaultConfig.KeyBinds[ 2] = 1<<0; // SACB RLDU
87         defaultConfig.KeyBinds[ 3] = 1<<1;
88         defaultConfig.KeyBinds[ 0] = 1<<2;
89         defaultConfig.KeyBinds[ 1] = 1<<3;
90         defaultConfig.KeyBinds[ 5] = 1<<4;
91         defaultConfig.KeyBinds[ 6] = 1<<5;
92         defaultConfig.KeyBinds[ 7] = 1<<6;
93         defaultConfig.KeyBinds[ 4] = 1<<7;
94         defaultConfig.KeyBinds[13] = 1<<26; // switch rend
95         defaultConfig.KeyBinds[ 8] = 1<<27; // save state
96         defaultConfig.KeyBinds[ 9] = 1<<28; // load state
97         defaultConfig.KeyBinds[12] = 1<<29; // vol up
98         defaultConfig.KeyBinds[11] = 1<<30; // vol down
99         defaultConfig.scaling = 0;
100         defaultConfig.turbo_rate = 15;
101 }
102
103
104 static int EmuScanBegin16(unsigned int num)
105 {
106         if (!(Pico.video.reg[1]&8)) num += 8;
107         DrawLineDest = (unsigned short *) giz_screen + 321 * num;
108
109         if ((currentConfig.EmuOpt&0x4000) && (num&1) == 0) // (Pico.m.frame_count&1))
110                 return 1; // skip next line
111
112         return 0;
113 }
114
115 static int EmuScanBegin8(unsigned int num)
116 {
117         // draw like the fast renderer
118         if (!(Pico.video.reg[1]&8)) num += 8;
119         HighCol = gfx_buffer + 328 * num;
120
121         return 0;
122 }
123
124 static void osd_text(int x, int y, const char *text)
125 {
126         int len = strlen(text) * 8 / 2;
127         int *p, h;
128         for (h = 0; h < 8; h++) {
129                 p = (int *) ((unsigned short *) giz_screen+x+321*(y+h));
130                 p = (int *) ((int)p & ~3); // align
131                 memset32(p, 0, len);
132         }
133         emu_text_out16(x, y, text);
134 }
135
136 /*
137 void log1(void *p1, void *p2)
138 {
139         lprintf("%p %p %p\n", p1, p2, DrawLineDest);
140 }
141 */
142
143 static void cd_leds(void)
144 {
145         static int old_reg = 0;
146         unsigned int col_g, col_r, *p;
147
148         if (!((Pico_mcd->s68k_regs[0] ^ old_reg) & 3)) return; // no change
149         old_reg = Pico_mcd->s68k_regs[0];
150
151         p = (unsigned int *)((short *)giz_screen + 321*2+4+2);
152         col_g = (old_reg & 2) ? 0x06000600 : 0;
153         col_r = (old_reg & 1) ? 0xc000c000 : 0;
154         *p++ = col_g; *p++ = col_g; p+=2; *p++ = col_r; *p++ = col_r; p += 321/2 - 12/2 + 1;
155         *p++ = col_g; p+=3; *p++ = col_r; p += 321/2 - 10/2;
156         *p++ = col_g; *p++ = col_g; p+=2; *p++ = col_r; *p++ = col_r;
157 }
158
159
160 static short localPal[0x100];
161
162 static void blit(const char *fps, const char *notice)
163 {
164         int emu_opt = currentConfig.EmuOpt;
165
166         if (PicoOpt&0x10)
167         {
168                 int lines_flags = 224;
169                 // 8bit fast renderer
170                 if (Pico.m.dirtyPal) {
171                         Pico.m.dirtyPal = 0;
172                         vidConvCpyRGB565(localPal, Pico.cram, 0x40);
173                 }
174                 // a hack for VR
175                 if (PicoRead16Hook == PicoSVPRead16)
176                         memset32((int *)(PicoDraw2FB+328*8+328*223), 0xe0e0e0e0, 328);
177                 if (!(Pico.video.reg[12]&1)) lines_flags|=0x10000;
178                 if (currentConfig.EmuOpt&0x4000)
179                         lines_flags|=0x40000; // (Pico.m.frame_count&1)?0x20000:0x40000;
180                 vidCpy8to16((unsigned short *)giz_screen+321*8, PicoDraw2FB+328*8, localPal, lines_flags);
181         }
182         else if (!(emu_opt&0x80))
183         {
184                 int lines_flags;
185                 // 8bit accurate renderer
186                 if (Pico.m.dirtyPal) {
187                         Pico.m.dirtyPal = 0;
188                         vidConvCpyRGB565(localPal, Pico.cram, 0x40);
189                         if (Pico.video.reg[0xC]&8) { // shadow/hilight mode
190                                 //vidConvCpyRGB32sh(localPal+0x40, Pico.cram, 0x40);
191                                 //vidConvCpyRGB32hi(localPal+0x80, Pico.cram, 0x40); // TODO?
192                                 memcpy32((void *)(localPal+0xc0), (void *)(localPal+0x40), 0x40*2/4);
193                                 localPal[0xc0] = 0x0600;
194                                 localPal[0xd0] = 0xc000;
195                                 localPal[0xe0] = 0x0000; // reserved pixels for OSD
196                                 localPal[0xf0] = 0xffff;
197                         }
198                         /* no support
199                         else if (rendstatus & 0x20) { // mid-frame palette changes
200                                 vidConvCpyRGB565(localPal+0x40, HighPal, 0x40);
201                                 vidConvCpyRGB565(localPal+0x80, HighPal+0x40, 0x40);
202                         } */
203                 }
204                 lines_flags = (Pico.video.reg[1]&8) ? 240 : 224;
205                 if (!(Pico.video.reg[12]&1)) lines_flags|=0x10000;
206                 if (currentConfig.EmuOpt&0x4000)
207                         lines_flags|=0x40000; // (Pico.m.frame_count&1)?0x20000:0x40000;
208                 vidCpy8to16((unsigned short *)giz_screen+321*8, PicoDraw2FB+328*8, localPal, lines_flags);
209         }
210
211         if (notice || (emu_opt & 2)) {
212                 int h = 232;
213                 if (notice)      osd_text(4, h, notice);
214                 if (emu_opt & 2) osd_text(OSD_FPS_X, h, fps);
215         }
216
217         if ((emu_opt & 0x400) && (PicoAHW & PAHW_MCD))
218                 cd_leds();
219 }
220
221 // clears whole screen or just the notice area (in all buffers)
222 static void clearArea(int full)
223 {
224         if (giz_screen == NULL)
225                 giz_screen = fb_lock(1);
226         if (full) memset32(giz_screen, 0, 320*240*2/4);
227         else      memset32((int *)((char *)giz_screen + 321*232*2), 0, 321*8*2/4);
228
229         if (currentConfig.EmuOpt&0x8000) {
230                 fb_unlock();
231                 giz_screen = fb_lock(0);
232                 if (full) memset32(giz_screen, 0, 320*240*2/4);
233                 else      memset32((int *)((char *)giz_screen + 321*232*2), 0, 321*8*2/4);
234         }
235 }
236
237 static void vidResetMode(void)
238 {
239         giz_screen = fb_lock(1);
240
241         if (PicoOpt&0x10) {
242         } else if (currentConfig.EmuOpt&0x80) {
243                 PicoDrawSetColorFormat(1);
244                 PicoScanBegin = EmuScanBegin16;
245         } else {
246                 PicoDrawSetColorFormat(-1);
247                 PicoScanBegin = EmuScanBegin8;
248         }
249         if ((PicoOpt&0x10) || !(currentConfig.EmuOpt&0x80)) {
250                 // setup pal for 8-bit modes
251                 localPal[0xc0] = 0x0600;
252                 localPal[0xd0] = 0xc000;
253                 localPal[0xe0] = 0x0000; // reserved pixels for OSD
254                 localPal[0xf0] = 0xffff;
255         }
256         Pico.m.dirtyPal = 1;
257
258         memset32(giz_screen, 0, 321*240*2/4);
259         if (currentConfig.EmuOpt&0x8000) {
260                 fb_unlock();
261                 giz_screen = fb_lock(0);
262                 memset32(giz_screen, 0, 321*240*2/4);
263         }
264         fb_unlock();
265         giz_screen = NULL;
266 }
267
268 /*
269 #include <stdarg.h>
270 static void stdbg(const char *fmt, ...)
271 {
272         static int cnt = 0;
273         va_list vl;
274
275         sprintf(noticeMsg, "%x ", cnt++);
276         va_start(vl, fmt);
277         vsnprintf(noticeMsg+strlen(noticeMsg), sizeof(noticeMsg)-strlen(noticeMsg), fmt, vl);
278         va_end(vl);
279
280         noticeMsgTime = GetTickCount();
281 }
282 */
283
284 static void updateSound(int len)
285 {
286         if (PicoOpt&8) len<<=1;
287
288         snd_all_samples += len;
289         PsndOut += len;
290         if (PsndOut - snd_cbuff >= snd_cbuf_samples)
291         {
292                 //if (PsndOut - snd_cbuff != snd_cbuf_samples)
293                 //      stdbg("snd diff is %i, not %i", PsndOut - snd_cbuff, snd_cbuf_samples);
294                 PsndOut = snd_cbuff;
295         }
296 }
297
298
299 static void SkipFrame(void)
300 {
301         PicoSkipFrame=1;
302         PicoFrame();
303         PicoSkipFrame=0;
304 }
305
306 /* forced frame to front buffer */
307 void pemu_forced_frame(int opts)
308 {
309         int po_old = PicoOpt;
310         int eo_old = currentConfig.EmuOpt;
311
312         PicoOpt &= ~0x10;
313         PicoOpt |= opts|POPT_ACC_SPRITES;
314         currentConfig.EmuOpt |= 0x80;
315
316         if (giz_screen == NULL)
317                 giz_screen = fb_lock(1);
318
319         PicoDrawSetColorFormat(1);
320         PicoScanBegin = EmuScanBegin16;
321         Pico.m.dirtyPal = 1;
322         PicoFrameDrawOnly();
323
324         fb_unlock();
325         giz_screen = NULL;
326
327         PicoOpt = po_old;
328         currentConfig.EmuOpt = eo_old;
329 }
330
331
332 static void RunEvents(unsigned int which)
333 {
334         if (which & 0x1800) // save or load (but not both)
335         {
336                 int do_it = 1;
337
338                 if (PsndOut != NULL)
339                         FrameworkAudio_SetPause(1);
340                 if (giz_screen == NULL)
341                         giz_screen = fb_lock(1);
342                 if ( emu_check_save_file(state_slot) &&
343                                 (( (which & 0x1000) && (currentConfig.EmuOpt & 0x800)) || // load
344                                  (!(which & 0x1000) && (currentConfig.EmuOpt & 0x200))) ) // save
345                 {
346                         int keys;
347                         blit("", (which & 0x1000) ? "LOAD STATE? (PLAY=yes, STOP=no)" : "OVERWRITE SAVE? (PLAY=yes, STOP=no)");
348                         while( !((keys = Framework_PollGetButtons()) & (PBTN_PLAY|PBTN_STOP)) )
349                                 Sleep(50);
350                         if (keys & PBTN_STOP) do_it = 0;
351                         while(  ((keys = Framework_PollGetButtons()) & (PBTN_PLAY|PBTN_STOP)) ) // wait for release
352                                 Sleep(50);
353                         clearArea(0);
354                 }
355
356                 if (do_it)
357                 {
358                         osd_text(4, 232, (which & 0x1000) ? "LOADING GAME" : "SAVING GAME");
359                         PicoStateProgressCB = emu_stateCb;
360                         emu_save_load_game((which & 0x1000) >> 12, 0);
361                         PicoStateProgressCB = NULL;
362                         Sleep(0);
363                 }
364
365                 if (PsndOut != NULL)
366                         FrameworkAudio_SetPause(0);
367                 reset_timing = 1;
368         }
369         if (which & 0x0400) // switch renderer
370         {
371                 if (PicoOpt&0x10) { PicoOpt&=~0x10; currentConfig.EmuOpt |=  0x80; }
372                 else              { PicoOpt|= 0x10; currentConfig.EmuOpt &= ~0x80; }
373
374                 vidResetMode();
375
376                 if (PicoOpt&0x10) {
377                         strcpy(noticeMsg, " 8bit fast renderer");
378                 } else if (currentConfig.EmuOpt&0x80) {
379                         strcpy(noticeMsg, "16bit accurate renderer");
380                 } else {
381                         strcpy(noticeMsg, " 8bit accurate renderer");
382                 }
383
384                 noticeMsgTime = GetTickCount();
385         }
386         if (which & 0x0300)
387         {
388                 if(which&0x0200) {
389                         state_slot -= 1;
390                         if(state_slot < 0) state_slot = 9;
391                 } else {
392                         state_slot += 1;
393                         if(state_slot > 9) state_slot = 0;
394                 }
395                 sprintf(noticeMsg, "SAVE SLOT %i [%s]", state_slot, emu_check_save_file(state_slot) ? "USED" : "FREE");
396                 noticeMsgTime = GetTickCount();
397         }
398 }
399
400 static void updateKeys(void)
401 {
402         unsigned int keys, allActions[2] = { 0, 0 }, events;
403         static unsigned int prevEvents = 0;
404         int i;
405
406         /* FIXME: port to input fw, merge with emu.c:emu_update_input() */
407         keys = Framework_PollGetButtons();
408         if (keys & PBTN_HOME)
409                 engineState = PGS_Menu;
410
411         keys &= CONFIGURABLE_KEYS;
412
413         PicoPad[0] = allActions[0] & 0xfff;
414         PicoPad[1] = allActions[1] & 0xfff;
415
416         if (allActions[0] & 0x7000) emu_DoTurbo(&PicoPad[0], allActions[0]);
417         if (allActions[1] & 0x7000) emu_DoTurbo(&PicoPad[1], allActions[1]);
418
419         events = (allActions[0] | allActions[1]) >> 16;
420
421         // volume is treated in special way and triggered every frame
422         if ((events & 0x6000) && PsndOut != NULL)
423         {
424                 int vol = currentConfig.volume;
425                 if (events & 0x2000) {
426                         if (vol < 100) vol++;
427                 } else {
428                         if (vol >   0) vol--;
429                 }
430                 FrameworkAudio_SetVolume(vol, vol);
431                 sprintf(noticeMsg, "VOL: %02i ", vol);
432                 noticeMsgTime = GetTickCount();
433                 currentConfig.volume = vol;
434         }
435
436         events &= ~prevEvents;
437         if (events) RunEvents(events);
438         if (movie_data) emu_updateMovie();
439
440         prevEvents = (allActions[0] | allActions[1]) >> 16;
441 }
442
443 void plat_debug_cat(char *str)
444 {
445 }
446
447 static void simpleWait(DWORD until)
448 {
449         DWORD tval;
450         int diff;
451
452         tval = GetTickCount();
453         diff = (int)until - (int)tval;
454         if (diff >= 2)
455                 Sleep(diff - 1);
456
457         while ((tval = GetTickCount()) < until && until - tval < 512) // some simple overflow detection
458                 spend_cycles(1024*2);
459 }
460
461 void pemu_loop(void)
462 {
463         static int PsndRate_old = 0, PicoOpt_old = 0, pal_old = 0;
464         char fpsbuff[24]; // fps count c string
465         DWORD tval, tval_prev = 0, tval_thissec = 0; // timing
466         int frames_done = 0, frames_shown = 0, oldmodes = 0, sec_ms = 1000;
467         int target_fps, target_frametime, lim_time, tval_diff, i;
468         char *notice = NULL;
469
470         lprintf("entered emu_Loop()\n");
471
472         fpsbuff[0] = 0;
473
474         // make sure we are in correct mode
475         vidResetMode();
476         if (currentConfig.scaling) PicoOpt|=0x4000;
477         else PicoOpt&=~0x4000;
478         Pico.m.dirtyPal = 1;
479         oldmodes = ((Pico.video.reg[12]&1)<<2) ^ 0xc;
480
481         // pal/ntsc might have changed, reset related stuff
482         target_fps = Pico.m.pal ? 50 : 60;
483         target_frametime = Pico.m.pal ? (1000<<8)/50 : (1000<<8)/60+1;
484         reset_timing = 1;
485
486         // prepare CD buffer
487         if (PicoAHW & PAHW_MCD) PicoCDBufferInit();
488
489         // prepare sound stuff
490         PsndOut = NULL;
491         if (currentConfig.EmuOpt & 4)
492         {
493                 int ret, snd_excess_add, stereo;
494                 if (PsndRate != PsndRate_old || (PicoOpt&0x0b) != (PicoOpt_old&0x0b) || Pico.m.pal != pal_old) {
495                         PsndRerate(Pico.m.frame_count ? 1 : 0);
496                 }
497                 stereo=(PicoOpt&8)>>3;
498                 snd_excess_add = ((PsndRate - PsndLen*target_fps)<<16) / target_fps;
499                 snd_cbuf_samples = (PsndRate<<stereo) * 16 / target_fps;
500                 lprintf("starting audio: %i len: %i (ex: %04x) stereo: %i, pal: %i\n",
501                         PsndRate, PsndLen, snd_excess_add, stereo, Pico.m.pal);
502                 ret = FrameworkAudio_Init(PsndRate, snd_cbuf_samples, stereo);
503                 if (ret != 0) {
504                         lprintf("FrameworkAudio_Init() failed: %i\n", ret);
505                         sprintf(noticeMsg, "sound init failed (%i), snd disabled", ret);
506                         noticeMsgTime = GetTickCount();
507                         currentConfig.EmuOpt &= ~4;
508                 } else {
509                         FrameworkAudio_SetVolume(currentConfig.volume, currentConfig.volume);
510                         PicoWriteSound = updateSound;
511                         snd_cbuff = FrameworkAudio_56448Buffer();
512                         PsndOut = snd_cbuff + snd_cbuf_samples / 2; // start writing at the middle
513                         snd_all_samples = 0;
514                         PsndRate_old = PsndRate;
515                         PicoOpt_old  = PicoOpt;
516                         pal_old = Pico.m.pal;
517                 }
518         }
519
520         // loop?
521         while (engineState == PGS_Running)
522         {
523                 int modes;
524
525                 tval = GetTickCount();
526                 if (reset_timing || tval < tval_prev) {
527                         //stdbg("timing reset");
528                         reset_timing = 0;
529                         tval_thissec = tval;
530                         frames_shown = frames_done = 0;
531                 }
532
533                 // show notice message?
534                 if (noticeMsgTime) {
535                         static int noticeMsgSum;
536                         if (tval - noticeMsgTime > 2000) { // > 2.0 sec
537                                 noticeMsgTime = 0;
538                                 clearArea(0);
539                                 notice = 0;
540                         } else {
541                                 int sum = noticeMsg[0]+noticeMsg[1]+noticeMsg[2];
542                                 if (sum != noticeMsgSum) { clearArea(0); noticeMsgSum = sum; }
543                                 notice = noticeMsg;
544                         }
545                 }
546
547                 // check for mode changes
548                 modes = ((Pico.video.reg[12]&1)<<2)|(Pico.video.reg[1]&8);
549                 if (modes != oldmodes) {
550                         oldmodes = modes;
551                         clearArea(1);
552                 }
553
554                 // second passed?
555                 if (tval - tval_thissec >= sec_ms)
556                 {
557 #ifdef BENCHMARK
558                         static int bench = 0, bench_fps = 0, bench_fps_s = 0, bfp = 0, bf[4];
559                         if(++bench == 10) {
560                                 bench = 0;
561                                 bench_fps_s = bench_fps;
562                                 bf[bfp++ & 3] = bench_fps;
563                                 bench_fps = 0;
564                         }
565                         bench_fps += frames_shown;
566                         sprintf(fpsbuff, "%02i/%02i/%02i", frames_shown, bench_fps_s, (bf[0]+bf[1]+bf[2]+bf[3])>>2);
567 #else
568                         if(currentConfig.EmuOpt & 2)
569                                 sprintf(fpsbuff, "%02i/%02i", frames_shown, frames_done);
570 #endif
571                         //tval_thissec += 1000;
572                         tval_thissec += sec_ms;
573
574                         if (PsndOut != NULL)
575                         {
576                                 /* some code which tries to sync things to audio clock, the dirty way */
577                                 static int audio_skew_prev = 0;
578                                 int audio_skew, adj, co = 9, shift = 7;
579                                 audio_skew = snd_all_samples*2 - FrameworkAudio_BufferPos();
580                                 if (PsndRate == 22050) co = 10;
581                                 if (PsndRate  > 22050) co = 11;
582                                 if (PicoOpt&8) shift++;
583                                 if (audio_skew < 0) {
584                                         adj = -((-audio_skew) >> shift);
585                                         if (audio_skew > -(6<<co)) adj>>=1;
586                                         if (audio_skew > -(4<<co)) adj>>=1;
587                                         if (audio_skew > -(2<<co)) adj>>=1;
588                                         if (audio_skew > audio_skew_prev) adj>>=2; // going up already
589                                 } else {
590                                         adj = audio_skew >> shift;
591                                         if (audio_skew < (6<<co)) adj>>=1;
592                                         if (audio_skew < (4<<co)) adj>>=1;
593                                         if (audio_skew < (2<<co)) adj>>=1;
594                                         if (audio_skew < audio_skew_prev) adj>>=2;
595                                 }
596                                 audio_skew_prev = audio_skew;
597                                 target_frametime += adj;
598                                 sec_ms = (target_frametime * target_fps) >> 8;
599                                 //stdbg("%i %i %i", audio_skew, adj, sec_ms);
600                                 frames_done = frames_shown = 0;
601                         }
602                         else if (currentConfig.Frameskip < 0) {
603                                 frames_done  -= target_fps; if (frames_done  < 0) frames_done  = 0;
604                                 frames_shown -= target_fps; if (frames_shown < 0) frames_shown = 0;
605                                 if (frames_shown > frames_done) frames_shown = frames_done;
606                         } else {
607                                 frames_done = frames_shown = 0;
608                         }
609                 }
610 #ifdef PFRAMES
611                 sprintf(fpsbuff, "%i", Pico.m.frame_count);
612 #endif
613
614                 tval_prev = tval;
615                 lim_time = (frames_done+1) * target_frametime;
616                 if (currentConfig.Frameskip >= 0) // frameskip enabled
617                 {
618                         for (i = 0; i < currentConfig.Frameskip; i++) {
619                                 updateKeys();
620                                 SkipFrame(); frames_done++;
621                                 if (PsndOut) { // do framelimitting if sound is enabled
622                                         int tval_diff;
623                                         tval = GetTickCount();
624                                         tval_diff = (int)(tval - tval_thissec) << 8;
625                                         if (tval_diff < lim_time) // we are too fast
626                                                 simpleWait(tval + ((lim_time - tval_diff)>>8));
627                                 }
628                                 lim_time += target_frametime;
629                         }
630                 }
631                 else // auto frameskip
632                 {
633                         int tval_diff;
634                         tval = GetTickCount();
635                         tval_diff = (int)(tval - tval_thissec) << 8;
636                         if (tval_diff > lim_time)
637                         {
638                                 // no time left for this frame - skip
639                                 if (tval_diff - lim_time >= (300<<8)) {
640                                         /* something caused a slowdown for us (disk access? cache flush?)
641                                          * try to recover by resetting timing... */
642                                         reset_timing = 1;
643                                         continue;
644                                 }
645                                 updateKeys();
646                                 SkipFrame(); frames_done++;
647                                 continue;
648                         }
649                 }
650
651                 updateKeys();
652
653                 if (currentConfig.EmuOpt&0x80)
654                         /* be sure correct framebuffer is locked */
655                         giz_screen = fb_lock((currentConfig.EmuOpt&0x8000) ? 0 : 1);
656
657                 PicoFrame();
658
659                 if (giz_screen == NULL)
660                         giz_screen = fb_lock((currentConfig.EmuOpt&0x8000) ? 0 : 1);
661
662                 blit(fpsbuff, notice);
663
664                 if (giz_screen != NULL) {
665                         fb_unlock();
666                         giz_screen = NULL;
667                 }
668
669                 if (currentConfig.EmuOpt&0x2000)
670                         Framework2D_WaitVSync();
671
672                 if (currentConfig.EmuOpt&0x8000)
673                         fb_flip();
674
675                 // check time
676                 tval = GetTickCount();
677                 tval_diff = (int)(tval - tval_thissec) << 8;
678
679                 if (currentConfig.Frameskip < 0 && tval_diff - lim_time >= (300<<8)) // slowdown detection
680                         reset_timing = 1;
681                 else if (PsndOut != NULL || currentConfig.Frameskip < 0)
682                 {
683                         // sleep if we are still too fast
684                         if (tval_diff < lim_time)
685                         {
686                                 // we are too fast
687                                 simpleWait(tval + ((lim_time - tval_diff) >> 8));
688                         }
689                 }
690
691                 frames_done++; frames_shown++;
692         }
693
694
695         if (PicoAHW & PAHW_MCD) PicoCDBufferFree();
696
697         if (PsndOut != NULL) {
698                 PsndOut = snd_cbuff = NULL;
699                 FrameworkAudio_Close();
700         }
701
702         // save SRAM
703         if ((currentConfig.EmuOpt & 1) && SRam.changed) {
704                 emu_stateCb("Writing SRAM/BRAM..");
705                 emu_save_load_game(0, 1);
706                 SRam.changed = 0;
707         }
708 }
709