From 636d5f257c3e5bafba99ddbdc385a289e12c9deb Mon Sep 17 00:00:00 2001
From: notaz <notasas@gmail.com>
Date: Sun, 23 Jun 2013 02:28:52 +0300
Subject: [PATCH] starting SDL port, refactoring

---
 .gitignore                   |   3 +
 Makefile                     |  12 +-
 platform/common/emu.c        |  64 +++++----
 platform/common/emu.h        |   5 +-
 platform/common/input_pico.h |  14 ++
 platform/common/main.c       |  27 ++--
 platform/common/menu_pico.c  |  62 ++++++---
 platform/common/menu_pico.h  |   1 +
 platform/common/mp3_dummy.c  |  23 ++++
 platform/common/plat_sdl.c   | 246 +++++++++++++++++++++++++++++++++++
 platform/libpicofe           |   2 +-
 platform/linux/emu.c         |  14 --
 12 files changed, 393 insertions(+), 80 deletions(-)
 create mode 100644 platform/common/mp3_dummy.c
 create mode 100644 platform/common/plat_sdl.c

diff --git a/.gitignore b/.gitignore
index 3c582d09..81907193 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,3 +8,6 @@ config.log
 cpu/musashi/m68kmake
 cpu/musashi/m68kops.c
 cpu/musashi/m68kops.h
+skin
+config.cfg
+srm/
diff --git a/Makefile b/Makefile
index a5039809..4cd832f2 100644
--- a/Makefile
+++ b/Makefile
@@ -26,20 +26,26 @@ CFLAGS += -I.
 CFLAGS += -Iplatform/linux/
 LDLIBS += -lm -lpng
 
+# tmp
+CFLAGS += `sdl-config --cflags`
+LDLIBS += `sdl-config --libs`
+
 all: PicoDrive
 
 # frontend
-OBJS += platform/linux/io.o platform/linux/emu.o platform/linux/blit.o \
+OBJS += platform/linux/emu.o platform/linux/blit.o \
 	platform/linux/log_io.o
 
 # common
 OBJS += platform/common/main.o platform/common/emu.o platform/common/menu_pico.o \
-	platform/common/config.o
+	platform/common/config.o platform/common/plat_sdl.o \
+	platform/common/mp3_dummy.o
 
 # libpicofe
 OBJS += platform/libpicofe/input.o platform/libpicofe/readpng.o \
 	platform/libpicofe/fonts.o platform/libpicofe/linux/in_evdev.o \
-	platform/libpicofe/linux/plat.o platform/libpicofe/linux/sndout_oss.o
+	platform/libpicofe/linux/plat.o platform/libpicofe/linux/sndout_oss.o \
+	platform/libpicofe/plat_sdl.o platform/libpicofe/in_sdl.o
 
 OBJS += platform/libpicofe/plat_dummy.o
 
diff --git a/platform/common/emu.c b/platform/common/emu.c
index 8066796b..76765a4d 100644
--- a/platform/common/emu.c
+++ b/platform/common/emu.c
@@ -47,8 +47,7 @@ int engineState = PGS_Menu;
 
 /* tmp buff to reduce stack usage for plats with small stack */
 static char static_buff[512];
-/* TODO: len checking */
-char rom_fname_reload[512];
+const char *rom_fname_reload;
 char rom_fname_loaded[512];
 int rom_loaded = 0;
 int reset_timing = 0;
@@ -147,7 +146,7 @@ static const char * const biosfiles_us[] = { "us_scd1_9210", "us_scd2_9306", "Se
 static const char * const biosfiles_eu[] = { "eu_mcd1_9210", "eu_mcd2_9306", "eu_mcd2_9303"   };
 static const char * const biosfiles_jp[] = { "jp_mcd1_9112", "jp_mcd1_9111" };
 
-static int find_bios(int region, char **bios_file)
+static int find_bios(int region, const char **bios_file)
 {
 	int i, count;
 	const char * const *files;
@@ -484,21 +483,28 @@ static void system_announce(void)
 	emu_status_msg("%s %s / %dFPS%s", tv_standard, sys_name, fps, extra);
 }
 
-// note: this function might mangle rom_fname
 // XXX: portions of this code should move to pico/
-int emu_reload_rom(char *rom_fname)
+int emu_reload_rom(const char *rom_fname_in)
 {
 	unsigned int rom_size = 0;
-	char *used_rom_name = rom_fname;
+	const char *used_rom_name = NULL;
+	char *rom_fname = NULL;
 	unsigned char *rom_data = NULL;
 	char ext[5];
 	pm_file *rom = NULL;
 	int cd_state = CIT_NOT_CD;
 	int ret, media_type, cd_region;
 	int cfg_loaded = 0, bad_rom = 0;
+	int menu_romload_started = 0;
+	int retval = 0;
 
-	lprintf("emu_ReloadRom(%s)\n", rom_fname);
+	lprintf("emu_ReloadRom(%s)\n", rom_fname_in);
 
+	rom_fname = strdup(rom_fname_in);
+	if (rom_fname == NULL)
+		return 0;
+
+	used_rom_name = rom_fname;
 	get_ext(rom_fname, ext);
 
 	// early cleanup
@@ -515,7 +521,7 @@ int emu_reload_rom(char *rom_fname)
 		FILE *movie_file = fopen(rom_fname, "rb");
 		if (!movie_file) {
 			menu_update_msg("Failed to open movie.");
-			return 0;
+			goto out;
 		}
 		fseek(movie_file, 0, SEEK_END);
 		movie_size = ftell(movie_file);
@@ -523,24 +529,24 @@ int emu_reload_rom(char *rom_fname)
 		if (movie_size < 64+3) {
 			menu_update_msg("Invalid GMV file.");
 			fclose(movie_file);
-			return 0;
+			goto out;
 		}
 		movie_data = malloc(movie_size);
 		if (movie_data == NULL) {
 			menu_update_msg("low memory.");
 			fclose(movie_file);
-			return 0;
+			goto out;
 		}
 		dummy = fread(movie_data, 1, movie_size, movie_file);
 		fclose(movie_file);
 		if (strncmp((char *)movie_data, "Gens Movie TEST", 15) != 0) {
 			menu_update_msg("Invalid GMV file.");
-			return 0;
+			goto out;
 		}
 		dummy = try_rfn_cut(rom_fname) || try_rfn_cut(rom_fname);
 		if (!dummy) {
 			menu_update_msg("Could't find a ROM for movie.");
-			return 0;
+			goto out;
 		}
 		get_ext(rom_fname, ext);
 		lprintf("gmv loaded for %s\n", rom_fname);
@@ -552,7 +558,7 @@ int emu_reload_rom(char *rom_fname)
 		dummy = try_rfn_cut(rom_fname) || try_rfn_cut(rom_fname);
 		if (!dummy) {
 			menu_update_msg("Could't find a ROM to patch.");
-			return 0;
+			goto out;
 		}
 		get_ext(rom_fname, ext);
 	}
@@ -560,7 +566,7 @@ int emu_reload_rom(char *rom_fname)
 	media_type = detect_media(rom_fname);
 	if (media_type == PM_BAD) {
 		menu_update_msg("Not a ROM/CD img selected.");
-		return 0;
+		goto out;
 	}
 
 	shutdown_MCD();
@@ -588,14 +594,14 @@ int emu_reload_rom(char *rom_fname)
 					(cd_region == 8 ? "EU" : "JAP") : "USA");
 			}
 			if (!find_bios(cd_region, &used_rom_name))
-				return 0;
+				goto out;
 
 			get_ext(used_rom_name, ext);
 			PicoAHW |= PAHW_MCD;
 		}
 		else {
 			menu_update_msg("Invalid CD image");
-			return 0;
+			goto out;
 		}
 	}
 	else if (media_type == PM_MARK3) {
@@ -606,10 +612,11 @@ int emu_reload_rom(char *rom_fname)
 	rom = pm_open(used_rom_name);
 	if (rom == NULL) {
 		menu_update_msg("Failed to open ROM");
-		return 0;
+		goto out;
 	}
 
 	menu_romload_prepare(used_rom_name); // also CD load
+	menu_romload_started = 1;
 	used_rom_name = NULL; // uses static_buff
 
 	ret = PicoCartLoad(rom, &rom_data, &rom_size, (PicoAHW & PAHW_SMS) ? 1 : 0);
@@ -618,7 +625,7 @@ int emu_reload_rom(char *rom_fname)
 		if      (ret == 2) menu_update_msg("Out of memory");
 		else if (ret == 3) menu_update_msg("Read failed");
 		else               menu_update_msg("PicoCartLoad() failed.");
-		goto fail;
+		goto out;
 	}
 
 	// detect wrong files
@@ -634,7 +641,7 @@ int emu_reload_rom(char *rom_fname)
 
 	if (bad_rom) {
 		menu_update_msg("Bad ROM detected.");
-		goto fail;
+		goto out;
 	}
 
 	// load config for this ROM (do this before insert to get correct region)
@@ -648,7 +655,7 @@ int emu_reload_rom(char *rom_fname)
 	emu_make_path(static_buff, "carthw.cfg", sizeof(static_buff));
 	if (PicoCartInsert(rom_data, rom_size, static_buff)) {
 		menu_update_msg("Failed to load ROM.");
-		goto fail;
+		goto out;
 	}
 
 	// insert CD if it was detected
@@ -658,11 +665,12 @@ int emu_reload_rom(char *rom_fname)
 			PicoCartUnload();
 			rom_data = NULL; // freed by unload
 			menu_update_msg("Insert_CD() failed, invalid CD image?");
-			goto fail;
+			goto out;
 		}
 	}
 
 	menu_romload_end();
+	menu_romload_started = 0;
 
 	if (PicoPatches) {
 		PicoPatchPrepare();
@@ -702,13 +710,14 @@ int emu_reload_rom(char *rom_fname)
 	if (currentConfig.EmuOpt & EOPT_EN_SRAM)
 		emu_save_load_game(1, 1);
 
-	return 1;
-
-fail:
-	if (rom_data)
+	retval = 1;
+out:
+	if (retval == 0 && rom_data)
 		free(rom_data);
-	menu_romload_end();
-	return 0;
+	if (menu_romload_started)
+		menu_romload_end();
+	free(rom_fname);
+	return retval;
 }
 
 int emu_swap_cd(const char *fname)
@@ -1466,6 +1475,7 @@ void emu_loop(void)
 	if (PicoAHW & PAHW_MCD)
 		PicoCDBufferInit();
 
+	plat_video_loop_prepare();
 	pemu_loop_prep();
 
 	/* number of ticks per frame */
diff --git a/platform/common/emu.h b/platform/common/emu.h
index ae8e4d78..faa4868e 100644
--- a/platform/common/emu.h
+++ b/platform/common/emu.h
@@ -95,7 +95,7 @@ extern int reset_timing;
 extern int pico_pen_x, pico_pen_y;
 extern int pico_inp_mode;
 
-extern char rom_fname_reload[512];		// ROM to try loading on next PGS_ReloadRom
+extern const char *rom_fname_reload;		// ROM to try loading on next PGS_ReloadRom
 extern char rom_fname_loaded[512];		// currently loaded ROM filename
 
 // engine states
@@ -125,7 +125,7 @@ void  emu_init(void);
 void  emu_finish(void);
 void  emu_loop(void);
 
-int   emu_reload_rom(char *rom_fname);
+int   emu_reload_rom(const char *rom_fname_in);
 int   emu_swap_cd(const char *fname);
 int   emu_save_load_game(int load, int sram);
 void  emu_reset_game(void);
@@ -177,6 +177,7 @@ void plat_status_msg_busy_next(const char *msg);
 void plat_status_msg_clear(void);
 
 void plat_video_toggle_renderer(int change, int menu_call);
+void plat_video_loop_prepare(void);
 
 void plat_update_volume(int has_changed, int is_up);
 
diff --git a/platform/common/input_pico.h b/platform/common/input_pico.h
index fbf328ab..c2730b52 100644
--- a/platform/common/input_pico.h
+++ b/platform/common/input_pico.h
@@ -1,6 +1,20 @@
 #ifndef INCLUDE_c48097f3ff2a6a9af1cce8fd7a9b3f0c
 #define INCLUDE_c48097f3ff2a6a9af1cce8fd7a9b3f0c 1
 
+/* gamepad - MXYZ SACB RLDU */
+#define GBTN_UP         0
+#define GBTN_DOWN       1
+#define GBTN_LEFT       2
+#define GBTN_RIGHT      3
+#define GBTN_B          4
+#define GBTN_C          5
+#define GBTN_A          6
+#define GBTN_START      7
+#define GBTN_Z          8
+#define GBTN_Y          9
+#define GBTN_X          10
+#define GBTN_MODE       11
+
 /* ui events */
 #define PEVB_VOL_DOWN   30
 #define PEVB_VOL_UP     29
diff --git a/platform/common/main.c b/platform/common/main.c
index 5a0287ed..968da0d3 100644
--- a/platform/common/main.c
+++ b/platform/common/main.c
@@ -49,24 +49,25 @@ void parse_cmd_line(int argc, char *argv[])
 				break;
 			}
 		} else {
-			/* External Frontend: ROM Name */
-			FILE *f;
-			strncpy(rom_fname_reload, argv[x], sizeof(rom_fname_reload));
-			rom_fname_reload[sizeof(rom_fname_reload) - 1] = 0;
-			f = fopen(rom_fname_reload, "rb");
-			if (f) fclose(f);
-			else unrecognized = 1;
-			engineState = PGS_ReloadRom;
+			FILE *f = fopen(argv[x], "rb");
+			if (f) {
+				fclose(f);
+				rom_fname_reload = argv[x];
+				engineState = PGS_ReloadRom;
+			}
+			else
+				unrecognized = 1;
 			break;
 		}
 	}
 
 	if (unrecognized) {
-		printf("\n\n\nPicoDrive v" VERSION " (c) notaz, 2006-2009\n");
+		printf("\n\n\nPicoDrive v" VERSION " (c) notaz, 2006-2009,2013\n");
 		printf("usage: %s [options] [romfile]\n", argv[0]);
 		printf("options:\n"
 			" -config <file>    use specified config file instead of default 'config.cfg'\n"
-			" -loadstate <num>  if ROM is specified, try loading slot <num>\n");
+			" -loadstate <num>  if ROM is specified, try loading savestate slot <num>\n");
+		exit(1);
 	}
 }
 
@@ -75,12 +76,13 @@ int main(int argc, char *argv[])
 {
 	g_argv = argv;
 
-	//plat_early_init();
+	plat_early_init();
 
 	in_init();
-	in_probe();
+	//in_probe();
 
 	plat_target_init();
+	plat_init();
 
 	emu_prep_defconfig(); // depends on input
 	emu_read_config(NULL, 0);
@@ -145,6 +147,7 @@ int main(int argc, char *argv[])
 	endloop:
 
 	emu_finish();
+	plat_finish();
 	plat_target_finish();
 
 	return 0;
diff --git a/platform/common/menu_pico.c b/platform/common/menu_pico.c
index 47f00236..62b6b803 100644
--- a/platform/common/menu_pico.c
+++ b/platform/common/menu_pico.c
@@ -20,8 +20,12 @@
 #define REVISION "0"
 
 static const char *rom_exts[] = {
-	"zip", "bin", "smd", "gen",
-	"iso", "cso", "cue", NULL
+	"zip",
+	"bin", "smd", "gen",
+	"iso", "cso", "cue",
+	"32x",
+	"sms",
+	NULL
 };
 
 // rrrr rggg gggb bbbb
@@ -56,8 +60,21 @@ static void menu_enter(int is_rom_loaded)
 {
 	if (is_rom_loaded)
 	{
+		int w = g_screen_width, h = g_screen_height;
+		short *src, *dst;
+
+		if (w > g_menuscreen_w)
+			w = g_menuscreen_w;
+		if (h > g_menuscreen_h)
+			h = g_menuscreen_h;
+		src = (short *)g_menubg_src_ptr;
+		dst = (short *)g_menubg_ptr +
+			(g_menuscreen_h / 2 - h / 2) * g_menuscreen_w +
+			(g_menuscreen_w / 2 - w / 2);
+
 		// darken the active framebuffer
-		menu_darken_bg(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h, 1);
+		for (; h > 0; dst += g_menuscreen_w, src += g_screen_width, h--)
+			menu_darken_bg(dst, src, w, 1);
 	}
 	else
 	{
@@ -468,9 +485,12 @@ static int menu_loop_adv_options(int id, int keys)
 
 // ------------ gfx options menu ------------
 
+static const char *men_dummy[] = { NULL };
+
 static menu_entry e_menu_gfx_options[] =
 {
-	mee_enum("Renderer", MA_OPT_RENDERER, currentConfig.renderer, renderer_names),
+	mee_enum("Video output mode", MA_OPT_VOUT_MODE, plat_target.vout_method, men_dummy),
+	mee_enum("Renderer",          MA_OPT_RENDERER, currentConfig.renderer, renderer_names),
 	MENU_OPTIONS_GFX
 	mee_end,
 };
@@ -866,21 +886,6 @@ static const char credits[] =
 	" Lordus, Exophase, Rokas,\n"
 	" Nemesis, Tasco Deluxe";
 
-static const char *romsel_run(void)
-{
-	const char *ret;
-	char *sel_name;
-
-	sel_name = malloc(sizeof(rom_fname_loaded));
-	if (sel_name == NULL)
-		return NULL;
-	strcpy(sel_name, rom_fname_loaded);
-
-	ret = menu_loop_romsel(sel_name, sizeof(rom_fname_loaded), rom_exts, NULL);
-	free(sel_name);
-	return ret;
-}
-
 static int main_menu_handler(int id, int keys)
 {
 	const char *ret_name;
@@ -906,9 +911,12 @@ static int main_menu_handler(int id, int keys)
 		}
 		break;
 	case MA_MAIN_LOAD_ROM:
-		ret_name = romsel_run();
+		rom_fname_reload = NULL;
+		ret_name = menu_loop_romsel(rom_fname_loaded,
+			sizeof(rom_fname_loaded), rom_exts, NULL);
 		if (ret_name != NULL) {
 			lprintf("selected file: %s\n", ret_name);
+			rom_fname_reload = ret_name;
 			engineState = PGS_ReloadRom;
 			return 1;
 		}
@@ -977,6 +985,7 @@ void menu_loop(void)
 	}
 
 	in_set_config_int(0, IN_CFG_BLOCKING, 0);
+	plat_video_menu_leave();
 }
 
 // --------- CD tray close menu ----------
@@ -985,10 +994,13 @@ static int mh_tray_load_cd(int id, int keys)
 {
 	const char *ret_name;
 
-	ret_name = romsel_run();
+	rom_fname_reload = NULL;
+	ret_name = menu_loop_romsel(rom_fname_loaded,
+			sizeof(rom_fname_loaded), rom_exts, NULL);
 	if (ret_name == NULL)
 		return 0;
 
+	rom_fname_reload = ret_name;
 	engineState = PGS_RestartRun;
 	return emu_swap_cd(ret_name);
 }
@@ -1024,6 +1036,7 @@ int menu_loop_tray(void)
 
 	while (in_menu_wait_any(NULL, 50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK));
 	in_set_config_int(0, IN_CFG_BLOCKING, 0);
+	plat_video_menu_leave();
 
 	return ret;
 }
@@ -1090,5 +1103,12 @@ menu_entry *me_list_get_next(void)
 
 void menu_init(void)
 {
+	int i;
+
 	menu_init_base();
+
+	i = me_id2offset(e_menu_gfx_options, MA_OPT_VOUT_MODE);
+	e_menu_gfx_options[i].data = plat_target.vout_methods;
+	me_enable(e_menu_gfx_options, MA_OPT_VOUT_MODE,
+		plat_target.vout_methods != NULL);
 }
diff --git a/platform/common/menu_pico.h b/platform/common/menu_pico.h
index 31cee72d..4302ded9 100644
--- a/platform/common/menu_pico.h
+++ b/platform/common/menu_pico.h
@@ -39,6 +39,7 @@ typedef enum
 	MA_OPT_INTERLACED,	/* giz */
 	MA_OPT_ROTATION,	/* uiq */
 	MA_OPT_TEARING_FIX,	/* wiz */
+	MA_OPT_VOUT_MODE,
 	MA_OPT2_GAMMA,
 	MA_OPT2_A_SN_GAMMA,
 	MA_OPT2_DBLBUFF,	/* giz */
diff --git a/platform/common/mp3_dummy.c b/platform/common/mp3_dummy.c
new file mode 100644
index 00000000..a76caf97
--- /dev/null
+++ b/platform/common/mp3_dummy.c
@@ -0,0 +1,23 @@
+/*
+ * dummy/none mp3 code
+ * (C) notaz, 2013
+ *
+ * This work is licensed under the terms of MAME license.
+ * See COPYING file in the top-level directory.
+ */
+
+#include "mp3.h"
+#include <pico/pico.h>
+
+int mp3_get_bitrate(void *f_, int len)
+{
+	return -1;
+}
+
+void mp3_start_play(void *f_, int pos)
+{
+}
+
+void mp3_update(int *buffer, int length, int stereo)
+{
+}
diff --git a/platform/common/plat_sdl.c b/platform/common/plat_sdl.c
new file mode 100644
index 00000000..28b412d2
--- /dev/null
+++ b/platform/common/plat_sdl.c
@@ -0,0 +1,246 @@
+/*
+ * PicoDrive
+ * (C) notaz, 2013
+ *
+ * This work is licensed under the terms of MAME license.
+ * See COPYING file in the top-level directory.
+ */
+
+#include <stdio.h>
+
+#include "../libpicofe/input.h"
+#include "../libpicofe/plat_sdl.h"
+#include "../libpicofe/in_sdl.h"
+#include "../libpicofe/gl.h"
+#include "emu.h"
+#include "menu_pico.h"
+#include "input_pico.h"
+#include "version.h"
+
+// FIXME: these 2 shouldn't be here
+static unsigned char PicoDraw2FB_[(8+320) * (8+240+8)];
+unsigned char *PicoDraw2FB = PicoDraw2FB_;
+
+static void *shadow_fb;
+
+static const struct in_default_bind in_sdl_defbinds[] = {
+	{ SDLK_UP,     IN_BINDTYPE_PLAYER12, GBTN_UP },
+	{ SDLK_DOWN,   IN_BINDTYPE_PLAYER12, GBTN_DOWN },
+	{ SDLK_LEFT,   IN_BINDTYPE_PLAYER12, GBTN_LEFT },
+	{ SDLK_RIGHT,  IN_BINDTYPE_PLAYER12, GBTN_RIGHT },
+	{ SDLK_z,      IN_BINDTYPE_PLAYER12, GBTN_A },
+	{ SDLK_x,      IN_BINDTYPE_PLAYER12, GBTN_B },
+	{ SDLK_c,      IN_BINDTYPE_PLAYER12, GBTN_C },
+	{ SDLK_a,      IN_BINDTYPE_PLAYER12, GBTN_X },
+	{ SDLK_s,      IN_BINDTYPE_PLAYER12, GBTN_Y },
+	{ SDLK_d,      IN_BINDTYPE_PLAYER12, GBTN_Z },
+	{ SDLK_RETURN, IN_BINDTYPE_PLAYER12, GBTN_START },
+	{ SDLK_f,      IN_BINDTYPE_PLAYER12, GBTN_MODE },
+	{ SDLK_ESCAPE, IN_BINDTYPE_EMU, PEVB_MENU },
+	{ SDLK_F1,     IN_BINDTYPE_EMU, PEVB_STATE_SAVE },
+	{ SDLK_F2,     IN_BINDTYPE_EMU, PEVB_STATE_LOAD },
+	{ SDLK_F3,     IN_BINDTYPE_EMU, PEVB_SSLOT_PREV },
+	{ SDLK_F4,     IN_BINDTYPE_EMU, PEVB_SSLOT_NEXT },
+	{ SDLK_F5,     IN_BINDTYPE_EMU, PEVB_SWITCH_RND },
+	{ SDLK_F6,     IN_BINDTYPE_EMU, PEVB_PICO_PPREV },
+	{ SDLK_F7,     IN_BINDTYPE_EMU, PEVB_PICO_PNEXT },
+	{ SDLK_F8,     IN_BINDTYPE_EMU, PEVB_PICO_SWINP },
+	{ SDLK_BACKSPACE, IN_BINDTYPE_EMU, PEVB_FF },
+	{ 0, 0, 0 }
+};
+
+/* YUV stuff */
+static int yuv_ry[32], yuv_gy[32], yuv_by[32];
+static unsigned char yuv_u[32 * 2], yuv_v[32 * 2];
+
+void bgr_to_uyvy_init(void)
+{
+  int i, v;
+
+  /* init yuv converter:
+    y0 = (int)((0.299f * r0) + (0.587f * g0) + (0.114f * b0));
+    y1 = (int)((0.299f * r1) + (0.587f * g1) + (0.114f * b1));
+    u = (int)(8 * 0.565f * (b0 - y0)) + 128;
+    v = (int)(8 * 0.713f * (r0 - y0)) + 128;
+  */
+  for (i = 0; i < 32; i++) {
+    yuv_ry[i] = (int)(0.299f * i * 65536.0f + 0.5f);
+    yuv_gy[i] = (int)(0.587f * i * 65536.0f + 0.5f);
+    yuv_by[i] = (int)(0.114f * i * 65536.0f + 0.5f);
+  }
+  for (i = -32; i < 32; i++) {
+    v = (int)(8 * 0.565f * i) + 128;
+    if (v < 0)
+      v = 0;
+    if (v > 255)
+      v = 255;
+    yuv_u[i + 32] = v;
+    v = (int)(8 * 0.713f * i) + 128;
+    if (v < 0)
+      v = 0;
+    if (v > 255)
+      v = 255;
+    yuv_v[i + 32] = v;
+  }
+}
+
+void rgb565_to_uyvy(void *d, const void *s, int pixels)
+{
+  unsigned int *dst = d;
+  const unsigned short *src = s;
+  const unsigned char *yu = yuv_u + 32;
+  const unsigned char *yv = yuv_v + 32;
+  int r0, g0, b0, r1, g1, b1;
+  int y0, y1, u, v;
+
+  for (; pixels > 0; src += 2, dst++, pixels -= 2)
+  {
+    r0 = (src[0] >> 11) & 0x1f;
+    g0 = (src[0] >> 6) & 0x1f;
+    b0 =  src[0] & 0x1f;
+    r1 = (src[1] >> 11) & 0x1f;
+    g1 = (src[1] >> 6) & 0x1f;
+    b1 =  src[1] & 0x1f;
+    y0 = (yuv_ry[r0] + yuv_gy[g0] + yuv_by[b0]) >> 16;
+    y1 = (yuv_ry[r1] + yuv_gy[g1] + yuv_by[b1]) >> 16;
+    u = yu[b0 - y0];
+    v = yv[r0 - y0];
+    // valid Y range seems to be 16..235
+    y0 = 16 + 219 * y0 / 31;
+    y1 = 16 + 219 * y1 / 31;
+
+    *dst = (y1 << 24) | (v << 16) | (y0 << 8) | u;
+  }
+}
+
+void plat_video_flip(void)
+{
+	if (plat_sdl_overlay != NULL) {
+		SDL_Rect dstrect =
+			{ 0, 0, plat_sdl_screen->w, plat_sdl_screen->h };
+
+		SDL_LockYUVOverlay(plat_sdl_overlay);
+		rgb565_to_uyvy(plat_sdl_overlay->pixels[0], shadow_fb,
+				g_screen_width * g_screen_height);
+		SDL_UnlockYUVOverlay(plat_sdl_overlay);
+		SDL_DisplayYUVOverlay(plat_sdl_overlay, &dstrect);
+	}
+	else if (plat_sdl_gl_active) {
+		gl_flip(shadow_fb, g_screen_width, g_screen_height);
+	}
+	else {
+		// XXX: no locking, but should be fine with SDL_SWSURFACE?
+		SDL_Flip(plat_sdl_screen);
+		g_screen_ptr = plat_sdl_screen->pixels;
+	}
+}
+
+void plat_video_wait_vsync(void)
+{
+}
+
+void plat_video_menu_enter(int is_rom_loaded)
+{
+	plat_sdl_change_video_mode(g_menuscreen_w, g_menuscreen_h, 0);
+}
+
+void plat_video_menu_begin(void)
+{
+	if (plat_sdl_overlay != NULL || plat_sdl_gl_active) {
+		g_menuscreen_ptr = shadow_fb;
+	}
+	else {
+		SDL_LockSurface(plat_sdl_screen);
+		g_menuscreen_ptr = plat_sdl_screen->pixels;
+	}
+}
+
+void plat_video_menu_end(void)
+{
+	if (plat_sdl_overlay != NULL) {
+		SDL_Rect dstrect =
+			{ 0, 0, plat_sdl_screen->w, plat_sdl_screen->h };
+
+		SDL_LockYUVOverlay(plat_sdl_overlay);
+		rgb565_to_uyvy(plat_sdl_overlay->pixels[0], shadow_fb,
+				g_menuscreen_w * g_menuscreen_h);
+		SDL_UnlockYUVOverlay(plat_sdl_overlay);
+
+		SDL_DisplayYUVOverlay(plat_sdl_overlay, &dstrect);
+	}
+	else if (plat_sdl_gl_active) {
+		gl_flip(g_menuscreen_ptr, g_menuscreen_w, g_menuscreen_h);
+	}
+	else {
+		SDL_UnlockSurface(plat_sdl_screen);
+		SDL_Flip(plat_sdl_screen);
+	}
+	g_menuscreen_ptr = NULL;
+
+}
+
+void plat_video_menu_leave(void)
+{
+}
+
+void plat_video_loop_prepare(void)
+{
+	plat_sdl_change_video_mode(g_screen_width, g_screen_height, 0);
+
+	if (plat_sdl_overlay != NULL || plat_sdl_gl_active) {
+		g_screen_ptr = shadow_fb;
+	}
+	else {
+		SDL_LockSurface(plat_sdl_screen);
+		g_screen_ptr = plat_sdl_screen->pixels;
+	}
+}
+
+void plat_early_init(void)
+{
+}
+
+void plat_init(void)
+{
+	int shadow_size;
+	int ret;
+
+	ret = plat_sdl_init();
+	if (ret != 0)
+		exit(1);
+
+	SDL_WM_SetCaption("PicoDrive" VERSION, NULL);
+
+	g_menuscreen_w = plat_sdl_screen->w;
+	g_menuscreen_h = plat_sdl_screen->h;
+	g_menuscreen_ptr = NULL;
+
+	shadow_size = g_menuscreen_w * g_menuscreen_h * 2;
+	if (shadow_size < 320 * 480 * 2)
+		shadow_size = 320 * 480 * 2;
+
+	shadow_fb = malloc(shadow_size);
+	g_menubg_ptr = malloc(shadow_size);
+	if (shadow_fb == NULL || g_menubg_ptr == NULL) {
+		fprintf(stderr, "OOM\n");
+		exit(1);
+	}
+
+	g_screen_width = 320;
+	g_screen_height = 240;
+	g_screen_ptr = shadow_fb;
+
+	in_sdl_init(in_sdl_defbinds, plat_sdl_event_handler);
+	in_probe();
+
+	bgr_to_uyvy_init();
+}
+
+void plat_finish(void)
+{
+	free(shadow_fb);
+	shadow_fb = NULL;
+	free(g_menubg_ptr);
+	g_menubg_ptr = NULL;
+	plat_sdl_finish();
+}
diff --git a/platform/libpicofe b/platform/libpicofe
index 7bf7acb6..20b14308 160000
--- a/platform/libpicofe
+++ b/platform/libpicofe
@@ -1 +1 @@
-Subproject commit 7bf7acb6d60e16e9eaa208761d019c39da396fc0
+Subproject commit 20b143089cc395dbcd51cac516a9e36f4ab6f5ac
diff --git a/platform/linux/emu.c b/platform/linux/emu.c
index 931031b4..591db8a0 100644
--- a/platform/linux/emu.c
+++ b/platform/linux/emu.c
@@ -171,20 +171,6 @@ void plat_video_toggle_renderer(int change, int is_menu)
 	emu_status_msg(renderer_names[currentConfig.renderer]);
 }
 
-void plat_video_menu_enter(int is_rom_loaded)
-{
-}
-
-void plat_video_menu_begin(void)
-{
-	g_menuscreen_ptr = g_screen_ptr;
-}
-
-void plat_video_menu_end(void)
-{
-	plat_video_flip();
-}
-
 void plat_status_msg_clear(void)
 {
 	unsigned short *d = (unsigned short *)g_screen_ptr + g_screen_width * g_screen_height;
-- 
2.39.5