UI adjustments, nub support
[libpicofe.git] / 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 #include <pspdisplay.h>
7 #include <psputils.h>
8 #include <pspgu.h>
9 #include <pspaudio.h>
10
11 #include "psp.h"
12 #include "menu.h"
13 #include "emu.h"
14 #include "../common/emu.h"
15 #include "../common/lprintf.h"
16 #include "../../Pico/PicoInt.h"
17
18 #define OSD_FPS_X 424
19
20 // additional pspaudio imports, credits to crazyc
21 int sceAudio_38553111(unsigned short samples, unsigned short freq, char unknown);  // play with conversion?
22 int sceAudio_5C37C0AE(void);                            // end play?
23 int sceAudio_E0727056(int volume, void *buffer);        // blocking output
24 int sceAudioOutput2GetRestSample();
25
26
27 char romFileName[PATH_MAX];
28 unsigned char *PicoDraw2FB = (unsigned char *)VRAM_CACHED_STUFF + 8; // +8 to be able to skip border with 1 quadword..
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 sound_init(void);
37 static void sound_deinit(void);
38 static void blit2(const char *fps, const char *notice, int lagging_behind);
39 static void clearArea(int full);
40
41 void emu_noticeMsgUpdated(void)
42 {
43         noticeMsgTime = sceKernelGetSystemTimeLow();
44 }
45
46 void emu_getMainDir(char *dst, int len)
47 {
48         if (len > 0) *dst = 0;
49 }
50
51 static void osd_text(int x, const char *text, int is_active)
52 {
53         unsigned short *screen = is_active ? psp_video_get_active_fb() : psp_screen;
54         int len = strlen(text) * 8 / 2;
55         int *p, h;
56         void *tmp;
57         for (h = 0; h < 8; h++) {
58                 p = (int *) (screen+x+512*(264+h));
59                 p = (int *) ((int)p & ~3); // align
60                 memset32(p, 0, len);
61         }
62         if (is_active) { tmp = psp_screen; psp_screen = screen; } // nasty pointer tricks
63         emu_textOut16(x, 264, text);
64         if (is_active) psp_screen = tmp;
65 }
66
67 void emu_msg_cb(const char *msg)
68 {
69         osd_text(4, msg, 1);
70         noticeMsgTime = sceKernelGetSystemTimeLow() - 2000000;
71
72         /* assumption: emu_msg_cb gets called only when something slow is about to happen */
73         reset_timing = 1;
74 }
75
76 static void emu_msg_tray_open(void)
77 {
78         strcpy(noticeMsg, "CD tray opened");
79         noticeMsgTime = sceKernelGetSystemTimeLow();
80 }
81
82
83 void emu_Init(void)
84 {
85         // make dirs for saves, cfgs, etc.
86         mkdir("mds", 0777);
87         mkdir("srm", 0777);
88         mkdir("brm", 0777);
89         mkdir("cfg", 0777);
90
91         sound_init();
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         sound_deinit();
123 }
124
125 void emu_setDefaultConfig(void)
126 {
127         memset(&currentConfig, 0, sizeof(currentConfig));
128         currentConfig.lastRomFile[0] = 0;
129         currentConfig.EmuOpt  = 0x1f | 0x680; // | confirm_save, cd_leds, 16bit rend
130         currentConfig.PicoOpt = 0x0f | 0xc00; // | cd_pcm, cd_cdda
131         currentConfig.PsndRate = 22050;
132         currentConfig.PicoRegion = 0; // auto
133         currentConfig.PicoAutoRgnOrder = 0x184; // US, EU, JP
134         currentConfig.Frameskip = -1; // auto
135         currentConfig.volume = 50;
136         currentConfig.CPUclock = 333;
137         currentConfig.KeyBinds[ 4] = 1<<0; // SACB RLDU
138         currentConfig.KeyBinds[ 6] = 1<<1;
139         currentConfig.KeyBinds[ 7] = 1<<2;
140         currentConfig.KeyBinds[ 5] = 1<<3;
141         currentConfig.KeyBinds[14] = 1<<4;
142         currentConfig.KeyBinds[13] = 1<<5;
143         currentConfig.KeyBinds[15] = 1<<6;
144         currentConfig.KeyBinds[ 3] = 1<<7;
145         currentConfig.KeyBinds[12] = 1<<26; // switch rnd
146         currentConfig.KeyBinds[ 8] = 1<<27; // save state
147         currentConfig.KeyBinds[ 9] = 1<<28; // load state
148         currentConfig.KeyBinds[28] = 1<<0; // num "buttons"
149         currentConfig.KeyBinds[30] = 1<<1;
150         currentConfig.KeyBinds[31] = 1<<2;
151         currentConfig.KeyBinds[29] = 1<<3;
152         currentConfig.PicoCDBuffers = 0;
153         currentConfig.scaling = 1;     // bilinear filtering for psp
154         currentConfig.scale = 1.20;    // fullscreen
155         currentConfig.hscale40 = 1.25;
156         currentConfig.hscale32 = 1.56;
157 }
158
159
160 extern void amips_clut(unsigned short *dst, unsigned char *src, unsigned short *pal, int count);
161
162 struct Vertex
163 {
164         short u,v;
165         short x,y,z;
166 };
167
168 static struct Vertex __attribute__((aligned(4))) g_vertices[2];
169 static unsigned short __attribute__((aligned(16))) localPal[0x100];
170 static int dynamic_palette = 0, need_pal_upload = 0, blit_16bit_mode = 0;
171 static int fbimg_offs = 0;
172
173 static void set_scaling_params(void)
174 {
175         int src_width, fbimg_width, fbimg_height, fbimg_xoffs, fbimg_yoffs;
176         g_vertices[0].x = g_vertices[0].y =
177         g_vertices[0].z = g_vertices[1].z = 0;
178
179         fbimg_height = (int)(240.0 * currentConfig.scale + 0.5);
180         if (Pico.video.reg[12] & 1) {
181                 fbimg_width = (int)(320.0 * currentConfig.scale * currentConfig.hscale40 + 0.5);
182                 src_width = 320;
183         } else {
184                 fbimg_width = (int)(256.0 * currentConfig.scale * currentConfig.hscale32 + 0.5);
185                 src_width = 256;
186         }
187
188         if (fbimg_width >= 480) {
189                 g_vertices[0].u = (fbimg_width-480)/2;
190                 g_vertices[1].u = src_width - (fbimg_width-480)/2;
191                 fbimg_width = 480;
192                 fbimg_xoffs = 0;
193         } else {
194                 g_vertices[0].u = 0;
195                 g_vertices[1].u = src_width;
196                 fbimg_xoffs = 240 - fbimg_width/2;
197         }
198
199         if (fbimg_height >= 272) {
200                 g_vertices[0].v = (fbimg_height-272)/2;
201                 g_vertices[1].v = 240 - (fbimg_height-272)/2;
202                 fbimg_height = 272;
203                 fbimg_yoffs = 0;
204         } else {
205                 g_vertices[0].v = 0;
206                 g_vertices[1].v = 240;
207                 fbimg_yoffs = 136 - fbimg_height/2;
208         }
209
210         g_vertices[1].x = fbimg_width;
211         g_vertices[1].y = fbimg_height;
212         if (fbimg_xoffs < 0) fbimg_xoffs = 0;
213         if (fbimg_yoffs < 0) fbimg_yoffs = 0;
214         fbimg_offs = (fbimg_yoffs*512 + fbimg_xoffs) * 2; // dst is always 16bit
215
216         /*
217         lprintf("set_scaling_params:\n");
218         lprintf("offs: %i, %i\n", fbimg_xoffs, fbimg_yoffs);
219         lprintf("xy0, xy1: %i, %i; %i, %i\n", g_vertices[0].x, g_vertices[0].y, g_vertices[1].x, g_vertices[1].y);
220         lprintf("uv0, uv1: %i, %i; %i, %i\n", g_vertices[0].u, g_vertices[0].v, g_vertices[1].u, g_vertices[1].v);
221         */
222 }
223
224 static void do_pal_update(int allow_sh)
225 {
226         unsigned int *spal=(void *)Pico.cram;
227         unsigned int *dpal=(void *)localPal;
228         int i;
229
230         for (i = 0x3f/2; i >= 0; i--)
231                 dpal[i] = ((spal[i]&0x000f000f)<< 1)|((spal[i]&0x00f000f0)<<3)|((spal[i]&0x0f000f00)<<4);
232
233         if (allow_sh && (Pico.video.reg[0xC]&8)) // shadow/hilight?
234         {
235                 // shadowed pixels
236                 for (i = 0x3f/2; i >= 0; i--)
237                         dpal[0x20|i] = dpal[0x60|i] = (dpal[i]>>1)&0x738e738e;
238                 // hilighted pixels
239                 for (i = 0x3f; i >= 0; i--) {
240                         int t=localPal[i]&0xe71c;t+=0x4208;
241                         if (t&0x20) t|=0x1c;
242                         if (t&0x800) t|=0x700;
243                         if (t&0x10000) t|=0xe000;
244                         t&=0xe71c;
245                         localPal[0x80|i]=(unsigned short)t;
246                 }
247                 localPal[0xe0] = 0;
248         }
249         Pico.m.dirtyPal = 0;
250         need_pal_upload = 1;
251 }
252
253 static void do_slowmode_lines(int line_to)
254 {
255         int line = 0, line_len = (Pico.video.reg[12]&1) ? 320 : 256;
256         unsigned short *dst = (unsigned short *)VRAM_STUFF + 512*240/2;
257         unsigned char  *src = (unsigned char  *)VRAM_CACHED_STUFF + 16;
258         if (!(Pico.video.reg[1]&8)) { line = 8; dst += 512*8; src += 512*8; }
259
260         for (; line < line_to; line++, dst+=512, src+=512)
261                 amips_clut(dst, src, localPal, line_len);
262 }
263
264 static void EmuScanPrepare(void)
265 {
266         HighCol = (unsigned char *)VRAM_CACHED_STUFF + 8;
267         if (!(Pico.video.reg[1]&8)) HighCol += 8*512;
268
269         dynamic_palette = 0;
270         if (Pico.m.dirtyPal)
271                 do_pal_update(1);
272 }
273
274 static int EmuScanSlow(unsigned int num, void *sdata)
275 {
276         if (!(Pico.video.reg[1]&8)) num += 8;
277
278         if (Pico.m.dirtyPal) {
279                 if (!dynamic_palette) {
280                         do_slowmode_lines(num);
281                         dynamic_palette = 1;
282                 }
283                 do_pal_update(1);
284         }
285
286         if (dynamic_palette) {
287                 int line_len = (Pico.video.reg[12]&1) ? 320 : 256;
288                 void *dst = (char *)VRAM_STUFF + 512*240 + 512*2*num;
289                 amips_clut(dst, HighCol + 8, localPal, line_len);
290         } else
291                 HighCol = (unsigned char *)VRAM_CACHED_STUFF + (num+1)*512 + 8;
292
293         return 0;
294 }
295
296 static void blitscreen_clut(void)
297 {
298         int offs = fbimg_offs;
299         offs += (psp_screen == VRAM_FB0) ? VRAMOFFS_FB0 : VRAMOFFS_FB1;
300
301         sceGuSync(0,0); // sync with prev
302         sceGuStart(GU_DIRECT, guCmdList);
303         sceGuDrawBuffer(GU_PSM_5650, (void *)offs, 512); // point to back buffer
304
305         if (dynamic_palette)
306         {
307                 if (!blit_16bit_mode) {
308                         sceGuTexMode(GU_PSM_5650, 0, 0, 0);
309                         sceGuTexImage(0,512,512,512,(char *)VRAM_STUFF + 512*240);
310
311                         blit_16bit_mode = 1;
312                 }
313         }
314         else
315         {
316                 if (blit_16bit_mode) {
317                         sceGuClutMode(GU_PSM_5650,0,0xff,0);
318                         sceGuTexMode(GU_PSM_T8,0,0,0); // 8-bit image
319                         sceGuTexImage(0,512,512,512,(char *)VRAM_STUFF + 16);
320                         blit_16bit_mode = 0;
321                 }
322
323                 if ((PicoOpt&0x10) && Pico.m.dirtyPal)
324                         do_pal_update(0);
325
326                 sceKernelDcacheWritebackAll();
327
328                 if (need_pal_upload) {
329                         need_pal_upload = 0;
330                         sceGuClutLoad((256/8), localPal); // upload 32*8 entries (256)
331                 }
332         }
333
334 #if 1
335         if (g_vertices[0].u == 0 && g_vertices[1].u == g_vertices[1].x)
336         {
337                 struct Vertex* vertices;
338                 int x;
339
340                 #define SLICE_WIDTH 32
341                 for (x = 0; x < g_vertices[1].x; x += SLICE_WIDTH)
342                 {
343                         // render sprite
344                         vertices = (struct Vertex*)sceGuGetMemory(2 * sizeof(struct Vertex));
345                         memcpy(vertices, g_vertices, 2 * sizeof(struct Vertex));
346                         vertices[0].u = vertices[0].x = x;
347                         vertices[1].u = vertices[1].x = x + SLICE_WIDTH;
348                         sceGuDrawArray(GU_SPRITES,GU_TEXTURE_16BIT|GU_VERTEX_16BIT|GU_TRANSFORM_2D,2,0,vertices);
349                 }
350                 // lprintf("listlen: %iB\n", sceGuCheckList()); // ~480 only
351         }
352         else
353 #endif
354                 sceGuDrawArray(GU_SPRITES,GU_TEXTURE_16BIT|GU_VERTEX_16BIT|GU_TRANSFORM_2D,2,0,g_vertices);
355
356         sceGuFinish();
357 }
358
359
360 static void cd_leds(void)
361 {
362         unsigned int reg, col_g, col_r, *p;
363
364         reg = Pico_mcd->s68k_regs[0];
365
366         p = (unsigned int *)((short *)psp_screen + 512*2+4+2);
367         col_g = (reg & 2) ? 0x06000600 : 0;
368         col_r = (reg & 1) ? 0x00180018 : 0;
369         *p++ = col_g; *p++ = col_g; p+=2; *p++ = col_r; *p++ = col_r; p += 512/2 - 12/2;
370         *p++ = col_g; *p++ = col_g; p+=2; *p++ = col_r; *p++ = col_r; p += 512/2 - 12/2;
371         *p++ = col_g; *p++ = col_g; p+=2; *p++ = col_r; *p++ = col_r;
372 }
373
374
375 static void dbg_text(void)
376 {
377         int *p, h, len;
378         char text[128];
379
380         sprintf(text, "sl: %i, 16b: %i", g_vertices[0].u == 0 && g_vertices[1].u == g_vertices[1].x, blit_16bit_mode);
381         len = strlen(text) * 8 / 2;
382         for (h = 0; h < 8; h++) {
383                 p = (int *) ((unsigned short *) psp_screen+2+512*(256+h));
384                 p = (int *) ((int)p & ~3); // align
385                 memset32(p, 0, len);
386         }
387         emu_textOut16(2, 256, text);
388 }
389
390
391 /* called after rendering is done, but frame emulation is not finished */
392 void blit1(void)
393 {
394         if (PicoOpt&0x10)
395         {
396                 int i;
397                 unsigned char *pd;
398                 // clear top and bottom trash
399                 for (pd = PicoDraw2FB+8, i = 8; i > 0; i--, pd += 512)
400                         memset32((int *)pd, 0xe0e0e0e0, 320/4);
401                 for (pd = PicoDraw2FB+512*232+8, i = 8; i > 0; i--, pd += 512)
402                         memset32((int *)pd, 0xe0e0e0e0, 320/4);
403         }
404
405         blitscreen_clut();
406 }
407
408
409 static void blit2(const char *fps, const char *notice, int lagging_behind)
410 {
411         int vsync = 0, emu_opt = currentConfig.EmuOpt;
412
413         if (notice || (emu_opt & 2)) {
414                 if (notice)      osd_text(4, notice, 0);
415                 if (emu_opt & 2) osd_text(OSD_FPS_X, fps, 0);
416         }
417
418         dbg_text();
419
420         if ((emu_opt & 0x400) && (PicoMCD & 1))
421                 cd_leds();
422
423         if (currentConfig.EmuOpt & 0x2000) { // want vsync
424                 if (!(currentConfig.EmuOpt & 0x10000) || !lagging_behind) vsync = 1;
425         }
426
427         psp_video_flip(vsync);
428 }
429
430 // clears whole screen or just the notice area (in all buffers)
431 static void clearArea(int full)
432 {
433         if (full) {
434                 memset32(psp_screen, 0, 512*272*2/4);
435                 psp_video_flip(0);
436                 memset32(psp_screen, 0, 512*272*2/4);
437                 memset32(VRAM_CACHED_STUFF, 0xe0e0e0e0, 512*240/4);
438                 memset32((int *)VRAM_CACHED_STUFF+512*240/4, 0, 512*240*2/4);
439         } else {
440                 void *fb = psp_video_get_active_fb();
441                 memset32((int *)((char *)psp_screen + 512*264*2), 0, 512*8*2/4);
442                 memset32((int *)((char *)fb         + 512*264*2), 0, 512*8*2/4);
443         }
444 }
445
446 static void vidResetMode(void)
447 {
448         // setup GU
449         sceGuSync(0,0); // sync with prev
450         sceGuStart(GU_DIRECT, guCmdList);
451
452         sceGuClutMode(GU_PSM_5650,0,0xff,0);
453         sceGuTexMode(GU_PSM_T8,0,0,0); // 8-bit image
454         sceGuTexFunc(GU_TFX_REPLACE,GU_TCC_RGB);
455         if (currentConfig.scaling)
456              sceGuTexFilter(GU_LINEAR, GU_LINEAR);
457         else sceGuTexFilter(GU_NEAREST, GU_NEAREST);
458         sceGuTexScale(1.0f,1.0f);
459         sceGuTexOffset(0.0f,0.0f);
460
461         sceGuTexImage(0,512,512,512,(char *)VRAM_STUFF + 16);
462
463         // slow rend.
464         PicoDrawSetColorFormat(-1);
465         PicoScan = EmuScanSlow;
466
467         localPal[0xe0] = 0;
468         Pico.m.dirtyPal = 1;
469         blit_16bit_mode = dynamic_palette = 0;
470
471         sceGuFinish();
472         set_scaling_params();
473         sceGuSync(0,0);
474 }
475
476
477 /* sound stuff */
478 #define SOUND_BLOCK_SIZE_NTSC (1470*2) // 1024 // 1152
479 #define SOUND_BLOCK_SIZE_PAL  (1764*2)
480 #define SOUND_BLOCK_COUNT    4
481
482 static short __attribute__((aligned(4))) sndBuffer[SOUND_BLOCK_SIZE_PAL*SOUND_BLOCK_COUNT + 44100/50*2];
483 static short *snd_playptr = NULL, *sndBuffer_endptr = NULL;
484 static int samples_made = 0, samples_done = 0, samples_block = 0;
485 static int sound_thread_exit = 0;
486 static SceUID sound_sem = -1;
487
488 static void writeSound(int len);
489
490 static int sound_thread(SceSize args, void *argp)
491 {
492         int ret;
493
494         lprintf("sthr: started, priority %i\n", sceKernelGetThreadCurrentPriority());
495
496         while (!sound_thread_exit)
497         {
498                 if (samples_made - samples_done < samples_block) {
499                         // wait for data (use at least 2 blocks)
500                         //lprintf("sthr: wait... (%i)\n", samples_made - samples_done);
501                         while (samples_made - samples_done <= samples_block*2 && !sound_thread_exit)
502                                 ret = sceKernelWaitSema(sound_sem, 1, 0);
503                         //lprintf("sthr: sceKernelWaitSema: %i\n", ret);
504                         continue;
505                 }
506
507                 //lprintf("sthr: got data: %i\n", samples_made - samples_done);
508
509                 ret = sceAudio_E0727056(PSP_AUDIO_VOLUME_MAX, snd_playptr);
510
511                 samples_done += samples_block;
512                 snd_playptr  += samples_block;
513                 if (snd_playptr >= sndBuffer_endptr)
514                         snd_playptr = sndBuffer;
515                 if (ret)
516                         lprintf("sthr: outf: %i; pos %i/%i\n", ret, samples_done, samples_made);
517
518                 // shouln't happen, but just in case
519                 if (samples_made - samples_done >= samples_block*3) {
520                         //lprintf("block skip (%i)\n", samples_made - samples_done);
521                         samples_done += samples_block; // skip
522                         snd_playptr  += samples_block;
523                 }
524
525         }
526
527         lprintf("sthr: exit\n");
528         sceKernelExitDeleteThread(0);
529         return 0;
530 }
531
532 static void sound_init(void)
533 {
534         SceUID thid;
535
536         sound_sem = sceKernelCreateSema("sndsem", 0, 0, 1, NULL);
537         if (sound_sem < 0) lprintf("sceKernelCreateSema() failed: %i\n", sound_sem);
538
539         samples_made = samples_done = 0;
540         samples_block = SOUND_BLOCK_SIZE_NTSC; // make sure it goes to sema
541         sound_thread_exit = 0;
542         thid = sceKernelCreateThread("sndthread", sound_thread, 0x12, 0x10000, 0, NULL);
543         if (thid >= 0)
544         {
545                 sceKernelStartThread(thid, 0, 0);
546         }
547         else
548                 lprintf("sceKernelCreateThread failed: %i\n", thid);
549 }
550
551 static void sound_prepare(void)
552 {
553         static int PsndRate_old = 0, PicoOpt_old = 0, pal_old = 0;
554         int ret, stereo;
555
556         samples_made = samples_done = 0;
557
558         if (PsndRate != PsndRate_old || (PicoOpt&0x0b) != (PicoOpt_old&0x0b) || Pico.m.pal != pal_old) {
559                 PsndRerate(Pico.m.frame_count ? 1 : 0);
560         }
561         stereo=(PicoOpt&8)>>3;
562
563         samples_block = Pico.m.pal ? SOUND_BLOCK_SIZE_PAL : SOUND_BLOCK_SIZE_NTSC;
564         if (PsndRate <= 22050) samples_block /= 2;
565         sndBuffer_endptr = &sndBuffer[samples_block*SOUND_BLOCK_COUNT];
566
567         lprintf("starting audio: %i, len: %i, stereo: %i, pal: %i, block samples: %i\n",
568                         PsndRate, PsndLen, stereo, Pico.m.pal, samples_block);
569
570         while (sceAudioOutput2GetRestSample() > 0) psp_msleep(100);
571         sceAudio_5C37C0AE();
572         ret = sceAudio_38553111(samples_block/2, PsndRate, 2); // seems to not need that stupid 64byte alignment
573         if (ret < 0) {
574                 lprintf("sceAudio_38553111() failed: %i\n", ret);
575                 sprintf(noticeMsg, "sound init failed (%i), snd disabled", ret);
576                 noticeMsgTime = sceKernelGetSystemTimeLow();
577                 currentConfig.EmuOpt &= ~4;
578         } else {
579                 PicoWriteSound = writeSound;
580                 memset32((int *)(void *)sndBuffer, 0, sizeof(sndBuffer)/4);
581                 snd_playptr = sndBuffer_endptr - samples_block;
582                 samples_made = samples_block; // send 1 empty block first..
583                 PsndOut = sndBuffer;
584                 PsndRate_old = PsndRate;
585                 PicoOpt_old  = PicoOpt;
586                 pal_old = Pico.m.pal;
587         }
588 }
589
590 static void sound_end(void)
591 {
592         samples_made = samples_done = 0;
593         while (sceAudioOutput2GetRestSample() > 0)
594                 psp_msleep(100);
595         sceAudio_5C37C0AE();
596 }
597
598 static void sound_deinit(void)
599 {
600         sound_thread_exit = 1;
601         sceKernelSignalSema(sound_sem, 1);
602         sceKernelDeleteSema(sound_sem);
603         sound_sem = -1;
604 }
605
606 static void writeSound(int len)
607 {
608         int ret;
609         if (PicoOpt&8) len<<=1;
610
611         PsndOut += len;
612         /*if (PsndOut > sndBuffer_endptr) {
613                 memcpy32((int *)(void *)sndBuffer, (int *)endptr, (PsndOut - endptr + 1) / 2);
614                 PsndOut = &sndBuffer[PsndOut - endptr];
615                 lprintf("mov\n");
616         }
617         else*/
618         if (PsndOut >= sndBuffer_endptr)
619                 PsndOut = sndBuffer;
620
621         // signal the snd thread
622         samples_made += len;
623         if (samples_made - samples_done > samples_block*2) {
624                 // lprintf("signal, %i/%i\n", samples_done, samples_made);
625                 ret = sceKernelSignalSema(sound_sem, 1);
626                 // lprintf("signal ret %i\n", ret);
627         }
628 }
629
630
631 static void SkipFrame(void)
632 {
633         PicoSkipFrame=1;
634         PicoFrame();
635         PicoSkipFrame=0;
636 }
637
638 void emu_forcedFrame(void)
639 {
640         int po_old = PicoOpt;
641         int eo_old = currentConfig.EmuOpt;
642
643         PicoOpt &= ~0x0010;
644         PicoOpt |=  0x4080; // soft_scale | acc_sprites
645         currentConfig.EmuOpt |= 0x80;
646
647         vidResetMode();
648         memset32(VRAM_CACHED_STUFF, 0xe0e0e0e0, 512*8/4); // borders
649         memset32((int *)VRAM_CACHED_STUFF + 512*232/4, 0xe0e0e0e0, 512*8/4);
650         memset32((int *)psp_screen + 512*264*2/4, 0, 512*8*2/4);
651
652         PicoDrawSetColorFormat(-1);
653         PicoScan = EmuScanSlow;
654         EmuScanPrepare();
655         PicoFrameDrawOnly();
656         blit1();
657         sceGuSync(0,0);
658
659         PicoOpt = po_old;
660         currentConfig.EmuOpt = eo_old;
661 }
662
663
664 static void RunEvents(unsigned int which)
665 {
666         if (which & 0x1800) // save or load (but not both)
667         {
668                 int do_it = 1;
669
670                 if ( emu_checkSaveFile(state_slot) &&
671                                 (( (which & 0x1000) && (currentConfig.EmuOpt & 0x800)) || // load
672                                  (!(which & 0x1000) && (currentConfig.EmuOpt & 0x200))) ) // save
673                 {
674                         int keys;
675                         sceGuSync(0,0);
676                         blit2("", (which & 0x1000) ? "LOAD STATE? (X=yes, O=no)" : "OVERWRITE SAVE? (X=yes, O=no)", 0);
677                         while( !((keys = psp_pad_read(1)) & (BTN_X|BTN_CIRCLE)) )
678                                 psp_msleep(50);
679                         if (keys & BTN_CIRCLE) do_it = 0;
680                         while(  ((keys = psp_pad_read(1)) & (BTN_X|BTN_CIRCLE)) ) // wait for release
681                                 psp_msleep(50);
682                         clearArea(0);
683                 }
684
685                 if (do_it)
686                 {
687                         osd_text(4, (which & 0x1000) ? "LOADING GAME" : "SAVING GAME", 1);
688                         PicoStateProgressCB = emu_msg_cb;
689                         emu_SaveLoadGame((which & 0x1000) >> 12, 0);
690                         PicoStateProgressCB = NULL;
691                         psp_msleep(0);
692                 }
693
694                 reset_timing = 1;
695         }
696         if (which & 0x0400) // switch renderer
697         {
698                 if (PicoOpt&0x10) { PicoOpt&=~0x10; currentConfig.EmuOpt |=  0x80; }
699                 else              { PicoOpt|= 0x10; currentConfig.EmuOpt &= ~0x80; }
700
701                 vidResetMode();
702
703                 if (PicoOpt&0x10)
704                         strcpy(noticeMsg, "fast renderer");
705                 else if (currentConfig.EmuOpt&0x80)
706                         strcpy(noticeMsg, "accurate renderer");
707
708                 noticeMsgTime = sceKernelGetSystemTimeLow();
709         }
710         if (which & 0x0300)
711         {
712                 if(which&0x0200) {
713                         state_slot -= 1;
714                         if(state_slot < 0) state_slot = 9;
715                 } else {
716                         state_slot += 1;
717                         if(state_slot > 9) state_slot = 0;
718                 }
719                 sprintf(noticeMsg, "SAVE SLOT %i [%s]", state_slot, emu_checkSaveFile(state_slot) ? "USED" : "FREE");
720                 noticeMsgTime = sceKernelGetSystemTimeLow();
721         }
722 }
723
724 static void updateKeys(void)
725 {
726         unsigned int keys, allActions[2] = { 0, 0 }, events;
727         static unsigned int prevEvents = 0;
728         int i;
729
730         keys = psp_pad_read(0);
731         if (keys & PSP_CTRL_HOME)
732                 sceDisplayWaitVblankStart();
733
734         if (keys & BTN_SELECT)
735                 engineState = PGS_Menu;
736
737         keys &= CONFIGURABLE_KEYS;
738
739         for (i = 0; i < 32; i++)
740         {
741                 if (keys & (1 << i))
742                 {
743                         int pl, acts = currentConfig.KeyBinds[i];
744                         if (!acts) continue;
745                         pl = (acts >> 16) & 1;
746                         if (combo_keys & (1 << i))
747                         {
748                                 int u = i+1, acts_c = acts & combo_acts;
749                                 // let's try to find the other one
750                                 if (acts_c)
751                                         for (; u < 32; u++)
752                                                 if ( (currentConfig.KeyBinds[u] & acts_c) && (keys & (1 << u)) ) {
753                                                         allActions[pl] |= acts_c;
754                                                         keys &= ~((1 << i) | (1 << u));
755                                                         break;
756                                                 }
757                                 // add non-combo actions if combo ones were not found
758                                 if (!acts_c || u == 32)
759                                         allActions[pl] |= acts & ~combo_acts;
760                         } else {
761                                 allActions[pl] |= acts;
762                         }
763                 }
764         }
765
766         PicoPad[0] = (unsigned short) allActions[0];
767         PicoPad[1] = (unsigned short) allActions[1];
768
769         events = (allActions[0] | allActions[1]) >> 16;
770
771         // volume is treated in special way and triggered every frame
772         if ((events & 0x6000) && PsndOut != NULL)
773         {
774                 int vol = currentConfig.volume;
775                 if (events & 0x2000) {
776                         if (vol < 100) vol++;
777                 } else {
778                         if (vol >   0) vol--;
779                 }
780                 // FrameworkAudio_SetVolume(vol, vol); // TODO
781                 sprintf(noticeMsg, "VOL: %02i ", vol);
782                 noticeMsgTime = sceKernelGetSystemTimeLow();
783                 currentConfig.volume = vol;
784         }
785
786         events &= ~prevEvents;
787         if (events) RunEvents(events);
788         if (movie_data) emu_updateMovie();
789
790         prevEvents = (allActions[0] | allActions[1]) >> 16;
791 }
792
793 static void find_combos(void)
794 {
795         int act, u;
796
797         // find out which keys and actions are combos
798         combo_keys = combo_acts = 0;
799         for (act = 0; act < 32; act++)
800         {
801                 int keyc = 0, keyc2 = 0;
802                 if (act == 16 || act == 17) continue; // player2 flag
803                 if (act > 17)
804                 {
805                         for (u = 0; u < 28; u++) // 28 because nub can't produce combos
806                                 if (currentConfig.KeyBinds[u] & (1 << act)) keyc++;
807                 }
808                 else
809                 {
810                         for (u = 0; u < 28; u++)
811                                 if ((currentConfig.KeyBinds[u] & 0x30000) == 0 && // pl. 1
812                                         (currentConfig.KeyBinds[u] & (1 << act))) keyc++;
813                         for (u = 0; u < 28; u++)
814                                 if ((currentConfig.KeyBinds[u] & 0x30000) == 1 && // pl. 2
815                                         (currentConfig.KeyBinds[u] & (1 << act))) keyc2++;
816                 }
817                 if (keyc > 1 || keyc2 > 1)
818                 {
819                         // loop again and mark those keys and actions as combo
820                         for (u = 0; u < 28; u++)
821                         {
822                                 if (currentConfig.KeyBinds[u] & (1 << act)) {
823                                         combo_keys |= 1 << u;
824                                         combo_acts |= 1 << act;
825                                 }
826                         }
827                 }
828         }
829 }
830
831
832 static void simpleWait(unsigned int until)
833 {
834         unsigned int tval;
835         int diff;
836
837         tval = sceKernelGetSystemTimeLow();
838         diff = (int)until - (int)tval;
839         if (diff >= 512 && diff < 100*1024)
840                 sceKernelDelayThread(diff);
841 }
842
843 void emu_Loop(void)
844 {
845         char fpsbuff[24]; // fps count c string
846         unsigned int tval, tval_prev = 0, tval_thissec = 0; // timing
847         int frames_done = 0, frames_shown = 0, oldmodes = 0;
848         int target_fps, target_frametime, lim_time, tval_diff, i;
849         char *notice = NULL;
850
851         lprintf("entered emu_Loop()\n");
852
853         fpsbuff[0] = 0;
854
855         if (currentConfig.CPUclock != psp_get_cpu_clock()) {
856                 lprintf("setting cpu clock to %iMHz... ", currentConfig.CPUclock);
857                 i = psp_set_cpu_clock(currentConfig.CPUclock);
858                 lprintf(i ? "failed\n" : "done\n");
859                 currentConfig.CPUclock = psp_get_cpu_clock();
860         }
861
862         // make sure we are in correct mode
863         vidResetMode();
864         clearArea(1);
865         Pico.m.dirtyPal = 1;
866         oldmodes = ((Pico.video.reg[12]&1)<<2) ^ 0xc;
867         find_combos();
868
869         // pal/ntsc might have changed, reset related stuff
870         target_fps = Pico.m.pal ? 50 : 60;
871         target_frametime = Pico.m.pal ? (1000000<<8)/50 : (1000000<<8)/60+1;
872         reset_timing = 1;
873
874         // prepare CD buffer
875         if (PicoMCD & 1) PicoCDBufferInit();
876
877         // prepare sound stuff
878         PsndOut = NULL;
879         if (currentConfig.EmuOpt & 4)
880         {
881                 sound_prepare();
882         }
883
884         sceDisplayWaitVblankStart();
885
886         // loop?
887         while (engineState == PGS_Running)
888         {
889                 int modes;
890
891                 tval = sceKernelGetSystemTimeLow();
892                 if (reset_timing || tval < tval_prev) {
893                         //stdbg("timing reset");
894                         reset_timing = 0;
895                         tval_thissec = tval;
896                         frames_shown = frames_done = 0;
897                 }
898
899                 // show notice message?
900                 if (noticeMsgTime) {
901                         static int noticeMsgSum;
902                         if (tval - noticeMsgTime > 2000000) { // > 2.0 sec
903                                 noticeMsgTime = 0;
904                                 clearArea(0);
905                                 notice = 0;
906                         } else {
907                                 int sum = noticeMsg[0]+noticeMsg[1]+noticeMsg[2];
908                                 if (sum != noticeMsgSum) { clearArea(0); noticeMsgSum = sum; }
909                                 notice = noticeMsg;
910                         }
911                 }
912
913                 // check for mode changes
914                 modes = ((Pico.video.reg[12]&1)<<2)|(Pico.video.reg[1]&8);
915                 if (modes != oldmodes) {
916                         oldmodes = modes;
917                         clearArea(1);
918                         set_scaling_params();
919                 }
920
921                 // second passed?
922                 if (tval - tval_thissec >= 1000000)
923                 {
924                         // missing 1 frame?
925                         if (currentConfig.Frameskip < 0 && frames_done < target_fps) {
926                                 SkipFrame(); frames_done++;
927                         }
928
929                         if (currentConfig.EmuOpt & 2)
930                                 sprintf(fpsbuff, "%02i/%02i  ", frames_shown, frames_done);
931
932                         tval_thissec += 1000000;
933
934                         if (currentConfig.Frameskip < 0) {
935                                 frames_done  -= target_fps; if (frames_done  < 0) frames_done  = 0;
936                                 frames_shown -= target_fps; if (frames_shown < 0) frames_shown = 0;
937                                 if (frames_shown > frames_done) frames_shown = frames_done;
938                         } else {
939                                 frames_done = frames_shown = 0;
940                         }
941                 }
942 #ifdef PFRAMES
943                 sprintf(fpsbuff, "%i", Pico.m.frame_count);
944 #endif
945
946                 tval_prev = tval;
947                 lim_time = (frames_done+1) * target_frametime;
948                 if (currentConfig.Frameskip >= 0) // frameskip enabled
949                 {
950                         for (i = 0; i < currentConfig.Frameskip; i++) {
951                                 updateKeys();
952                                 SkipFrame(); frames_done++;
953                                 if (PsndOut) { // do framelimitting if sound is enabled
954                                         int tval_diff;
955                                         tval = sceKernelGetSystemTimeLow();
956                                         tval_diff = (int)(tval - tval_thissec) << 8;
957                                         if (tval_diff < lim_time) // we are too fast
958                                                 simpleWait(tval + ((lim_time - tval_diff)>>8));
959                                 }
960                                 lim_time += target_frametime;
961                         }
962                 }
963                 else // auto frameskip
964                 {
965                         int tval_diff;
966                         tval = sceKernelGetSystemTimeLow();
967                         tval_diff = (int)(tval - tval_thissec) << 8;
968                         if (tval_diff > lim_time && (frames_done/16 < frames_shown))
969                         {
970                                 // no time left for this frame - skip
971                                 if (tval_diff - lim_time >= (300000<<8)) {
972                                         reset_timing = 1;
973                                         continue;
974                                 }
975                                 updateKeys();
976                                 SkipFrame(); frames_done++;
977                                 continue;
978                         }
979                 }
980
981                 updateKeys();
982
983                 if (!(PicoOpt&0x10))
984                         EmuScanPrepare();
985
986                 PicoFrame();
987
988                 sceGuSync(0,0);
989
990                 // check time
991                 tval = sceKernelGetSystemTimeLow();
992                 tval_diff = (int)(tval - tval_thissec) << 8;
993
994                 blit2(fpsbuff, notice, tval_diff > lim_time);
995
996                 if (currentConfig.Frameskip < 0 && tval_diff - lim_time >= (300000<<8)) // slowdown detection
997                         reset_timing = 1;
998                 else if (PsndOut != NULL || currentConfig.Frameskip < 0)
999                 {
1000                         // sleep if we are still too fast
1001                         if (tval_diff < lim_time)
1002                         {
1003                                 // we are too fast
1004                                 simpleWait(tval + ((lim_time - tval_diff) >> 8));
1005                         }
1006                 }
1007
1008                 frames_done++; frames_shown++;
1009         }
1010
1011
1012         if (PicoMCD & 1) PicoCDBufferFree();
1013
1014         if (PsndOut != NULL) {
1015                 PsndOut = NULL;
1016                 sound_end();
1017         }
1018
1019         // save SRAM
1020         if ((currentConfig.EmuOpt & 1) && SRam.changed) {
1021                 emu_msg_cb("Writing SRAM/BRAM..");
1022                 emu_SaveLoadGame(0, 1);
1023                 SRam.changed = 0;
1024         }
1025
1026         // clear fps counters and stuff
1027         memset32((int *)psp_video_get_active_fb() + 512*264*2/4, 0, 512*8*2/4);
1028 }
1029
1030
1031 void emu_ResetGame(void)
1032 {
1033         PicoReset(0);
1034         reset_timing = 1;
1035 }
1036