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