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 | |
28 | static int g_layer_x, g_layer_y, g_layer_w, g_layer_h; |
29 | static struct vout_fbdev *main_fb, *layer_fb; |
30 | static void *layer_buf; |
31 | static int bounce_buf[320 * 241 / 4]; |
32 | static unsigned short pal[256]; |
88e59df3 |
33 | static unsigned int pal2[256]; // for neon_*2x |
b054fd77 |
34 | |
44343b87 |
35 | enum scaling { |
88e59df3 |
36 | SCALING_1X = 0, |
44343b87 |
37 | SCALING_PROPORTIONAL, |
38 | SCALING_4_3, |
39 | SCALING_FULLSCREEN, |
40 | }; |
41 | |
88e59df3 |
42 | enum sw_filter { |
43 | SWFILTER_NONE = 0, |
44 | SWFILTER_SCALE2X, |
45 | SWFILTER_EAGLE2X, |
46 | }; |
47 | |
b054fd77 |
48 | static 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 | |
70 | static 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 | |
120 | static 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 | |
126 | void 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 | |
198 | fail1: |
199 | vout_fbdev_finish(layer_fb); |
200 | fail0: |
201 | vout_fbdev_finish(main_fb); |
202 | xenv_finish(); |
203 | exit(1); |
204 | } |
205 | |
206 | void platform_late_init(void) |
207 | { |
208 | in_evdev_init(in_evdev_defbinds); |
209 | in_probe(); |
210 | plat_target_setup_input(); |
211 | } |
212 | |
213 | /* video */ |
214 | void CleanSurface(void) |
215 | { |
216 | memset(bounce_buf, 0, sizeof(bounce_buf)); |
217 | vout_fbdev_clear(layer_fb); |
218 | } |
219 | |
220 | void KillVideo(void) |
221 | { |
222 | } |
223 | |
224 | int InitVideo(void) |
225 | { |
226 | CleanSurface(); |
227 | |
228 | srendline = 0; |
229 | erendline = 239; |
230 | |
231 | return 1; |
232 | } |
233 | |
234 | // 16: rrrr rggg gg0b bbbb |
235 | void 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 | |
242 | void 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 | |
250 | void 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 |
301 | void BlitScreen(int skip) |
302 | { |
303 | } |
304 | |
305 | /* throttle */ |
306 | extern uint8 PAL; |
307 | extern int FSkip; |
308 | static struct timeval tv_expect; |
309 | static int skip_count = 0; |
310 | static int us_interval, us_interval1024; |
311 | #define MAX_LAG_FRAMES 3 |
312 | |
313 | void 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 | |
324 | void 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 |
369 | void 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 | |
434 | void platform_set_volume(int val) |
435 | { |
436 | } |
437 | |
438 | void 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 | |
470 | void plat_video_menu_begin(void) |
471 | { |
472 | } |
473 | |
474 | void plat_video_menu_end(void) |
475 | { |
476 | g_menuscreen_ptr = vout_fbdev_flip(main_fb); |
477 | } |
478 | |
479 | void 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 | |
486 | char *DriverUsage=""; |
487 | |
488 | ARGPSTRUCT DriverArgs[]={ |
489 | {0,0,0,0} |
490 | }; |
491 | |
492 | void DoDriverArgs(void) |
493 | { |
494 | } |
495 | |
496 | void GetBaseDirectory(char *BaseDirectory) |
497 | { |
498 | strcpy(BaseDirectory, "fceultra"); |
499 | } |
500 | |
158d88ba |
501 | void platform_get_def_rompath(char *buf, int size) |
502 | { |
503 | strcpy(buf, "/media"); |
504 | } |
505 | |
b054fd77 |
506 | void 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 | } |