psp mp3 implementation
[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 #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    8
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 = 0;
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                         if (ret < 0) 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("sthr: 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) lprintf("snd oflow %i!\n", PsndOut - sndBuffer_endptr);
619         if (PsndOut >= sndBuffer_endptr)
620                 PsndOut = sndBuffer;
621
622         // signal the snd thread
623         samples_made += len;
624         if (samples_made - samples_done > samples_block*2) {
625                 // lprintf("signal, %i/%i\n", samples_done, samples_made);
626                 ret = sceKernelSignalSema(sound_sem, 1);
627                 //if (ret < 0) lprintf("snd signal ret %08x\n", ret);
628         }
629 }
630
631
632 static void SkipFrame(void)
633 {
634         PicoSkipFrame=1;
635         PicoFrame();
636         PicoSkipFrame=0;
637 }
638
639 void emu_forcedFrame(void)
640 {
641         int po_old = PicoOpt;
642         int eo_old = currentConfig.EmuOpt;
643
644         PicoOpt &= ~0x0010;
645         PicoOpt |=  0x4080; // soft_scale | acc_sprites
646         currentConfig.EmuOpt |= 0x80;
647
648         vidResetMode();
649         memset32(VRAM_CACHED_STUFF, 0xe0e0e0e0, 512*8/4); // borders
650         memset32((int *)VRAM_CACHED_STUFF + 512*232/4, 0xe0e0e0e0, 512*8/4);
651         memset32((int *)psp_screen + 512*264*2/4, 0, 512*8*2/4);
652
653         PicoDrawSetColorFormat(-1);
654         PicoScan = EmuScanSlow;
655         EmuScanPrepare();
656         PicoFrameDrawOnly();
657         blit1();
658         sceGuSync(0,0);
659
660         PicoOpt = po_old;
661         currentConfig.EmuOpt = eo_old;
662 }
663
664
665 static void RunEvents(unsigned int which)
666 {
667         if (which & 0x1800) // save or load (but not both)
668         {
669                 int do_it = 1;
670
671                 if ( emu_checkSaveFile(state_slot) &&
672                                 (( (which & 0x1000) && (currentConfig.EmuOpt & 0x800)) || // load
673                                  (!(which & 0x1000) && (currentConfig.EmuOpt & 0x200))) ) // save
674                 {
675                         int keys;
676                         sceGuSync(0,0);
677                         blit2("", (which & 0x1000) ? "LOAD STATE? (X=yes, O=no)" : "OVERWRITE SAVE? (X=yes, O=no)", 0);
678                         while( !((keys = psp_pad_read(1)) & (BTN_X|BTN_CIRCLE)) )
679                                 psp_msleep(50);
680                         if (keys & BTN_CIRCLE) do_it = 0;
681                         while(  ((keys = psp_pad_read(1)) & (BTN_X|BTN_CIRCLE)) ) // wait for release
682                                 psp_msleep(50);
683                         clearArea(0);
684                 }
685
686                 if (do_it)
687                 {
688                         osd_text(4, (which & 0x1000) ? "LOADING GAME" : "SAVING GAME", 1);
689                         PicoStateProgressCB = emu_msg_cb;
690                         emu_SaveLoadGame((which & 0x1000) >> 12, 0);
691                         PicoStateProgressCB = NULL;
692                         psp_msleep(0);
693                 }
694
695                 reset_timing = 1;
696         }
697         if (which & 0x0400) // switch renderer
698         {
699                 if (PicoOpt&0x10) { PicoOpt&=~0x10; currentConfig.EmuOpt |=  0x80; }
700                 else              { PicoOpt|= 0x10; currentConfig.EmuOpt &= ~0x80; }
701
702                 vidResetMode();
703
704                 if (PicoOpt&0x10)
705                         strcpy(noticeMsg, "fast renderer");
706                 else if (currentConfig.EmuOpt&0x80)
707                         strcpy(noticeMsg, "accurate renderer");
708
709                 noticeMsgTime = sceKernelGetSystemTimeLow();
710         }
711         if (which & 0x0300)
712         {
713                 if(which&0x0200) {
714                         state_slot -= 1;
715                         if(state_slot < 0) state_slot = 9;
716                 } else {
717                         state_slot += 1;
718                         if(state_slot > 9) state_slot = 0;
719                 }
720                 sprintf(noticeMsg, "SAVE SLOT %i [%s]", state_slot, emu_checkSaveFile(state_slot) ? "USED" : "FREE");
721                 noticeMsgTime = sceKernelGetSystemTimeLow();
722         }
723 }
724
725 static void updateKeys(void)
726 {
727         unsigned int keys, allActions[2] = { 0, 0 }, events;
728         static unsigned int prevEvents = 0;
729         int i;
730
731         keys = psp_pad_read(0);
732         if (keys & PSP_CTRL_HOME)
733                 sceDisplayWaitVblankStart();
734
735         if (keys & BTN_SELECT)
736                 engineState = PGS_Menu;
737
738         keys &= CONFIGURABLE_KEYS;
739
740         for (i = 0; i < 32; i++)
741         {
742                 if (keys & (1 << i))
743                 {
744                         int pl, acts = currentConfig.KeyBinds[i];
745                         if (!acts) continue;
746                         pl = (acts >> 16) & 1;
747                         if (combo_keys & (1 << i))
748                         {
749                                 int u = i+1, acts_c = acts & combo_acts;
750                                 // let's try to find the other one
751                                 if (acts_c)
752                                         for (; u < 32; u++)
753                                                 if ( (currentConfig.KeyBinds[u] & acts_c) && (keys & (1 << u)) ) {
754                                                         allActions[pl] |= acts_c;
755                                                         keys &= ~((1 << i) | (1 << u));
756                                                         break;
757                                                 }
758                                 // add non-combo actions if combo ones were not found
759                                 if (!acts_c || u == 32)
760                                         allActions[pl] |= acts & ~combo_acts;
761                         } else {
762                                 allActions[pl] |= acts;
763                         }
764                 }
765         }
766
767         PicoPad[0] = (unsigned short) allActions[0];
768         PicoPad[1] = (unsigned short) allActions[1];
769
770         events = (allActions[0] | allActions[1]) >> 16;
771
772         // volume is treated in special way and triggered every frame
773         if ((events & 0x6000) && PsndOut != NULL)
774         {
775                 int vol = currentConfig.volume;
776                 if (events & 0x2000) {
777                         if (vol < 100) vol++;
778                 } else {
779                         if (vol >   0) vol--;
780                 }
781                 // FrameworkAudio_SetVolume(vol, vol); // TODO
782                 sprintf(noticeMsg, "VOL: %02i ", vol);
783                 noticeMsgTime = sceKernelGetSystemTimeLow();
784                 currentConfig.volume = vol;
785         }
786
787         events &= ~prevEvents;
788         if (events) RunEvents(events);
789         if (movie_data) emu_updateMovie();
790
791         prevEvents = (allActions[0] | allActions[1]) >> 16;
792 }
793
794 static void find_combos(void)
795 {
796         int act, u;
797
798         // find out which keys and actions are combos
799         combo_keys = combo_acts = 0;
800         for (act = 0; act < 32; act++)
801         {
802                 int keyc = 0, keyc2 = 0;
803                 if (act == 16 || act == 17) continue; // player2 flag
804                 if (act > 17)
805                 {
806                         for (u = 0; u < 28; u++) // 28 because nub can't produce combos
807                                 if (currentConfig.KeyBinds[u] & (1 << act)) keyc++;
808                 }
809                 else
810                 {
811                         for (u = 0; u < 28; u++)
812                                 if ((currentConfig.KeyBinds[u] & 0x30000) == 0 && // pl. 1
813                                         (currentConfig.KeyBinds[u] & (1 << act))) keyc++;
814                         for (u = 0; u < 28; u++)
815                                 if ((currentConfig.KeyBinds[u] & 0x30000) == 1 && // pl. 2
816                                         (currentConfig.KeyBinds[u] & (1 << act))) keyc2++;
817                 }
818                 if (keyc > 1 || keyc2 > 1)
819                 {
820                         // loop again and mark those keys and actions as combo
821                         for (u = 0; u < 28; u++)
822                         {
823                                 if (currentConfig.KeyBinds[u] & (1 << act)) {
824                                         combo_keys |= 1 << u;
825                                         combo_acts |= 1 << act;
826                                 }
827                         }
828                 }
829         }
830 }
831
832
833 static void simpleWait(unsigned int until)
834 {
835         unsigned int tval;
836         int diff;
837
838         tval = sceKernelGetSystemTimeLow();
839         diff = (int)until - (int)tval;
840         if (diff >= 512 && diff < 100*1024)
841                 sceKernelDelayThread(diff);
842 }
843
844 void emu_Loop(void)
845 {
846         char fpsbuff[24]; // fps count c string
847         unsigned int tval, tval_prev = 0, tval_thissec = 0; // timing
848         int frames_done = 0, frames_shown = 0, oldmodes = 0;
849         int target_fps, target_frametime, lim_time, tval_diff, i;
850         char *notice = NULL;
851
852         lprintf("entered emu_Loop()\n");
853
854         fpsbuff[0] = 0;
855
856         if (currentConfig.CPUclock != psp_get_cpu_clock()) {
857                 lprintf("setting cpu clock to %iMHz... ", currentConfig.CPUclock);
858                 i = psp_set_cpu_clock(currentConfig.CPUclock);
859                 lprintf(i ? "failed\n" : "done\n");
860                 currentConfig.CPUclock = psp_get_cpu_clock();
861         }
862
863         // make sure we are in correct mode
864         vidResetMode();
865         clearArea(1);
866         Pico.m.dirtyPal = 1;
867         oldmodes = ((Pico.video.reg[12]&1)<<2) ^ 0xc;
868         find_combos();
869
870         // pal/ntsc might have changed, reset related stuff
871         target_fps = Pico.m.pal ? 50 : 60;
872         target_frametime = Pico.m.pal ? (1000000<<8)/50 : (1000000<<8)/60+1;
873         reset_timing = 1;
874
875         // prepare CD buffer
876         if (PicoMCD & 1) PicoCDBufferInit();
877
878         // prepare sound stuff
879         PsndOut = NULL;
880         if (currentConfig.EmuOpt & 4)
881         {
882                 sound_prepare();
883         }
884
885         sceDisplayWaitVblankStart();
886
887         // loop?
888         while (engineState == PGS_Running)
889         {
890                 int modes;
891
892                 tval = sceKernelGetSystemTimeLow();
893                 if (reset_timing || tval < tval_prev) {
894                         //stdbg("timing reset");
895                         reset_timing = 0;
896                         tval_thissec = tval;
897                         frames_shown = frames_done = 0;
898                 }
899
900                 // show notice message?
901                 if (noticeMsgTime) {
902                         static int noticeMsgSum;
903                         if (tval - noticeMsgTime > 2000000) { // > 2.0 sec
904                                 noticeMsgTime = 0;
905                                 clearArea(0);
906                                 notice = 0;
907                         } else {
908                                 int sum = noticeMsg[0]+noticeMsg[1]+noticeMsg[2];
909                                 if (sum != noticeMsgSum) { clearArea(0); noticeMsgSum = sum; }
910                                 notice = noticeMsg;
911                         }
912                 }
913
914                 // check for mode changes
915                 modes = ((Pico.video.reg[12]&1)<<2)|(Pico.video.reg[1]&8);
916                 if (modes != oldmodes) {
917                         oldmodes = modes;
918                         clearArea(1);
919                         set_scaling_params();
920                 }
921
922                 // second passed?
923                 if (tval - tval_thissec >= 1000000)
924                 {
925                         // missing 1 frame?
926                         if (currentConfig.Frameskip < 0 && frames_done < target_fps) {
927                                 SkipFrame(); frames_done++;
928                         }
929
930                         if (currentConfig.EmuOpt & 2)
931                                 sprintf(fpsbuff, "%02i/%02i  ", frames_shown, frames_done);
932
933                         tval_thissec += 1000000;
934
935                         if (currentConfig.Frameskip < 0) {
936                                 frames_done  -= target_fps; if (frames_done  < 0) frames_done  = 0;
937                                 frames_shown -= target_fps; if (frames_shown < 0) frames_shown = 0;
938                                 if (frames_shown > frames_done) frames_shown = frames_done;
939                         } else {
940                                 frames_done = frames_shown = 0;
941                         }
942                 }
943 #ifdef PFRAMES
944                 sprintf(fpsbuff, "%i", Pico.m.frame_count);
945 #endif
946
947                 tval_prev = tval;
948                 lim_time = (frames_done+1) * target_frametime;
949                 if (currentConfig.Frameskip >= 0) // frameskip enabled
950                 {
951                         for (i = 0; i < currentConfig.Frameskip; i++) {
952                                 updateKeys();
953                                 SkipFrame(); frames_done++;
954                                 if (PsndOut) { // do framelimitting if sound is enabled
955                                         int tval_diff;
956                                         tval = sceKernelGetSystemTimeLow();
957                                         tval_diff = (int)(tval - tval_thissec) << 8;
958                                         if (tval_diff < lim_time) // we are too fast
959                                                 simpleWait(tval + ((lim_time - tval_diff)>>8));
960                                 }
961                                 lim_time += target_frametime;
962                         }
963                 }
964                 else // auto frameskip
965                 {
966                         int tval_diff;
967                         tval = sceKernelGetSystemTimeLow();
968                         tval_diff = (int)(tval - tval_thissec) << 8;
969                         if (tval_diff > lim_time && (frames_done/16 < frames_shown))
970                         {
971                                 // no time left for this frame - skip
972                                 if (tval_diff - lim_time >= (300000<<8)) {
973                                         reset_timing = 1;
974                                         continue;
975                                 }
976                                 updateKeys();
977                                 SkipFrame(); frames_done++;
978                                 continue;
979                         }
980                 }
981
982                 updateKeys();
983
984                 if (!(PicoOpt&0x10))
985                         EmuScanPrepare();
986
987                 PicoFrame();
988
989                 sceGuSync(0,0);
990
991                 // check time
992                 tval = sceKernelGetSystemTimeLow();
993                 tval_diff = (int)(tval - tval_thissec) << 8;
994
995                 blit2(fpsbuff, notice, tval_diff > lim_time);
996
997                 if (currentConfig.Frameskip < 0 && tval_diff - lim_time >= (300000<<8)) // slowdown detection
998                         reset_timing = 1;
999                 else if (PsndOut != NULL || currentConfig.Frameskip < 0)
1000                 {
1001                         // sleep if we are still too fast
1002                         if (tval_diff < lim_time)
1003                         {
1004                                 // we are too fast
1005                                 simpleWait(tval + ((lim_time - tval_diff) >> 8));
1006                         }
1007                 }
1008
1009                 frames_done++; frames_shown++;
1010         }
1011
1012
1013         if (PicoMCD & 1) PicoCDBufferFree();
1014
1015         if (PsndOut != NULL) {
1016                 PsndOut = NULL;
1017                 sound_end();
1018         }
1019
1020         // save SRAM
1021         if ((currentConfig.EmuOpt & 1) && SRam.changed) {
1022                 emu_msg_cb("Writing SRAM/BRAM..");
1023                 emu_SaveLoadGame(0, 1);
1024                 SRam.changed = 0;
1025         }
1026
1027         // clear fps counters and stuff
1028         memset32((int *)psp_video_get_active_fb() + 512*264*2/4, 0, 512*8*2/4);
1029 }
1030
1031
1032 void emu_ResetGame(void)
1033 {
1034         PicoReset(0);
1035         reset_timing = 1;
1036 }
1037