working audio sync?
[picodrive.git] / platform / gizmondo / emu.c
CommitLineData
e5f426aa 1#include <windows.h>
ea8c405f 2#include <string.h>
e5f426aa 3
2ec14aec 4#include <sys/stat.h> // mkdir
5#include <sys/types.h>
6
7#include "kgsdk/Framework.h"
ea8c405f 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"
e5f426aa 13#include "emu.h"
ea8c405f 14#include "menu.h"
15#include "giz.h"
16#include "asm_utils.h"
e5f426aa 17
ea8c405f 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.
27unsigned char gfx_buffer[321*240*2*2];
e5f426aa 28char romFileName[MAX_PATH];
29int engineState;
e5f426aa 30
ea8c405f 31unsigned char *PicoDraw2FB = gfx_buffer; // temporary buffer for alt renderer ( (8+320)*(8+240+8) )
e5f426aa 32int reset_timing = 0;
ea8c405f 33
fa283c9a 34static int combo_keys = 0, combo_acts = 0; // keys and actions which need button combos
ea8c405f 35static DWORD noticeMsgTime = 0;
fa283c9a 36static short *snd_cbuff = NULL;
37static int snd_cbuf_samples = 0, snd_all_samples = 0;
ea8c405f 38
39
40static void blit(const char *fps, const char *notice);
41static void clearArea(int full);
42
43void emu_noticeMsgUpdated(void)
44{
45 noticeMsgTime = GetTickCount();
46}
47
48void emu_getMainDir(char *dst, int len)
49{
50 if (len > 0) *dst = 0;
51}
52
53static 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
66static void emu_state_cb(const char *str)
67{
9839d126 68 if (giz_screen == NULL)
69 giz_screen = Framework2D_LockBuffer();
70
ea8c405f 71 clearArea(0);
72 blit("", str);
9839d126 73
74 Framework2D_UnlockBuffer();
75 giz_screen = NULL;
ea8c405f 76}
77
78static void emu_msg_tray_open(void)
79{
80 strcpy(noticeMsg, "CD tray opened");
81 noticeMsgTime = GetTickCount();
82}
83
84
85void emu_Init(void)
86{
87 // make dirs for saves, cfgs, etc.
2ec14aec 88 mkdir("mds", 0777);
89 mkdir("srm", 0777);
90 mkdir("brm", 0777);
91 mkdir("cfg", 0777);
ea8c405f 92
93 PicoInit();
94 PicoMessage = emu_msg_cb;
95 PicoMCDopenTray = emu_msg_tray_open;
96 PicoMCDcloseTray = menu_loop_tray;
97}
98
99void 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
124void emu_setDefaultConfig(void)
125{
126 memset(&currentConfig, 0, sizeof(currentConfig));
127 currentConfig.lastRomFile[0] = 0;
9839d126 128 currentConfig.EmuOpt = 0x1f | 0x680; // | confirm_save, cd_leds, 16bit rend
fa283c9a 129 currentConfig.PicoOpt = 0x07 | 0xc00; // | cd_pcm, cd_cdda
ea8c405f 130 currentConfig.PsndRate = 22050;
131 currentConfig.PicoRegion = 0; // auto
132 currentConfig.PicoAutoRgnOrder = 0x184; // US, EU, JP
fa283c9a 133 currentConfig.Frameskip = -1; // auto
ea8c405f 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;
2ec14aec 142 currentConfig.KeyBinds[ 4] = 1<<7;
ea8c405f 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
fa283c9a 147 currentConfig.PicoCDBuffers = 0;
ea8c405f 148 currentConfig.scaling = 0;
149}
150
151
152static 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
160static int EmuScan8(unsigned int num, void *sdata)
161{
162 // draw like the fast renderer
163 if (!(Pico.video.reg[1]&8)) num += 8;
2ec14aec 164 HighCol = gfx_buffer + 328*(num+1);
ea8c405f 165
166 return 0;
167}
168
169static void osd_text(int x, int y, const char *text)
170{
499a0be3 171 int len = strlen(text) * 8 / 2;
172 int *p, h;
ea8c405f 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
499a0be3 176 memset32(p, 0, len);
ea8c405f 177 }
178 emu_textOut16(x, y, text);
179}
180
499a0be3 181/*
182void log1(void *p1, void *p2)
183{
184 lprintf("%p %p %p\n", p1, p2, DrawLineDest);
185}
186*/
ea8c405f 187
9839d126 188static 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
205static short localPal[0x100];
ea8c405f 206
207static void blit(const char *fps, const char *notice)
208{
209 int emu_opt = currentConfig.EmuOpt;
210
9839d126 211 if (PicoOpt&0x10)
212 {
213 int lines_flags = 224;
ea8c405f 214 // 8bit fast renderer
215 if (Pico.m.dirtyPal) {
216 Pico.m.dirtyPal = 0;
217 vidConvCpyRGB565(localPal, Pico.cram, 0x40);
218 }
9839d126 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;
ea8c405f 225 // 8bit accurate renderer
226 if (Pico.m.dirtyPal) {
227 Pico.m.dirtyPal = 0;
228 vidConvCpyRGB565(localPal, Pico.cram, 0x40);
2ec14aec 229 if (Pico.video.reg[0xC]&8) { // shadow/hilight mode
ea8c405f 230 //vidConvCpyRGB32sh(localPal+0x40, Pico.cram, 0x40);
9839d126 231 //vidConvCpyRGB32hi(localPal+0x80, Pico.cram, 0x40); // TODO?
2ec14aec 232 blockcpy(localPal+0xc0, localPal+0x40, 0x40*2);
ea8c405f 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 }
9839d126 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);
ea8c405f 247 }
248
249 if (notice || (emu_opt & 2)) {
250 int h = 232;
251 if (notice) osd_text(4, h, notice);
9839d126 252 if (emu_opt & 2) osd_text(OSD_FPS_X, h, fps);
ea8c405f 253 }
ea8c405f 254
9839d126 255 if ((emu_opt & 0x400) && (PicoMCD & 1))
256 cd_leds();
ea8c405f 257}
258
259// clears whole screen or just the notice area (in all buffers)
260static 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);
499a0be3 265 else memset32((int *)((char *)giz_screen + 321*232*2), 0, 321*8*2/4);
ea8c405f 266}
267
268static void vidResetMode(void)
269{
499a0be3 270 giz_screen = Framework2D_LockBuffer();
271
ea8c405f 272 if (PicoOpt&0x10) {
273 } else if (currentConfig.EmuOpt&0x80) {
274 PicoDrawSetColorFormat(1);
275 PicoScan = EmuScan16;
276 } else {
499a0be3 277 PicoDrawSetColorFormat(-1);
ea8c405f 278 PicoScan = EmuScan8;
279 }
499a0be3 280 if ((PicoOpt&0x10) || !(currentConfig.EmuOpt&0x80)) {
ea8c405f 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;
499a0be3 288
289 memset32(giz_screen, 0, 321*240*2/4);
ea8c405f 290 Framework2D_UnlockBuffer();
291 giz_screen = NULL;
292}
293
294
fa283c9a 295#include <stdarg.h>
296static void stdbg(const char *fmt, ...)
ea8c405f 297{
fa283c9a 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
310static 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
325static void SkipFrame(void)
326{
327 PicoSkipFrame=1;
ea8c405f 328 PicoFrame();
329 PicoSkipFrame=0;
330}
331
332void emu_forcedFrame(void)
333{
fa283c9a 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;
ea8c405f 355}
356
fa283c9a 357
9839d126 358static 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
ea8c405f 411static void updateKeys(void)
412{
2ec14aec 413 unsigned int keys, allActions[2] = { 0, 0 }, events;
414 static unsigned int prevEvents = 0;
415 int i;
416
417 keys = Framework_PollGetButtons();
fa283c9a 418 if (keys & BTN_HOME)
2ec14aec 419 engineState = PGS_Menu;
2ec14aec 420
421 keys &= CONFIGURABLE_KEYS;
422
423 for (i = 0; i < 32; i++)
424 {
fa283c9a 425 if (keys & (1 << i))
426 {
2ec14aec 427 int pl, acts = currentConfig.KeyBinds[i];
428 if (!acts) continue;
429 pl = (acts >> 16) & 1;
fa283c9a 430 if (combo_keys & (1 << i))
431 {
2ec14aec 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;
fa283c9a 444 } else {
2ec14aec 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
fa283c9a 456 if ((events & 0x6000) && PsndOut != NULL)
457 {
2ec14aec 458 int vol = currentConfig.volume;
459 if (events & 0x2000) {
460 if (vol < 100) vol++;
461 } else {
462 if (vol > 0) vol--;
463 }
fa283c9a 464 FrameworkAudio_SetVolume(vol, vol);
2ec14aec 465 sprintf(noticeMsg, "VOL: %02i", vol);
466 noticeMsgTime = GetTickCount();
467 currentConfig.volume = vol;
468 }
469
470 events &= ~prevEvents;
9839d126 471 if (events) RunEvents(events);
2ec14aec 472 if (movie_data) emu_updateMovie();
473
474 prevEvents = (allActions[0] | allActions[1]) >> 16;
ea8c405f 475}
476
fa283c9a 477static 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
ea8c405f 506static void simpleWait(DWORD until)
507{
9839d126 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);
ea8c405f 518}
519
520void emu_Loop(void)
521{
fa283c9a 522 static int PsndRate_old = 0, PicoOpt_old = 0, pal_old = 0;
ea8c405f 523 char fpsbuff[24]; // fps count c string
524 DWORD tval, tval_prev = 0, tval_thissec = 0; // timing
fa283c9a 525 int frames_done = 0, frames_shown = 0, oldmodes = 0, sec_ms = 1000;
ea8c405f 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;
fa283c9a 539 find_combos();
ea8c405f 540
541 // pal/ntsc might have changed, reset related stuff
542 target_fps = Pico.m.pal ? 50 : 60;
fa283c9a 543 target_frametime = Pico.m.pal ? (1000<<8)/50 : (1000<<8)/60+1;
ea8c405f 544 reset_timing = 1;
545
fa283c9a 546 // prepare CD buffer
547 if (PicoMCD & 1) PicoCDBufferInit();
548
ea8c405f 549 // prepare sound stuff
fa283c9a 550 PsndOut = NULL;
551 if (currentConfig.EmuOpt & 4)
552 {
553 int ret, snd_excess_add, stereo=(PicoOpt&8)>>3;
ea8c405f 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;
fa283c9a 558 snd_cbuf_samples = (PsndRate<<stereo) * 16 / target_fps;
ea8c405f 559 lprintf("starting audio: %i len: %i (ex: %04x) stereo: %i, pal: %i\n",
f3d1de29 560 PsndRate, PsndLen, snd_excess_add, stereo, Pico.m.pal);
fa283c9a 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 }
ea8c405f 577 }
578
ea8c405f 579 // loop?
580 while (engineState == PGS_Running)
581 {
582 int modes;
583
584 tval = GetTickCount();
585 if (reset_timing || tval < tval_prev) {
fa283c9a 586 stdbg("timing reset");
ea8c405f 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) {
ea8c405f 609 oldmodes = modes;
610 clearArea(1);
611 }
612
613 // second passed?
fa283c9a 614 if (tval - tval_thissec >= sec_ms)
ea8c405f 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
fa283c9a 630 //tval_thissec += 1000;
631 tval_thissec += sec_ms;
ea8c405f 632
fa283c9a 633 if (PsndOut != NULL)
634 {
f3d1de29 635 /* some code which tries to sync things to audio clock, the dirty way */
636 static int audio_skew_prev = 0;
637 int audio_skew, adj, co = 9, shift = 7;
fa283c9a 638 audio_skew = snd_all_samples*2 - FrameworkAudio_BufferPos();
f3d1de29 639 if (PsndRate == 22050) co = 10;
640 if (PsndRate > 22050) co = 11;
641 if (PicoOpt&8) shift++;
642 if (audio_skew < 0) {
643 adj = -((-audio_skew) >> shift);
644 if (audio_skew > -(6<<co)) adj>>=1;
645 if (audio_skew > -(4<<co)) adj>>=1;
646 if (audio_skew > -(2<<co)) adj>>=1;
647 if (audio_skew > audio_skew_prev) adj>>=2; // going up already
648 } else {
649 adj = audio_skew >> shift;
650 if (audio_skew < (6<<co)) adj>>=1;
651 if (audio_skew < (4<<co)) adj>>=1;
652 if (audio_skew < (2<<co)) adj>>=1;
653 if (audio_skew < audio_skew_prev) adj>>=2;
654 }
655 audio_skew_prev = audio_skew;
fa283c9a 656 target_frametime += adj;
657 sec_ms = (target_frametime * target_fps) >> 8;
658 stdbg("%i %i %i", audio_skew, adj, sec_ms);
ea8c405f 659 frames_done = frames_shown = 0;
fa283c9a 660 }
661 else if (currentConfig.Frameskip < 0) {
ea8c405f 662 frames_done -= target_fps; if (frames_done < 0) frames_done = 0;
663 frames_shown -= target_fps; if (frames_shown < 0) frames_shown = 0;
664 if (frames_shown > frames_done) frames_shown = frames_done;
fa283c9a 665 } else {
666 frames_done = frames_shown = 0;
ea8c405f 667 }
668 }
669#ifdef PFRAMES
670 sprintf(fpsbuff, "%i", Pico.m.frame_count);
671#endif
672
673 tval_prev = tval;
674 lim_time = (frames_done+1) * target_frametime;
675 if (currentConfig.Frameskip >= 0) // frameskip enabled
676 {
677 for (i = 0; i < currentConfig.Frameskip; i++) {
678 updateKeys();
fa283c9a 679 SkipFrame(); frames_done++;
ea8c405f 680 if (PsndOut) { // do framelimitting if sound is enabled
681 int tval_diff;
682 tval = GetTickCount();
683 tval_diff = (int)(tval - tval_thissec) << 8;
684 if (tval_diff < lim_time) // we are too fast
685 simpleWait(tval + ((lim_time - tval_diff)>>8));
686 }
687 lim_time += target_frametime;
688 }
689 }
690 else // auto frameskip
691 {
692 int tval_diff;
693 tval = GetTickCount();
694 tval_diff = (int)(tval - tval_thissec) << 8;
695 if (tval_diff > lim_time)
696 {
697 // no time left for this frame - skip
698 if (tval_diff - lim_time >= (300<<8)) {
699 /* something caused a slowdown for us (disk access? cache flush?)
700 * try to recover by resetting timing... */
701 reset_timing = 1;
702 continue;
703 }
704 updateKeys();
fa283c9a 705 SkipFrame(); frames_done++;
ea8c405f 706 continue;
707 }
708 }
709
710 updateKeys();
711
499a0be3 712 if (giz_screen == NULL && (currentConfig.EmuOpt&0x80))
ea8c405f 713 giz_screen = Framework2D_LockBuffer();
499a0be3 714 if (!(PicoOpt&0x10))
715 PicoScan((unsigned) -1, NULL);
ea8c405f 716
717 PicoFrame();
499a0be3 718
719 if (giz_screen == NULL)
720 giz_screen = Framework2D_LockBuffer();
721
ea8c405f 722 blit(fpsbuff, notice);
723
724 if (giz_screen != NULL) {
725 Framework2D_UnlockBuffer();
726 giz_screen = NULL;
727 }
728
729 // check time
730 tval = GetTickCount();
731 tval_diff = (int)(tval - tval_thissec) << 8;
732
733 if (currentConfig.Frameskip < 0 && tval_diff - lim_time >= (300<<8)) // slowdown detection
734 reset_timing = 1;
735 else if (PsndOut != NULL || currentConfig.Frameskip < 0)
736 {
737 // sleep if we are still too fast
738 if (tval_diff < lim_time)
739 {
740 // we are too fast
741 simpleWait(tval + ((lim_time - tval_diff) >> 8));
742 }
743 }
744
745 frames_done++; frames_shown++;
746 }
747
748
749 if (PicoMCD & 1) PicoCDBufferFree();
750
fa283c9a 751 if (PsndOut != NULL) {
752 PsndOut = snd_cbuff = NULL;
753 FrameworkAudio_Close();
754 }
755
ea8c405f 756 // save SRAM
757 if ((currentConfig.EmuOpt & 1) && SRam.changed) {
758 emu_state_cb("Writing SRAM/BRAM..");
759 emu_SaveLoadGame(0, 1);
760 SRam.changed = 0;
761 }
762}
763
764
765void emu_ResetGame(void)
766{
767 PicoReset(0);
768 reset_timing = 1;
769}
e5f426aa 770