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