giz wip (acc 16bit faster than 8!)
[picodrive.git] / platform / gizmondo / emu.c
1 #include <windows.h>
2 #include <string.h>
3
4 #include <sys/stat.h>  // mkdir
5 #include <sys/types.h>
6
7 #include "kgsdk/Framework.h"
8 #include "kgsdk/Framework2D.h"
9 #include "kgsdk/FrameworkAudio.h"
10 #include "../common/emu.h"
11 #include "../common/lprintf.h"
12 #include "../common/arm_utils.h"
13 #include "emu.h"
14 #include "menu.h"
15 #include "giz.h"
16 #include "asm_utils.h"
17
18 #include <Pico/PicoInt.h>
19
20 #ifdef BENCHMARK
21 #define OSD_FPS_X 220
22 #else
23 #define OSD_FPS_X 260
24 #endif
25
26 // main 300K gfx-related buffer. Used by menu and renderers.
27 unsigned char gfx_buffer[321*240*2*2];
28 char romFileName[MAX_PATH];
29 int engineState;
30
31 unsigned char *PicoDraw2FB = gfx_buffer;  // temporary buffer for alt renderer ( (8+320)*(8+240+8) )
32 int reset_timing = 0;
33
34 static DWORD noticeMsgTime = 0;
35 static int osd_fps_x;
36
37
38 static void blit(const char *fps, const char *notice);
39 static void clearArea(int full);
40
41 void emu_noticeMsgUpdated(void)
42 {
43         noticeMsgTime = GetTickCount();
44 }
45
46 void emu_getMainDir(char *dst, int len)
47 {
48         if (len > 0) *dst = 0;
49 }
50
51 static void emu_msg_cb(const char *msg)
52 {
53         if (giz_screen == NULL)
54                 giz_screen = Framework2D_LockBuffer();
55
56         memset32((int *)((char *)giz_screen + 321*232*2), 0, 321*8*2/4);
57         emu_textOut16(4, 232, msg);
58         noticeMsgTime = GetTickCount() - 2000;
59
60         /* assumption: emu_msg_cb gets called only when something slow is about to happen */
61         reset_timing = 1;
62 }
63
64 static void emu_state_cb(const char *str)
65 {
66         clearArea(0);
67         blit("", str);
68 }
69
70 static void emu_msg_tray_open(void)
71 {
72         strcpy(noticeMsg, "CD tray opened");
73         noticeMsgTime = GetTickCount();
74 }
75
76
77 void emu_Init(void)
78 {
79         // make dirs for saves, cfgs, etc.
80         mkdir("mds", 0777);
81         mkdir("srm", 0777);
82         mkdir("brm", 0777);
83         mkdir("cfg", 0777);
84
85         PicoInit();
86         PicoMessage = emu_msg_cb;
87         PicoMCDopenTray = emu_msg_tray_open;
88         PicoMCDcloseTray = menu_loop_tray;
89 }
90
91 void emu_Deinit(void)
92 {
93         // save SRAM
94         if((currentConfig.EmuOpt & 1) && SRam.changed) {
95                 emu_SaveLoadGame(0, 1);
96                 SRam.changed = 0;
97         }
98
99         if (!(currentConfig.EmuOpt & 0x20)) {
100                 FILE *f = fopen(PicoConfigFile, "r+b");
101                 if (!f) emu_WriteConfig(0);
102                 else {
103                         // if we already have config, reload it, except last ROM
104                         fseek(f, sizeof(currentConfig.lastRomFile), SEEK_SET);
105                         fread(&currentConfig.EmuOpt, 1, sizeof(currentConfig) - sizeof(currentConfig.lastRomFile), f);
106                         fseek(f, 0, SEEK_SET);
107                         fwrite(&currentConfig, 1, sizeof(currentConfig), f);
108                         fflush(f);
109                         fclose(f);
110                 }
111         }
112
113         PicoExit();
114 }
115
116 void emu_setDefaultConfig(void)
117 {
118         memset(&currentConfig, 0, sizeof(currentConfig));
119         currentConfig.lastRomFile[0] = 0;
120         currentConfig.EmuOpt  = 0x1f | 0x600; // | confirm_save, cd_leds
121         currentConfig.PicoOpt = 0x0f | 0xc00; // | cd_pcm, cd_cdda
122         currentConfig.PsndRate = 22050;
123         currentConfig.PicoRegion = 0; // auto
124         currentConfig.PicoAutoRgnOrder = 0x184; // US, EU, JP
125         currentConfig.Frameskip = 0;//-1; // auto
126         currentConfig.volume = 50;
127         currentConfig.KeyBinds[ 2] = 1<<0; // SACB RLDU
128         currentConfig.KeyBinds[ 3] = 1<<1;
129         currentConfig.KeyBinds[ 0] = 1<<2;
130         currentConfig.KeyBinds[ 1] = 1<<3;
131         currentConfig.KeyBinds[ 5] = 1<<4;
132         currentConfig.KeyBinds[ 6] = 1<<5;
133         currentConfig.KeyBinds[ 7] = 1<<6;
134         currentConfig.KeyBinds[ 4] = 1<<7;
135         currentConfig.KeyBinds[ 8] = 1<<27; // save state
136         currentConfig.KeyBinds[ 9] = 1<<28; // load state
137         currentConfig.KeyBinds[12] = 1<<29; // vol up
138         currentConfig.KeyBinds[11] = 1<<30; // vol down
139         currentConfig.PicoCDBuffers = 64;
140         currentConfig.scaling = 0;
141 }
142
143
144 static int EmuScan16(unsigned int num, void *sdata)
145 {
146         if (!(Pico.video.reg[1]&8)) num += 8;
147         DrawLineDest = (unsigned short *) giz_screen + 321*(num+1);
148
149         return 0;
150 }
151
152 static int EmuScan8(unsigned int num, void *sdata)
153 {
154         // draw like the fast renderer
155         if (!(Pico.video.reg[1]&8)) num += 8;
156         HighCol = gfx_buffer + 328*(num+1);
157
158         return 0;
159 }
160
161 static void osd_text(int x, int y, const char *text)
162 {
163         int len = strlen(text) * 8 / 2;
164         int *p, h;
165         for (h = 0; h < 8; h++) {
166                 p = (int *) ((unsigned short *) giz_screen+x+321*(y+h));
167                 p = (int *) ((int)p & ~3); // align
168                 memset32(p, 0, len);
169         }
170         emu_textOut16(x, y, text);
171 }
172
173 /*
174 void log1(void *p1, void *p2)
175 {
176         lprintf("%p %p %p\n", p1, p2, DrawLineDest);
177 }
178 */
179
180 short localPal[0x100];
181 static void (*vidCpy8to16)(void *dest, void *src, short *pal, int lines) = NULL;
182
183 static void blit(const char *fps, const char *notice)
184 {
185         int emu_opt = currentConfig.EmuOpt;
186
187         if (PicoOpt&0x10) {
188                 // 8bit fast renderer
189                 if (Pico.m.dirtyPal) {
190                         Pico.m.dirtyPal = 0;
191                         vidConvCpyRGB565(localPal, Pico.cram, 0x40);
192                 }
193                 vidCpy8to16((unsigned short *)giz_screen+321*8, PicoDraw2FB+328*8, localPal, 224);
194         } else if (!(emu_opt&0x80)) {
195                 // 8bit accurate renderer
196                 if (Pico.m.dirtyPal) {
197                         Pico.m.dirtyPal = 0;
198                         vidConvCpyRGB565(localPal, Pico.cram, 0x40);
199                         if (Pico.video.reg[0xC]&8) { // shadow/hilight mode
200                                 //vidConvCpyRGB32sh(localPal+0x40, Pico.cram, 0x40);
201                                 //vidConvCpyRGB32hi(localPal+0x80, Pico.cram, 0x40); // TODO
202                                 blockcpy(localPal+0xc0, localPal+0x40, 0x40*2);
203                                 localPal[0xc0] = 0x0600;
204                                 localPal[0xd0] = 0xc000;
205                                 localPal[0xe0] = 0x0000; // reserved pixels for OSD
206                                 localPal[0xf0] = 0xffff;
207                         }
208                         /* no support
209                         else if (rendstatus & 0x20) { // mid-frame palette changes
210                                 vidConvCpyRGB565(localPal+0x40, HighPal, 0x40);
211                                 vidConvCpyRGB565(localPal+0x80, HighPal+0x40, 0x40);
212                         } */
213                 }
214                 // TODO...
215                 vidCpy8to16((unsigned short *)giz_screen+321*8, PicoDraw2FB+328*8, localPal, 224);
216         }
217
218         if (notice || (emu_opt & 2)) {
219                 int h = 232;
220                 if (notice)      osd_text(4, h, notice);
221                 if (emu_opt & 2) osd_text(osd_fps_x, h, fps);
222         }
223 //      if ((emu_opt & 0x400) && (PicoMCD & 1))
224 //              cd_leds();
225
226 }
227
228 // clears whole screen or just the notice area (in all buffers)
229 static void clearArea(int full)
230 {
231         if (giz_screen == NULL)
232                 giz_screen = Framework2D_LockBuffer();
233         if (full) memset32(giz_screen, 0, 320*240*2/4);
234         else      memset32((int *)((char *)giz_screen + 321*232*2), 0, 321*8*2/4);
235 }
236
237 static void vidResetMode(void)
238 {
239         giz_screen = Framework2D_LockBuffer();
240
241         if (PicoOpt&0x10) {
242         } else if (currentConfig.EmuOpt&0x80) {
243                 PicoDrawSetColorFormat(1);
244                 PicoScan = EmuScan16;
245         } else {
246                 PicoDrawSetColorFormat(-1);
247                 PicoScan = EmuScan8;
248         }
249         if ((PicoOpt&0x10) || !(currentConfig.EmuOpt&0x80)) {
250                 // setup pal for 8-bit modes
251                 localPal[0xc0] = 0x0600;
252                 localPal[0xd0] = 0xc000;
253                 localPal[0xe0] = 0x0000; // reserved pixels for OSD
254                 localPal[0xf0] = 0xffff;
255         }
256         Pico.m.dirtyPal = 1;
257
258         memset32(giz_screen, 0, 321*240*2/4);
259         Framework2D_UnlockBuffer();
260         giz_screen = NULL;
261 }
262
263
264 static void SkipFrame(int do_audio)
265 {
266         PicoSkipFrame=do_audio ? 1 : 2;
267         PicoFrame();
268         PicoSkipFrame=0;
269 }
270
271 void emu_forcedFrame(void)
272 {
273         // TODO
274 }
275
276 static void updateKeys(void)
277 {
278         unsigned int keys, allActions[2] = { 0, 0 }, events;
279         static unsigned int prevEvents = 0;
280         int i;
281
282         keys = Framework_PollGetButtons();
283         if (keys & BTN_HOME) {
284                 engineState = PGS_Menu;
285                 // wait until select is released, so menu would not resume game
286                 while (Framework_PollGetButtons() & BTN_HOME) Sleep(50);
287         }
288
289         keys &= CONFIGURABLE_KEYS;
290
291         for (i = 0; i < 32; i++)
292         {
293                 if (keys & (1 << i)) {
294                         int pl, acts = currentConfig.KeyBinds[i];
295                         if (!acts) continue;
296                         pl = (acts >> 16) & 1;
297                         /* TODO if (combo_keys & (1 << i)) {
298                                 int u = i+1, acts_c = acts & combo_acts;
299                                 // let's try to find the other one
300                                 if (acts_c)
301                                         for (; u < 32; u++)
302                                                 if ( (currentConfig.KeyBinds[u] & acts_c) && (keys & (1 << u)) ) {
303                                                         allActions[pl] |= acts_c;
304                                                         keys &= ~((1 << i) | (1 << u));
305                                                         break;
306                                                 }
307                                 // add non-combo actions if combo ones were not found
308                                 if (!acts_c || u == 32)
309                                         allActions[pl] |= acts & ~combo_acts;
310                         } else */ {
311                                 allActions[pl] |= acts;
312                         }
313                 }
314         }
315
316         PicoPad[0] = (unsigned short) allActions[0];
317         PicoPad[1] = (unsigned short) allActions[1];
318
319         events = (allActions[0] | allActions[1]) >> 16;
320
321         // volume is treated in special way and triggered every frame
322         if (events & 0x6000) {
323                 int vol = currentConfig.volume;
324                 if (events & 0x2000) {
325                         if (vol < 100) vol++;
326                 } else {
327                         if (vol >   0) vol--;
328                 }
329                 //gp2x_sound_volume(vol, vol);
330                 sprintf(noticeMsg, "VOL: %02i", vol);
331                 noticeMsgTime = GetTickCount();
332                 currentConfig.volume = vol;
333         }
334
335         events &= ~prevEvents;
336         //if (events) RunEvents(events); // TODO
337         if (movie_data) emu_updateMovie();
338
339         prevEvents = (allActions[0] | allActions[1]) >> 16;
340 }
341
342 static void simpleWait(DWORD until)
343 {
344 }
345
346 void emu_Loop(void)
347 {
348         //static int PsndRate_old = 0, PicoOpt_old = 0, PsndLen_real = 0, pal_old = 0;
349         char fpsbuff[24]; // fps count c string
350         DWORD tval, tval_prev = 0, tval_thissec = 0; // timing
351         int frames_done = 0, frames_shown = 0, oldmodes = 0;
352         int target_fps, target_frametime, lim_time, tval_diff, i;
353         char *notice = NULL;
354
355         lprintf("entered emu_Loop()\n");
356
357         fpsbuff[0] = 0;
358
359         // make sure we are in correct mode
360         vidResetMode();
361         if (currentConfig.scaling) PicoOpt|=0x4000;
362         else PicoOpt&=~0x4000;
363         Pico.m.dirtyPal = 1;
364         oldmodes = ((Pico.video.reg[12]&1)<<2) ^ 0xc;
365         //find_combos(); // TODO
366
367         // pal/ntsc might have changed, reset related stuff
368         target_fps = Pico.m.pal ? 50 : 60;
369         target_frametime = (1000<<8)/target_fps;
370         reset_timing = 1;
371
372         // prepare sound stuff
373 /*      if (currentConfig.EmuOpt & 4) {
374                 int snd_excess_add;
375                 if (PsndRate != PsndRate_old || (PicoOpt&0x0b) != (PicoOpt_old&0x0b) || Pico.m.pal != pal_old) {
376                         sound_rerate(Pico.m.frame_count ? 1 : 0);
377                 }
378                 snd_excess_add = ((PsndRate - PsndLen*target_fps)<<16) / target_fps;
379                 lprintf("starting audio: %i len: %i (ex: %04x) stereo: %i, pal: %i\n",
380                         PsndRate, PsndLen, snd_excess_add, (PicoOpt&8)>>3, Pico.m.pal);
381                 gp2x_start_sound(PsndRate, 16, (PicoOpt&8)>>3);
382                 gp2x_sound_volume(currentConfig.volume, currentConfig.volume);
383                 PicoWriteSound = updateSound;
384                 memset(sndBuffer, 0, sizeof(sndBuffer));
385                 PsndOut = sndBuffer;
386                 PsndRate_old = PsndRate;
387                 PsndLen_real = PsndLen;
388                 PicoOpt_old  = PicoOpt;
389                 pal_old = Pico.m.pal;
390         } else*/ {
391                 PsndOut = 0;
392         }
393
394         // prepare CD buffer
395         if (PicoMCD & 1) PicoCDBufferInit();
396
397         // loop?
398         while (engineState == PGS_Running)
399         {
400                 int modes;
401
402                 tval = GetTickCount();
403                 if (reset_timing || tval < tval_prev) {
404                         reset_timing = 0;
405                         tval_thissec = tval;
406                         frames_shown = frames_done = 0;
407                 }
408
409                 // show notice message?
410                 if (noticeMsgTime) {
411                         static int noticeMsgSum;
412                         if (tval - noticeMsgTime > 2000) { // > 2.0 sec
413                                 noticeMsgTime = 0;
414                                 clearArea(0);
415                                 notice = 0;
416                         } else {
417                                 int sum = noticeMsg[0]+noticeMsg[1]+noticeMsg[2];
418                                 if (sum != noticeMsgSum) { clearArea(0); noticeMsgSum = sum; }
419                                 notice = noticeMsg;
420                         }
421                 }
422
423                 // check for mode changes
424                 modes = ((Pico.video.reg[12]&1)<<2)|(Pico.video.reg[1]&8);
425                 if (modes != oldmodes) {
426                         osd_fps_x = OSD_FPS_X;
427                         //if (modes & 4)
428                                 vidCpy8to16 = vidCpy8to16_40;
429                         //else
430                         //      vidCpy8to16 = vidCpy8to16_32col;
431                         oldmodes = modes;
432                         clearArea(1);
433                 }
434
435                 // second passed?
436                 if (tval - tval_thissec >= 1000)
437                 {
438 #ifdef BENCHMARK
439                         static int bench = 0, bench_fps = 0, bench_fps_s = 0, bfp = 0, bf[4];
440                         if(++bench == 10) {
441                                 bench = 0;
442                                 bench_fps_s = bench_fps;
443                                 bf[bfp++ & 3] = bench_fps;
444                                 bench_fps = 0;
445                         }
446                         bench_fps += frames_shown;
447                         sprintf(fpsbuff, "%02i/%02i/%02i", frames_shown, bench_fps_s, (bf[0]+bf[1]+bf[2]+bf[3])>>2);
448 #else
449                         if(currentConfig.EmuOpt & 2)
450                                 sprintf(fpsbuff, "%02i/%02i", frames_shown, frames_done);
451 #endif
452                         tval_thissec = tval;
453
454                         if (PsndOut == 0 && currentConfig.Frameskip >= 0) {
455                                 frames_done = frames_shown = 0;
456                         } else {
457                                 // it is quite common for this implementation to leave 1 fame unfinished
458                                 // when second changes, but we don't want buffer to starve.
459                                 if (PsndOut && frames_done < target_fps && frames_done > target_fps-5) {
460                                         updateKeys();
461                                         SkipFrame(1); frames_done++;
462                                 }
463
464                                 frames_done  -= target_fps; if (frames_done  < 0) frames_done  = 0;
465                                 frames_shown -= target_fps; if (frames_shown < 0) frames_shown = 0;
466                                 if (frames_shown > frames_done) frames_shown = frames_done;
467                         }
468                 }
469 #ifdef PFRAMES
470                 sprintf(fpsbuff, "%i", Pico.m.frame_count);
471 #endif
472
473                 tval_prev = tval;
474                 lim_time = (frames_done+1) * target_frametime;
475                 if (currentConfig.Frameskip >= 0) // frameskip enabled
476                 {
477                         for (i = 0; i < currentConfig.Frameskip; i++) {
478                                 updateKeys();
479                                 SkipFrame(1); frames_done++;
480                                 if (PsndOut) { // do framelimitting if sound is enabled
481                                         int tval_diff;
482                                         tval = GetTickCount();
483                                         tval_diff = (int)(tval - tval_thissec) << 8;
484                                         if (tval_diff < lim_time) // we are too fast
485                                                 simpleWait(tval + ((lim_time - tval_diff)>>8));
486                                 }
487                                 lim_time += target_frametime;
488                         }
489                 }
490                 else // auto frameskip
491                 {
492                         int tval_diff;
493                         tval = GetTickCount();
494                         tval_diff = (int)(tval - tval_thissec) << 8;
495                         if (tval_diff > lim_time)
496                         {
497                                 // no time left for this frame - skip
498                                 if (tval_diff - lim_time >= (300<<8)) {
499                                         /* something caused a slowdown for us (disk access? cache flush?)
500                                          * try to recover by resetting timing... */
501                                         reset_timing = 1;
502                                         continue;
503                                 }
504                                 updateKeys();
505                                 SkipFrame(tval_diff < lim_time+target_frametime*2); frames_done++;
506                                 continue;
507                         }
508                 }
509
510                 updateKeys();
511
512                 if (giz_screen == NULL && (currentConfig.EmuOpt&0x80))
513                         giz_screen = Framework2D_LockBuffer();
514                 if (!(PicoOpt&0x10))
515                         PicoScan((unsigned) -1, NULL);
516
517                 PicoFrame();
518
519                 if (giz_screen == NULL)
520                         giz_screen = Framework2D_LockBuffer();
521
522                 blit(fpsbuff, notice);
523
524                 if (giz_screen != NULL) {
525                         Framework2D_UnlockBuffer();
526                         giz_screen = NULL;
527                 }
528                 //lprintf("after unlock\n");
529
530                 // check time
531                 tval = GetTickCount();
532                 tval_diff = (int)(tval - tval_thissec) << 8;
533
534                 if (currentConfig.Frameskip < 0 && tval_diff - lim_time >= (300<<8)) // slowdown detection
535                         reset_timing = 1;
536                 else if (PsndOut != NULL || currentConfig.Frameskip < 0)
537                 {
538                         // sleep if we are still too fast
539                         if (tval_diff < lim_time)
540                         {
541                                 // we are too fast
542                                 simpleWait(tval + ((lim_time - tval_diff) >> 8));
543                         }
544                 }
545
546                 frames_done++; frames_shown++;
547         }
548
549
550         if (PicoMCD & 1) PicoCDBufferFree();
551
552         // save SRAM
553         if ((currentConfig.EmuOpt & 1) && SRam.changed) {
554                 emu_state_cb("Writing SRAM/BRAM..");
555                 emu_SaveLoadGame(0, 1);
556                 SRam.changed = 0;
557         }
558 }
559
560
561 void emu_ResetGame(void)
562 {
563         PicoReset(0);
564         reset_timing = 1;
565 }
566