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