2ea239e915d120019d907376c03ca625b0755d85
[fceu.git] / drivers / pandora / pandora.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <fcntl.h>
7 #include <sys/ioctl.h>
8 #include <unistd.h>
9 #include <linux/input.h>
10 #include <linux/omapfb.h>
11
12 #include "../common/platform.h"
13 #include "../common/input.h"
14 #include "../common/settings.h"
15 #include "../common/main.h"
16 #include "../common/args.h"
17 #include "../../video.h"
18 #include "../arm/asmutils.h"
19 #include "../libpicofe/input.h"
20 #include "../libpicofe/plat.h"
21 #include "../libpicofe/menu.h"
22 #include "../libpicofe/linux/in_evdev.h"
23 #include "../libpicofe/linux/fbdev.h"
24 #include "../libpicofe/linux/xenv.h"
25
26 static int g_layer_x, g_layer_y, g_layer_w, g_layer_h;
27 static struct vout_fbdev *main_fb, *layer_fb;
28 static void *layer_buf;
29 static int bounce_buf[320 * 241 / 4];
30 static unsigned short pal[256];
31
32 enum scaling {
33         SCALING_1X,
34         SCALING_PROPORTIONAL,
35         SCALING_4_3,
36         SCALING_FULLSCREEN,
37 };
38
39 static const struct in_default_bind in_evdev_defbinds[] = {
40   { KEY_UP,       IN_BINDTYPE_PLAYER12, NKEYB_UP },
41   { KEY_DOWN,     IN_BINDTYPE_PLAYER12, NKEYB_DOWN },
42   { KEY_LEFT,     IN_BINDTYPE_PLAYER12, NKEYB_LEFT },
43   { KEY_RIGHT,    IN_BINDTYPE_PLAYER12, NKEYB_RIGHT },
44   { KEY_PAGEDOWN, IN_BINDTYPE_PLAYER12, NKEYB_B },
45   { KEY_END,      IN_BINDTYPE_PLAYER12, NKEYB_A },
46   { KEY_HOME,     IN_BINDTYPE_PLAYER12, NKEYB_B_TURBO },
47   { KEY_PAGEUP,   IN_BINDTYPE_PLAYER12, NKEYB_A_TURBO },
48   { KEY_LEFTCTRL, IN_BINDTYPE_PLAYER12, NKEYB_SELECT },
49   { KEY_LEFTALT,  IN_BINDTYPE_PLAYER12, NKEYB_START },
50   { KEY_1,        IN_BINDTYPE_EMU, EACTB_SAVE_STATE },
51   { KEY_2,        IN_BINDTYPE_EMU, EACTB_LOAD_STATE },
52   { KEY_3,        IN_BINDTYPE_EMU, EACTB_PREV_SLOT },
53   { KEY_4,        IN_BINDTYPE_EMU, EACTB_NEXT_SLOT },
54   { KEY_5,        IN_BINDTYPE_EMU, EACTB_FDS_INSERT },
55   { KEY_6,        IN_BINDTYPE_EMU, EACTB_FDS_SELECT },
56   { KEY_7,        IN_BINDTYPE_EMU, EACTB_INSERT_COIN },
57   { KEY_SPACE,    IN_BINDTYPE_EMU, EACTB_ENTER_MENU },
58   { 0, 0, 0 }
59 };
60
61 static int omap_setup_layer_(int fd, int enabled, int x, int y, int w, int h)
62 {
63         struct omapfb_plane_info pi = { 0, };
64         struct omapfb_mem_info mi = { 0, };
65         int ret;
66
67         ret = ioctl(fd, OMAPFB_QUERY_PLANE, &pi);
68         if (ret != 0) {
69                 perror("QUERY_PLANE");
70                 return -1;
71         }
72
73         ret = ioctl(fd, OMAPFB_QUERY_MEM, &mi);
74         if (ret != 0) {
75                 perror("QUERY_MEM");
76                 return -1;
77         }
78
79         /* must disable when changing stuff */
80         if (pi.enabled) {
81                 pi.enabled = 0;
82                 ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
83                 if (ret != 0)
84                         perror("SETUP_PLANE");
85         }
86
87         if (mi.size < 640*512*3*3) {
88                 mi.size = 640*512*3*3;
89                 ret = ioctl(fd, OMAPFB_SETUP_MEM, &mi);
90                 if (ret != 0) {
91                         perror("SETUP_MEM");
92                         return -1;
93                 }
94         }
95
96         pi.pos_x = x;
97         pi.pos_y = y;
98         pi.out_width = w;
99         pi.out_height = h;
100         pi.enabled = enabled;
101
102         ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
103         if (ret != 0) {
104                 perror("SETUP_PLANE");
105                 return -1;
106         }
107
108         return 0;
109 }
110
111 static int omap_enable_layer(int enabled)
112 {
113         return omap_setup_layer_(vout_fbdev_get_fd(layer_fb), enabled,
114                 g_layer_x, g_layer_y, g_layer_w, g_layer_h);
115 }
116
117 void platform_init(void)
118 {
119         const char *main_fb_name, *layer_fb_name;
120         int fd, ret, w, h;
121
122         memset(&Settings, 0, sizeof(Settings));
123         Settings.frameskip = 0;
124         Settings.sound_rate = 44100;
125         Settings.turbo_rate_add = (8*2 << 24) / 60 + 1; // 8Hz turbofire
126         Settings.gamma = 100;
127         Settings.sstate_confirm = 1;
128         Settings.scaling = SCALING_4_3;
129
130         main_fb_name = getenv("FBDEV_MAIN");
131         if (main_fb_name == NULL)
132                 main_fb_name = "/dev/fb0";
133
134         layer_fb_name = getenv("FBDEV_LAYER");
135         if (layer_fb_name == NULL)
136                 layer_fb_name = "/dev/fb1";
137
138         // must set the layer up first to be able to use it
139         fd = open(layer_fb_name, O_RDWR);
140         if (fd == -1) {
141                 fprintf(stderr, "%s: ", layer_fb_name);
142                 perror("open");
143                 exit(1);
144         }
145
146         g_layer_x = 80, g_layer_y = 0;
147         g_layer_w = 640, g_layer_h = 480;
148
149         ret = omap_setup_layer_(fd, 0, g_layer_x, g_layer_y, g_layer_w, g_layer_h);
150         close(fd);
151         if (ret != 0) {
152                 fprintf(stderr, "failed to set up layer, exiting.\n");
153                 exit(1);
154         }
155
156         xenv_init(NULL, "fceu");
157
158         w = h = 0;
159         main_fb = vout_fbdev_init(main_fb_name, &w, &h, 16, 2);
160         if (main_fb == NULL) {
161                 fprintf(stderr, "couldn't init fb: %s\n", main_fb_name);
162                 exit(1);
163         }
164
165         g_menuscreen_w = w;
166         g_menuscreen_h = h;
167         g_menuscreen_ptr = vout_fbdev_flip(main_fb);
168
169         w = 640;
170         h = 512;
171         layer_fb = vout_fbdev_init(layer_fb_name, &w, &h, 16, 3);
172         if (layer_fb == NULL) {
173                 fprintf(stderr, "couldn't init fb: %s\n", layer_fb_name);
174                 goto fail0;
175         }
176         layer_buf = vout_fbdev_resize(layer_fb, 256, 240, 16,
177                 0, 0, 0, 0, 3);
178         if (layer_buf == NULL) {
179                 fprintf(stderr, "couldn't get layer buf\n");
180                 goto fail1;
181         }
182
183         plat_target_init();
184
185         XBuf = (void *)bounce_buf;
186
187         return;
188
189 fail1:
190         vout_fbdev_finish(layer_fb);
191 fail0:
192         vout_fbdev_finish(main_fb);
193         xenv_finish();
194         exit(1);
195 }
196
197 void platform_late_init(void)
198 {
199         in_evdev_init(in_evdev_defbinds);
200         in_probe();
201         plat_target_setup_input();
202 }
203
204 /* video */
205 void CleanSurface(void)
206 {
207         memset(bounce_buf, 0, sizeof(bounce_buf));
208         vout_fbdev_clear(layer_fb);
209 }
210
211 void KillVideo(void)
212 {
213 }
214
215 int InitVideo(void)
216 {
217         CleanSurface();
218
219         srendline = 0;
220         erendline = 239;
221
222         return 1;
223 }
224
225 // 16: rrrr rggg gg0b bbbb
226 void FCEUD_SetPalette(uint8 index, uint8 r, uint8 g, uint8 b)
227 {
228         pal[index] = ((r & 0xf8) << 8) | ((g & 0xfc) << 3) | (b >> 3);
229 }
230
231 void FCEUD_GetPalette(uint8 index, uint8 *r, uint8 *g, uint8 *b)
232 {
233         unsigned int v = pal[index];
234         *r = (v >> 8) & 0xf8;
235         *g = (v >> 3) & 0xfc;
236         *b = v << 3;
237 }
238
239 void BlitPrepare(int skip)
240 {
241         char *s;
242         short *d;
243         int *p, i;
244
245         if (skip)
246                 return;
247
248         if (eoptions & EO_CLIPSIDES)
249         {
250                 int i, *p = bounce_buf + 32/4;
251                 for (i = 240; i; i--, p += 320/4)
252                 {
253                         p[0] = p[1] = p[62] = p[63] = 0;
254                 }
255         }
256
257         p = bounce_buf + 32/4;
258         if (srendline > 0)
259                 for (i = srendline; i > 0; i--, p += 320/4)
260                         memset(p, 0, 256);
261         if (erendline < 239)
262         {
263                 p = bounce_buf + erendline*320/4 + 32/4;
264                 for (i = 239-erendline; i > 0; i--, p += 320/4)
265                         memset(p, 0, 256);
266         }
267
268         /* this is called before throttle, so we blit here too */
269         s = (char *)bounce_buf + 32;
270         d = (short *)layer_buf;
271
272         for (i = 0; i < 239; i++, d += 256, s += 320)
273                 do_clut(d, s, pal, 256);
274         
275         layer_buf = vout_fbdev_flip(layer_fb);
276 }
277
278 void BlitScreen(int skip)
279 {
280 }
281
282 /* throttle */
283 extern uint8 PAL;
284 extern int FSkip;
285 static struct timeval tv_expect;
286 static int skip_count = 0;
287 static int us_interval, us_interval1024;
288 #define MAX_LAG_FRAMES 3
289
290 void RefreshThrottleFPS(void)
291 {
292         skip_count = 0;
293         us_interval = PAL ? 20000 : 16667;
294         us_interval1024 = PAL ? 20000*1024 : 17066667;
295
296         vout_fbdev_wait_vsync(layer_fb);
297         gettimeofday(&tv_expect, 0);
298         tv_expect.tv_usec *= 1024;
299 }
300
301 void SpeedThrottle(void)
302 {
303         struct timeval now;
304         int diff;
305
306         gettimeofday(&now, 0);
307
308         // usec*1024 units to prevent drifting
309         tv_expect.tv_usec += us_interval1024;
310         if (tv_expect.tv_usec > 1000000 * 1024) {
311                 tv_expect.tv_usec -= 1000000 * 1024;
312                 tv_expect.tv_sec++;
313         }
314
315         diff = (tv_expect.tv_sec - now.tv_sec) * 1000000 +
316                 (tv_expect.tv_usec >> 10) - now.tv_usec;
317
318         if (Settings.frameskip >= 0)
319         {
320                 if (skip_count >= Settings.frameskip)
321                         skip_count = 0;
322                 else {
323                         skip_count++;
324                         FSkip = 1;
325                 }
326         }
327         else 
328         {
329                 FSkip = diff < -us_interval;
330         }
331
332         if (diff > MAX_LAG_FRAMES * us_interval
333             || diff < -MAX_LAG_FRAMES * us_interval)
334         {
335                 //printf("reset %d\n", diff);
336                 RefreshThrottleFPS();
337                 return;
338         }
339         else if (diff > us_interval)
340         {
341                 usleep(diff - us_interval);
342         }
343 }
344
345 /* called just before emulation */
346 void platform_apply_config(void)
347 {
348         float mult;
349
350         switch (Settings.scaling)
351         {
352         case SCALING_1X:
353                 g_layer_w = 256;
354                 g_layer_h = 240;
355                 break;
356         case SCALING_PROPORTIONAL:
357                 // assumes screen width > height
358                 mult = (float)g_menuscreen_h / 240;
359                 g_layer_w = (int)(256 * mult);
360                 g_layer_h = g_menuscreen_h;
361                 break;
362         case SCALING_4_3:
363                 g_layer_w = g_menuscreen_h * 4 / 3;
364                 g_layer_h = g_menuscreen_h;
365                 break;
366         case SCALING_FULLSCREEN:
367                 g_layer_w = g_menuscreen_w;
368                 g_layer_h = g_menuscreen_h;
369                 break;
370         }
371         if (g_layer_w > g_menuscreen_w)
372                 g_layer_w = g_menuscreen_w;
373         if (g_layer_h > g_menuscreen_h)
374                 g_layer_h = g_menuscreen_h;
375         g_layer_x = g_menuscreen_w / 2 - g_layer_w / 2;
376         g_layer_y = g_menuscreen_h / 2 - g_layer_h / 2;
377
378         plat_target_set_filter(Settings.hw_filter);
379         plat_target_set_lcdrate(PAL);
380
381         // adjust since we run at 60Hz, and real NES doesn't
382         FCEUI_Sound(Settings.sound_rate + Settings.sound_rate / 980);
383
384         omap_enable_layer(1);
385 }
386
387 void platform_set_volume(int val)
388 {
389 }
390
391 void plat_video_menu_enter(int is_rom_loaded)
392 {
393         unsigned short *d = g_menubg_src_ptr;
394         unsigned char *s, *sr = (void *)bounce_buf;
395         int u, v = 240 / 2;
396         int x, y, w;
397
398         omap_enable_layer(0);
399
400         if (!fceugi)
401                 return;
402
403         for (y = 0; y < g_menuscreen_h; y++)
404         {
405                 s = sr + v * 320 + 32;
406                 u = 256 / 2;
407                 for (x = 0; x < g_menuscreen_w; )
408                 {
409                         w = g_menuscreen_w - x;
410                         if (w > 256 - u)
411                                 w = 256 - u;
412                         do_clut(d + x, s + u, pal, w);
413                         u = (u + w) & 255;
414                         x += w;
415                 }
416                 d += x;
417                 v++;
418                 if (v > erendlinev[PAL])
419                         v = srendlinev[PAL];
420         }
421 }
422
423 void plat_video_menu_begin(void)
424 {
425 }
426
427 void plat_video_menu_end(void)
428 {
429         g_menuscreen_ptr = vout_fbdev_flip(main_fb);
430 }
431
432 void plat_video_menu_leave(void)
433 {
434         memset(g_menuscreen_ptr, 0, g_menuscreen_w * g_menuscreen_h * 2);
435         g_menuscreen_ptr = vout_fbdev_flip(main_fb);
436         // layer enabled in platform_apply_config()
437 }
438
439 char *DriverUsage="";
440
441 ARGPSTRUCT DriverArgs[]={
442          {0,0,0,0}
443 };
444
445 void DoDriverArgs(void)
446 {
447 }
448
449 void GetBaseDirectory(char *BaseDirectory)
450 {
451         strcpy(BaseDirectory, "fceultra");
452 }
453
454 void platform_finish(void)
455 {
456         omap_enable_layer(0);
457         vout_fbdev_finish(layer_fb);
458         vout_fbdev_finish(main_fb);
459         xenv_finish();
460 }