#include "../common/args.h"
#include "../../video.h"
#include "../arm/asmutils.h"
+#include "../arm/neon_scale2x.h"
+#include "../arm/neon_eagle2x.h"
#include "../libpicofe/input.h"
#include "../libpicofe/plat.h"
#include "../libpicofe/menu.h"
static void *layer_buf;
static int bounce_buf[320 * 241 / 4];
static unsigned short pal[256];
+static unsigned int pal2[256]; // for neon_*2x
+
+enum scaling {
+ SCALING_1X = 0,
+ SCALING_PROPORTIONAL,
+ SCALING_4_3,
+ SCALING_FULLSCREEN,
+};
+
+enum sw_filter {
+ SWFILTER_NONE = 0,
+ SWFILTER_SCALE2X,
+ SWFILTER_EAGLE2X,
+};
static const struct in_default_bind in_evdev_defbinds[] = {
{ KEY_UP, IN_BINDTYPE_PLAYER12, NKEYB_UP },
{ KEY_PAGEUP, IN_BINDTYPE_PLAYER12, NKEYB_A_TURBO },
{ KEY_LEFTCTRL, IN_BINDTYPE_PLAYER12, NKEYB_SELECT },
{ KEY_LEFTALT, IN_BINDTYPE_PLAYER12, NKEYB_START },
+ { KEY_1, IN_BINDTYPE_EMU, EACTB_SAVE_STATE },
+ { KEY_2, IN_BINDTYPE_EMU, EACTB_LOAD_STATE },
+ { KEY_3, IN_BINDTYPE_EMU, EACTB_PREV_SLOT },
+ { KEY_4, IN_BINDTYPE_EMU, EACTB_NEXT_SLOT },
+ { KEY_5, IN_BINDTYPE_EMU, EACTB_FDS_INSERT },
+ { KEY_6, IN_BINDTYPE_EMU, EACTB_FDS_SELECT },
+ { KEY_7, IN_BINDTYPE_EMU, EACTB_INSERT_COIN },
{ KEY_SPACE, IN_BINDTYPE_EMU, EACTB_ENTER_MENU },
{ 0, 0, 0 }
};
perror("SETUP_PLANE");
}
- if (mi.size < 640*512*3*3) {
- mi.size = 640*512*3*3;
+ if (mi.size < 256*2*240*2*3) {
+ mi.size = 256*2*240*2*3;
ret = ioctl(fd, OMAPFB_SETUP_MEM, &mi);
if (ret != 0) {
perror("SETUP_MEM");
Settings.turbo_rate_add = (8*2 << 24) / 60 + 1; // 8Hz turbofire
Settings.gamma = 100;
Settings.sstate_confirm = 1;
+ Settings.scaling = SCALING_4_3;
main_fb_name = getenv("FBDEV_MAIN");
if (main_fb_name == NULL)
g_menuscreen_h = h;
g_menuscreen_ptr = vout_fbdev_flip(main_fb);
- w = 640;
- h = 512;
+ w = 256*2;
+ h = 240*2;
layer_fb = vout_fbdev_init(layer_fb_name, &w, &h, 16, 3);
if (layer_fb == NULL) {
fprintf(stderr, "couldn't init fb: %s\n", layer_fb_name);
}
plat_target_init();
- omap_enable_layer(1);
XBuf = (void *)bounce_buf;
// 16: rrrr rggg gg0b bbbb
void FCEUD_SetPalette(uint8 index, uint8 r, uint8 g, uint8 b)
{
- pal[index] = ((r & 0xf8) << 8) | ((g & 0xfc) << 3) | (b >> 3);
+ unsigned int v = ((r & 0xf8) << 8) | ((g & 0xfc) << 3) | (b >> 3);
+ pal[index] = v;
+ pal2[index] = v;
}
void FCEUD_GetPalette(uint8 index, uint8 *r, uint8 *g, uint8 *b)
void BlitPrepare(int skip)
{
+ unsigned char *s;
+ unsigned short *d;
+ int *p, i;
+
if (skip)
return;
}
}
- if (Settings.accurate_mode)
+ p = bounce_buf + 32/4;
+ if (srendline > 0)
+ for (i = srendline; i > 0; i--, p += 320/4)
+ memset(p, 0, 256);
+ if (erendline < 239)
{
- int i, *p = bounce_buf + 32/4;
- if (srendline > 0)
- for (i = srendline; i > 0; i--, p += 320/4)
- memset(p, 0, 256);
- if (erendline < 239)
- {
- int *p = bounce_buf + erendline*320/4 + 32/4;
- for (i = 239-srendline; i > 0; i--, p += 320/4)
- memset(p, 0, 256);
- }
+ p = bounce_buf + erendline*320/4 + 32/4;
+ for (i = 239-erendline; i > 0; i--, p += 320/4)
+ memset(p, 0, 256);
}
+
+ /* this is called before throttle, so we blit here too */
+ s = (unsigned char *)bounce_buf + 32;
+ d = (unsigned short *)layer_buf;
+
+ switch (Settings.sw_filter)
+ {
+ case SWFILTER_SCALE2X:
+ neon_scale2x_8_16(s, d, pal2, 256, 320, 256*2*2, 240);
+ break;
+ case SWFILTER_EAGLE2X:
+ neon_eagle2x_8_16(s, d, pal2, 256, 320, 256*2*2, 240);
+ break;
+ case SWFILTER_NONE:
+ default:
+ for (i = 0; i < 239; i++, d += 256, s += 320)
+ do_clut(d, s, pal, 256);
+ break;
+ }
+
+ layer_buf = vout_fbdev_flip(layer_fb);
}
void BlitScreen(int skip)
{
- char *s = (char *)bounce_buf + 32;
- short *d = (short *)layer_buf;
- int i;
+}
- if (skip)
- return;
+/* throttle */
+extern uint8 PAL;
+extern int FSkip;
+static struct timeval tv_expect;
+static int skip_count = 0;
+static int us_interval, us_interval1024;
+#define MAX_LAG_FRAMES 3
- for (i = 0; i < 239; i++, d += 256, s += 320)
- do_clut(d, s, pal, 256);
-
- layer_buf = vout_fbdev_flip(layer_fb);
+void RefreshThrottleFPS(void)
+{
+ skip_count = 0;
+ us_interval = PAL ? 20000 : 16667;
+ us_interval1024 = PAL ? 20000*1024 : 17066667;
+
+ vout_fbdev_wait_vsync(layer_fb);
+ gettimeofday(&tv_expect, 0);
+ tv_expect.tv_usec *= 1024;
}
+void SpeedThrottle(void)
+{
+ struct timeval now;
+ int diff;
+
+ gettimeofday(&now, 0);
+
+ // usec*1024 units to prevent drifting
+ tv_expect.tv_usec += us_interval1024;
+ if (tv_expect.tv_usec > 1000000 * 1024) {
+ tv_expect.tv_usec -= 1000000 * 1024;
+ tv_expect.tv_sec++;
+ }
+
+ diff = (tv_expect.tv_sec - now.tv_sec) * 1000000 +
+ (tv_expect.tv_usec >> 10) - now.tv_usec;
+
+ if (Settings.frameskip >= 0)
+ {
+ if (skip_count >= Settings.frameskip)
+ skip_count = 0;
+ else {
+ skip_count++;
+ FSkip = 1;
+ }
+ }
+ else
+ {
+ FSkip = diff < -us_interval;
+ }
+
+ if (diff > MAX_LAG_FRAMES * us_interval
+ || diff < -MAX_LAG_FRAMES * us_interval)
+ {
+ //printf("reset %d\n", diff);
+ RefreshThrottleFPS();
+ return;
+ }
+ else if (diff > us_interval)
+ {
+ usleep(diff - us_interval);
+ }
+}
+
+/* called just before emulation */
void platform_apply_config(void)
{
+ static int old_layer_w, old_layer_h;
+ int fb_w = 256, fb_h = 240;
+ float mult;
+
+ if (Settings.sw_filter == SWFILTER_SCALE2X
+ || Settings.sw_filter == SWFILTER_EAGLE2X)
+ {
+ fb_w *= 2;
+ fb_h *= 2;
+ }
+
+ if (fb_w != old_layer_w || fb_h != old_layer_h)
+ {
+ layer_buf = vout_fbdev_resize(layer_fb, fb_w, fb_h, 16,
+ 0, 0, 0, 0, 3);
+ if (layer_buf == NULL) {
+ fprintf(stderr, "fatal: mode change %dx%x -> %dx%d failed\n",
+ old_layer_w, old_layer_h, fb_w, fb_h);
+ exit(1);
+ }
+
+ old_layer_w = fb_w;
+ old_layer_h = fb_h;
+ }
+
+ switch (Settings.scaling)
+ {
+ case SCALING_1X:
+ g_layer_w = fb_w;
+ g_layer_h = fb_h;
+ break;
+ case SCALING_PROPORTIONAL:
+ // assumes screen width > height
+ mult = (float)g_menuscreen_h / fb_h;
+ g_layer_w = (int)(fb_w * mult);
+ g_layer_h = g_menuscreen_h;
+ break;
+ case SCALING_FULLSCREEN:
+ g_layer_w = g_menuscreen_w;
+ g_layer_h = g_menuscreen_h;
+ break;
+ case SCALING_4_3:
+ default:
+ g_layer_w = g_menuscreen_h * 4 / 3;
+ g_layer_h = g_menuscreen_h;
+ break;
+ }
+ if (g_layer_w > g_menuscreen_w)
+ g_layer_w = g_menuscreen_w;
+ if (g_layer_h > g_menuscreen_h)
+ g_layer_h = g_menuscreen_h;
+ g_layer_x = g_menuscreen_w / 2 - g_layer_w / 2;
+ g_layer_y = g_menuscreen_h / 2 - g_layer_h / 2;
+
+ plat_target_set_filter(Settings.hw_filter);
+ plat_target_set_lcdrate(PAL);
+
+ // adjust since we run at 60Hz, and real NES doesn't
+ FCEUI_Sound(Settings.sound_rate + Settings.sound_rate / 980);
+
+ omap_enable_layer(1);
}
void platform_set_volume(int val)
void plat_video_menu_enter(int is_rom_loaded)
{
+ unsigned short *d = g_menubg_src_ptr;
+ unsigned char *s, *sr = (void *)bounce_buf;
+ int u, v = 240 / 2;
+ int x, y, w;
+
omap_enable_layer(0);
+
+ if (!fceugi)
+ return;
+
+ for (y = 0; y < g_menuscreen_h; y++)
+ {
+ s = sr + v * 320 + 32;
+ u = 256 / 2;
+ for (x = 0; x < g_menuscreen_w; )
+ {
+ w = g_menuscreen_w - x;
+ if (w > 256 - u)
+ w = 256 - u;
+ do_clut(d + x, s + u, pal, w);
+ u = (u + w) & 255;
+ x += w;
+ }
+ d += x;
+ v++;
+ if (v > erendlinev[PAL])
+ v = srendlinev[PAL];
+ }
}
void plat_video_menu_begin(void)
{
memset(g_menuscreen_ptr, 0, g_menuscreen_w * g_menuscreen_h * 2);
g_menuscreen_ptr = vout_fbdev_flip(main_fb);
-
- omap_enable_layer(1);
+ // layer enabled in platform_apply_config()
}
char *DriverUsage="";
strcpy(BaseDirectory, "fceultra");
}
+void platform_get_def_rompath(char *buf, int size)
+{
+ strcpy(buf, "/media");
+}
+
void platform_finish(void)
{
omap_enable_layer(0);