From: kub <derkub@gmail.com>
Date: Mon, 5 Feb 2024 19:14:06 +0000 (+0100)
Subject: fixes for audio and video, add psp-like scaling
X-Git-Tag: v2.00~119^2~4
X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4abc40d56b2f9d6bb3246b8a07925262abc55557;p=picodrive.git

fixes for audio and video, add psp-like scaling
---

diff --git a/platform/common/menu_pico.c b/platform/common/menu_pico.c
index a9fc7900..78dfcc33 100644
--- a/platform/common/menu_pico.c
+++ b/platform/common/menu_pico.c
@@ -72,6 +72,8 @@ static int menu_w, menu_h;
 #include <platform/gp2x/menu.c>
 #elif defined(__PSP__)
 #include <platform/psp/menu.c>
+#elif defined(__PS2__)
+#include <platform/ps2/menu.c>
 #elif defined(PANDORA)
 #include <platform/pandora/menu.c>
 #else
diff --git a/platform/ps2/emu.c b/platform/ps2/emu.c
index 05d7d8dd..a5de6493 100644
--- a/platform/ps2/emu.c
+++ b/platform/ps2/emu.c
@@ -20,7 +20,7 @@
 
 #include <pico/pico_int.h>
 
-#define OSD_FPS_X 220
+#define OSD_FPS_X (gsGlobal->Width - 80)
 
 /* turn black GS Screen */
 #define GS_BLACK GS_SETREG_RGBA(0x00, 0x00, 0x00, 0x80)
@@ -29,6 +29,8 @@
 
 static int osd_buf_cnt, osd_cdleds;
 
+static int out_x, out_y;
+static int out_w, out_h;
 static float hscale, vscale;
 
 static struct in_default_bind in_ps2_defbinds[] =
@@ -51,14 +53,18 @@ static struct in_default_bind in_ps2_defbinds[] =
 const char *renderer_names[] = { "16bit accurate", " 8bit accurate", " 8bit fast", NULL };
 const char *renderer_names32x[] = { "accurate", "faster", "fastest", NULL };
 enum renderer_types { RT_16BIT, RT_8BIT_ACC, RT_8BIT_FAST, RT_COUNT };
+static int is_bg_frame;
 
 static GSGLOBAL *gsGlobal;
 
 static GSTEXTURE *g_menuscreen;
 static GSPRIMUVPOINT *g_menuscreen_vertices;
 
+static GSTEXTURE *g_screens[2];
+static int g_screen_index;
 static GSTEXTURE *g_screen;
 static GSPRIMUVPOINT *g_screen_vertices;
+static u16 *g_screen_palette;
 
 static GSTEXTURE *osd;
 static uint32_t osd_vertices_count;
@@ -72,84 +78,73 @@ static int32_t vsync_callback_id;
 static uint8_t vsync; /* 0 (Disabled), 1 (Enabled), 2 (Dynamic) */
 
 /* sound stuff */
-#define SOUND_BLOCK_SIZE_NTSC (1470*2) // 1024 // 1152
-#define SOUND_BLOCK_SIZE_PAL  (1764*2)
 #define SOUND_BLOCK_COUNT    8
+#define SOUND_BUFFER_SIZE    (2*54000/50*SOUND_BLOCK_COUNT) // max.rate/min.frames
 
-static short __attribute__((aligned(4))) sndBuffer[SOUND_BLOCK_SIZE_PAL*SOUND_BLOCK_COUNT + 54000/50*2];
+static short __attribute__((aligned(4))) sndBuffer[SOUND_BUFFER_SIZE];
 static short *snd_playptr = NULL, *sndBuffer_endptr = NULL;
 static int samples_made = 0, samples_done = 0, samples_block = 0;
 static int sound_thread_exit = 0;
 static int32_t sound_sem = -1;
-static uint8_t stack[0x10000] __attribute__((aligned(16)));
+static uint8_t stack[0x4000] __attribute__((aligned(16)));
 extern void *_gp;
 
 static int mp3_init(void) { return 0; }
 
 static void writeSound(int len)
 {
-	int ret;
-
-    // printf("writeSound, len: %i\n", len);
+	int ret, l;
 
 	PicoIn.sndOut += len / 2;
-	/*if (PicoIn.sndOut > sndBuffer_endptr) {
-		memcpy((int *)(void *)sndBuffer, (int *)endptr, (PicoIn.sndOut - endptr + 1) * 2);
-		PicoIn.sndOut = &sndBuffer[PicoIn.sndOut - endptr];
-		lprintf("mov\n");
-	}
-	else*/
-	if (PicoIn.sndOut > sndBuffer_endptr) lprintf("snd oflow %i!\n", PicoIn.sndOut - sndBuffer_endptr);
-	if (PicoIn.sndOut >= sndBuffer_endptr)
+
+	l = PicoIn.sndOut - sndBuffer;
+	if (l > sizeof(sndBuffer)/2)
+		lprintf("ovfl %d %d\n", len, PicoIn.sndOut - sndBuffer);
+	if (l > samples_block * 6) {
+		sndBuffer_endptr = PicoIn.sndOut;
 		PicoIn.sndOut = sndBuffer;
+	}
+	if (sndBuffer_endptr < PicoIn.sndOut)
+		sndBuffer_endptr = PicoIn.sndOut;
 
 	// signal the snd thread
 	samples_made += len / 2;
-	if (samples_made - samples_done > samples_block*2) {
-		lprintf("signal, %i/%i\n", samples_done, samples_made);
-		ret = SignalSema(sound_sem);
-		//if (ret < 0) lprintf("snd signal ret %08x\n", ret);
-	}
+//	lprintf("signal, %i/%i\n", samples_done, samples_made);
+	ret = SignalSema(sound_sem);
+//	if (ret < 0) lprintf("snd signal ret %08x\n", ret);
 }
 
 static int sound_thread(void *argp)
 {
-	int ret = 0;
-
 	while (!sound_thread_exit)
 	{
+		int ret = 0;
+
 		if (samples_made - samples_done < samples_block) {
 			// wait for data (use at least 2 blocks)
-			//lprintf("sthr: wait... (%i)\n", samples_made - samples_done);
-			while (samples_made - samples_done <= samples_block*2 && !sound_thread_exit) {
-                printf("sthr: WaitSema\n");
+//			lprintf("sthr: wait... (%i)\n", samples_made - samples_done);
+			while (samples_made - samples_done < samples_block*2 && !sound_thread_exit)
 				ret = WaitSema(sound_sem);
-            }
 			if (ret < 0) lprintf("sthr: WaitSema: %i\n", ret);
 			continue;
 		}
+//		lprintf("sthr: got data: %i\n", samples_made - samples_done);
+		short *sndOut = PicoIn.sndOut, *sndEnd = sndBuffer_endptr;
+		int buflen = samples_block * 2;
+		if (sndOut >= snd_playptr)
+			buflen = sndOut - snd_playptr;
+		else	buflen = sndEnd - snd_playptr;
+		if (buflen > samples_block)
+			buflen = samples_block;
+		ret = audsrv_play_audio((char *)snd_playptr, buflen*2);
+//		if (ret != buflen*2 && ret >= 0) lprintf("sthr: play ret: %i, buflen: %i\n", ret, buflen*2);
+		if (ret < 0) lprintf("sthr: play: ret %08x; pos %i/%i\n", ret, samples_done, samples_made);
+
+		samples_done += buflen;
+		snd_playptr  += buflen;
 
-		// lprintf("sthr: got data: %i\n", samples_made - samples_done);
-        int buflen = samples_block * 2;
-        ret = (audsrv_play_audio((char *)snd_playptr, buflen) != buflen) ? -1 : 0;
-        printf("audsrv_play_audio ret: %i, buflen: %i\n", ret, buflen);
-		// ret = sceAudioSRCOutputBlocking(PSP_AUDIO_VOLUME_MAX, snd_playptr);
-
-		samples_done += samples_block;
-		snd_playptr  += samples_block;
 		if (snd_playptr >= sndBuffer_endptr)
 			snd_playptr = sndBuffer;
-		// 1.5 kernel returns 0, newer ones return # of samples queued
-		if (ret < 0)
-			lprintf("sthr: audsrv_play_audio: %08x; pos %i/%i\n", ret, samples_done, samples_made);
-
-		// shouln't happen, but just in case
-		if (samples_made - samples_done >= samples_block*3) {
-			//lprintf("sthr: block skip (%i)\n", samples_made - samples_done);
-			samples_done += samples_block; // skip
-			snd_playptr  += samples_block;
-		}
-
 	}
 
 	lprintf("sthr: exit\n");
@@ -162,36 +157,39 @@ static void sound_init(void)
 	int thid;
 	int ret;
 	ee_sema_t sema;
-    ee_thread_t thread;
-
-    sema.max_count  = 1;
-    sema.init_count = 0;
-    sema.option     = (u32) "sndsem";
-    if ((sound_sem = CreateSema(&sema)) < 0)
-        return;
-
-    thread.func             = &sound_thread;
-    thread.stack            = stack;
-    thread.stack_size       = sizeof(stack);
-    thread.gp_reg           = &_gp;
-    thread.option           = (u32) "sndthread";
-    thread.initial_priority = 0x12;
-    thid = CreateThread(&thread);
-
-    if (thid >= 0) {
+	ee_thread_t thread;
+
+	sema.max_count  = 1;
+	sema.init_count = 0;
+	sema.option     = (u32) "sndsem";
+	if ((sound_sem = CreateSema(&sema)) < 0)
+		return;
+	audsrv_init();
+
+	thread.func             = &sound_thread;
+	thread.stack            = stack;
+	thread.stack_size       = sizeof(stack);
+	thread.gp_reg           = &_gp;
+	thread.option           = (u32) "sndthread";
+	thread.initial_priority = 40;
+	thid = CreateThread(&thread);
+
+	samples_block = 22050/50; // needs to be initialized before thread start
+	if (thid >= 0) {
 		ret = StartThread(thid, NULL);
 		if (ret < 0) lprintf("sound_init: StartThread returned %08x\n", ret);
-	}else {
-        DeleteSema(sound_sem);
+		ChangeThreadPriority(0, 64);
+	} else {
+		DeleteSema(sound_sem);
 		lprintf("CreateThread failed: %i\n", thid);
-    }
+	}
 }
 
 void pemu_sound_start(void) {
-    static int PsndRate_old = 0, PicoOpt_old = 0, pal_old = 0;
+	static int PsndRate_old = 0, PicoOpt_old = 0, pal_old = 0;
 	static int mp3_init_done;
 	int ret, stereo;
-    struct audsrv_fmt_t format;
+	struct audsrv_fmt_t format;
 
 	samples_made = samples_done = 0;
 
@@ -215,49 +213,35 @@ void pemu_sound_start(void) {
 	}
 	stereo=(PicoIn.opt&8)>>3;
 
-	samples_block = Pico.m.pal ? SOUND_BLOCK_SIZE_PAL : SOUND_BLOCK_SIZE_NTSC;
-    printf("samples_block: %i\n", samples_block);
-	if (PicoIn.sndRate <= 22050) samples_block /= 2;
-	sndBuffer_endptr = &sndBuffer[samples_block*SOUND_BLOCK_COUNT];
+	samples_block = PicoIn.sndRate * (stereo ? 2 : 1) / (Pico.m.pal ? 50 : 60);
 
 	lprintf("starting audio: %i, len: %i, stereo: %i, pal: %i, block samples: %i\n",
 			PicoIn.sndRate, Pico.snd.len, stereo, Pico.m.pal, samples_block);
 
 	format.bits = 16;
-    format.freq = PicoIn.sndRate;
-    format.channels = 2;
+	format.freq = PicoIn.sndRate;
+	format.channels = 2;
 	ret = audsrv_set_format(&format);
-    audsrv_set_volume(MAX_VOLUME);
+	audsrv_set_volume(MAX_VOLUME);
 	if (ret < 0) {
 		lprintf("audsrv_set_format() failed: %i\n", ret);
 		emu_status_msg("sound init failed (%i), snd disabled", ret);
 		currentConfig.EmuOpt &= ~EOPT_EN_SOUND;
 	} else {
 		PicoIn.writeSound = writeSound;
-		memset32((int *)(void *)sndBuffer, 0, sizeof(sndBuffer)/4);
-		snd_playptr = sndBuffer_endptr - samples_block;
-		samples_made = samples_block; // send 1 empty block first..
-		PicoIn.sndOut = sndBuffer;
+		snd_playptr = PicoIn.sndOut = sndBuffer_endptr = sndBuffer;
+
 		PsndRate_old = PicoIn.sndRate;
 		PicoOpt_old  = PicoIn.opt;
 		pal_old = Pico.m.pal;
 	}
+	ret = audsrv_play_audio((char *)snd_playptr, 4);
 }
 
 void pemu_sound_stop(void)
 {
-	int i;
-	if (samples_done == 0)
-	{
-		// if no data is written between sceAudioSRCChReserve and sceAudioSRCChRelease calls,
-		// we get a deadlock on next sceAudioSRCChReserve call
-		// so this is yet another workaround:
-		memset32((int *)(void *)sndBuffer, 0, samples_block*4/4);
-		samples_made = samples_block * 3;
-		SignalSema(sound_sem);
-	}
-	plat_sleep_ms(100);
 	samples_made = samples_done = 0;
+	plat_sleep_ms(100);
 	audsrv_stop_audio();
 }
 
@@ -270,192 +254,297 @@ static void sound_deinit(void)
 }
 
 #define is_16bit_mode() \
-	(currentConfig.renderer == RT_16BIT || (PicoIn.AHW & PAHW_32X))
+	(currentConfig.renderer == RT_16BIT || (PicoIn.AHW & PAHW_32X) || is_bg_frame)
 
 static int vsync_handler(void)
 {
-   iSignalSema(vsync_sema_id);
+	iSignalSema(vsync_sema_id);
+
+	ExitHandler();
+	return 0;
+}
+
+/* Copy of gsKit_sync_flip, but without the 'flip' */
+static void gsKit_sync(GSGLOBAL *gsGlobal)
+{
+	if (!gsGlobal->FirstFrame)
+		WaitSema(vsync_sema_id);
+
+	while (PollSema(vsync_sema_id) >= 0);
+}
+
+/* Copy of gsKit_sync_flip, but without the 'sync' */
+static void gsKit_flip(GSGLOBAL *gsGlobal)
+{
+	if (!gsGlobal->FirstFrame)
+	{
+		if (gsGlobal->DoubleBuffering == GS_SETTING_ON)
+		{
+			GS_SET_DISPFB2(gsGlobal->ScreenBuffer[gsGlobal->ActiveBuffer & 1] / 8192,
+					gsGlobal->Width / 64, gsGlobal->PSM, 0, 0);
+
+			gsGlobal->ActiveBuffer ^= 1;
+		}
+	}
+
+	gsKit_setactive(gsGlobal);
+}
+
+static void flipScreen()
+{
+	gsKit_flip(gsGlobal);
 
-   ExitHandler();
-   return 0;
+	gsKit_TexManager_nextFrame(gsGlobal);
 }
 
-static void set_g_menuscreen_values()
+
+static void set_g_menuscreen_values(void)
 {
-    if (g_menuscreen != NULL) {
-        free(g_menuscreen->Mem);
-        free(g_menuscreen);
-        free(g_menubg_ptr);
-        free(g_menuscreen_vertices);
-    }
-    g_menuscreen = (GSTEXTURE *)calloc(1, sizeof(GSTEXTURE));
-    size_t g_menuscreenSize = gsKit_texture_size_ee(gsGlobal->Width, gsGlobal->Height, GS_PSM_CT16);
-    g_menuscreen->Width = gsGlobal->Width;
-    g_menuscreen->Height = gsGlobal->Height;
-    g_menuscreen->PSM = GS_PSM_CT16;
-    g_menuscreen->Mem = malloc(g_menuscreenSize);
+	size_t g_menuscreenSize = gsKit_texture_size_ee(gsGlobal->Width, gsGlobal->Height, GS_PSM_CT16);
 
-    g_menubg_ptr = (uint8_t *)malloc(g_menuscreenSize);
+	g_menuscreen = (GSTEXTURE *)calloc(1, sizeof(GSTEXTURE));
+	g_menuscreen->Mem = malloc(g_menuscreenSize);
+	g_menuscreen_vertices = (GSPRIMUVPOINT *)calloc(2, sizeof(GSPRIMUVPOINT));
 
-    g_menuscreen_w = g_menuscreen->Width;
-    g_menuscreen_h  = g_menuscreen->Height; 
-    g_menuscreen_pp = g_menuscreen->Width;
-    g_menuscreen_ptr = g_menuscreen->Mem;
+	g_menuscreen->Width = gsGlobal->Width;
+	g_menuscreen->Height = gsGlobal->Height;
+	g_menuscreen->PSM = GS_PSM_CT16;
 
-    g_menubg_src_w = g_menuscreen->Width;
-    g_menubg_src_h  = g_menuscreen->Height;
-    g_menubg_src_pp = g_menuscreen->Width;
+	g_menuscreen_w = g_menuscreen->Width;
+	g_menuscreen_h  = g_menuscreen->Height;
+	g_menuscreen_pp = g_menuscreen->Width;
+	g_menuscreen_ptr = g_menuscreen->Mem;
 
-    g_menuscreen_vertices = (GSPRIMUVPOINT *)calloc(2, sizeof(GSPRIMUVPOINT));
-    
-    g_menuscreen_vertices[0].xyz2 = vertex_to_XYZ2(gsGlobal, 0, 0, 2);
+	g_menuscreen_vertices[0].xyz2 = vertex_to_XYZ2(gsGlobal, 0, 0, 2);
 	g_menuscreen_vertices[0].uv = vertex_to_UV(g_menuscreen, 0, 0);
 	g_menuscreen_vertices[0].rgbaq = color_to_RGBAQ(0x80, 0x80, 0x80, 0x80, 0);
 
-    g_menuscreen_vertices[1].xyz2 = vertex_to_XYZ2(gsGlobal, g_menuscreen->Width, g_menuscreen->Height, 2);
-    g_menuscreen_vertices[1].uv = vertex_to_UV(g_menuscreen, g_menuscreen->Width, g_menuscreen->Height);
-    g_menuscreen_vertices[1].rgbaq = color_to_RGBAQ(0x80, 0x80, 0x80, 0x80, 0);
+	g_menuscreen_vertices[1].xyz2 = vertex_to_XYZ2(gsGlobal, g_menuscreen->Width, g_menuscreen->Height, 2);
+	g_menuscreen_vertices[1].uv = vertex_to_UV(g_menuscreen, g_menuscreen->Width, g_menuscreen->Height);
+	g_menuscreen_vertices[1].rgbaq = color_to_RGBAQ(0x80, 0x80, 0x80, 0x80, 0);
 }
 
 void set_g_screen_values() {
-    if (g_screen != NULL) {
-        free(g_screen->Mem);
-        free(g_screen);
-        free(g_screen_vertices);
-    }
-    g_screen = (GSTEXTURE *)calloc(1, sizeof(GSTEXTURE));
-    size_t g_screenSize = gsKit_texture_size_ee(328, 256, GS_PSM_CT16);
-    g_screen->Width = 328;
-    g_screen->Height = 256;
-    g_screen->PSM = GS_PSM_CT16;
-    g_screen->Mem = (uint32_t *)malloc(g_screenSize);
-
-    g_screen_width = 328;
-    g_screen_height = 256; 
-    g_screen_ppitch = 328;
-    g_screen_ptr = g_screen->Mem;
-
-    g_screen_vertices = (GSPRIMUVPOINT *)calloc(2, sizeof(GSPRIMUVPOINT));
-    
-    g_screen_vertices[0].xyz2 = vertex_to_XYZ2(gsGlobal, 0, 0, 0);
-    g_screen_vertices[0].uv = vertex_to_UV(g_screen, 0, 0);
-    g_screen_vertices[0].rgbaq = color_to_RGBAQ(0x80, 0x80, 0x80, 0x80, 0);
-
-    g_screen_vertices[1].xyz2 = vertex_to_XYZ2(gsGlobal, gsGlobal->Width, gsGlobal->Height, 0);
-    g_screen_vertices[1].uv = vertex_to_UV(g_screen, g_screen->Width, g_screen->Height);
-    g_screen_vertices[1].rgbaq = color_to_RGBAQ(0x80, 0x80, 0x80, 0x80, 0);
-
-    if (is_16bit_mode())
-		PicoDrawSetOutBuf(g_screen_ptr, g_screen_ppitch * 2);
-	else
-		PicoDrawSetOutBuf(g_screen_ptr, g_screen_ppitch);
+	size_t g_screenSize = gsKit_texture_size_ee(328, 256, GS_PSM_CT16);
+	int i;
+
+	g_screen_palette = malloc(gsKit_texture_size_ee(16, 16, GS_PSM_CT16));
+	g_screen_vertices = (GSPRIMUVPOINT *)calloc(2, sizeof(GSPRIMUVPOINT));
+	for (i = 0; i < 2; i++) {
+		g_screens[i] = (GSTEXTURE *)calloc(1, sizeof(GSTEXTURE));
+		g_screens[i]->Mem = (uint32_t *)malloc(g_screenSize);
+
+		g_screens[i]->Width = 328;
+		g_screens[i]->Height = 256;
+		g_screens[i]->PSM = GS_PSM_CT16;
+		g_screens[i]->Filter = GS_FILTER_LINEAR;
+
+		g_screens[i]->Clut = g_screen_palette;
+		g_screens[i]->ClutPSM = GS_PSM_CT16;
+	}
+	g_screen = g_screens[g_screen_index];
+	g_screen_ptr = g_screen->Mem;
+
+	g_screen_width = 328;
+	g_screen_height = 256;
+	g_screen_ppitch = 328;
+
+	g_screen_vertices[0].xyz2 = vertex_to_XYZ2(gsGlobal, 0, 0, 0);
+	g_screen_vertices[0].uv = vertex_to_UV(g_screen, 0, 0);
+	g_screen_vertices[0].rgbaq = color_to_RGBAQ(0x80, 0x80, 0x80, 0x80, 0);
+
+	g_screen_vertices[1].xyz2 = vertex_to_XYZ2(gsGlobal, gsGlobal->Width, gsGlobal->Height, 0);
+	g_screen_vertices[1].uv = vertex_to_UV(g_screen, g_screen->Width, g_screen->Height);
+	g_screen_vertices[1].rgbaq = color_to_RGBAQ(0x80, 0x80, 0x80, 0x80, 0);
 }
 
 void set_cdleds_values() {
-    if (cdleds != NULL) {
-        free(cdleds->Mem);
-        free(cdleds);
-        free(cdleds_vertices);
-    }
-    cdleds = (GSTEXTURE *)calloc(1, sizeof(GSTEXTURE));
-    size_t cdledsSize = gsKit_texture_size_ee(14, 5, GS_PSM_CT16);
-    cdleds->Width = 14;
-    cdleds->Height = 5;
-    cdleds->PSM = GS_PSM_CT16;
-    cdleds->Mem = (uint32_t *)malloc(cdledsSize);
-
-    cdleds_vertices = (GSPRIMUVPOINT *)calloc(2, sizeof(GSPRIMUVPOINT));
-    
-    cdleds_vertices[0].xyz2 = vertex_to_XYZ2(gsGlobal, 4, 1, 1);
-    cdleds_vertices[0].uv = vertex_to_UV(cdleds, 0, 0);
-    cdleds_vertices[0].rgbaq = color_to_RGBAQ(0x80, 0x80, 0x80, 0x80, 0);
-
-    cdleds_vertices[1].xyz2 = vertex_to_XYZ2(gsGlobal, cdleds->Width, cdleds->Height, 1);
-    cdleds_vertices[1].uv = vertex_to_UV(cdleds, cdleds->Width, cdleds->Height);
-    cdleds_vertices[1].rgbaq = color_to_RGBAQ(0x80, 0x80, 0x80, 0x80, 0);
+	size_t cdledsSize = gsKit_texture_size_ee(14, 5, GS_PSM_CT16);
+
+	cdleds = (GSTEXTURE *)calloc(1, sizeof(GSTEXTURE));
+	cdleds->Mem = (uint32_t *)malloc(cdledsSize);
+	cdleds_vertices = (GSPRIMUVPOINT *)calloc(2, sizeof(GSPRIMUVPOINT));
+
+	cdleds->Width = 14;
+	cdleds->Height = 5;
+	cdleds->PSM = GS_PSM_CT16;
+
+	cdleds_vertices[0].xyz2 = vertex_to_XYZ2(gsGlobal, 4, 1, 1);
+	cdleds_vertices[0].uv = vertex_to_UV(cdleds, 0, 0);
+	cdleds_vertices[0].rgbaq = color_to_RGBAQ(0x80, 0x80, 0x80, 0x80, 0);
+
+	cdleds_vertices[1].xyz2 = vertex_to_XYZ2(gsGlobal, cdleds->Width, cdleds->Height, 1);
+	cdleds_vertices[1].uv = vertex_to_UV(cdleds, cdleds->Width, cdleds->Height);
+	cdleds_vertices[1].rgbaq = color_to_RGBAQ(0x80, 0x80, 0x80, 0x80, 0);
 }
 
 void set_osd_values() {
-    if (osd != NULL) {
-        free(osd->Mem);
-        free(osd);
-        free(osd_vertices);
-    }
-    osd = (GSTEXTURE *)calloc(1, sizeof(GSTEXTURE));
-    size_t osdSize = gsKit_texture_size_ee(512, 8, GS_PSM_CT16);
-    osd->Width = 512;
-    osd->Height = 8;
-    osd->PSM = GS_PSM_CT16;
-    osd->Mem = (uint32_t *)malloc(osdSize);
-
-    osd_vertices_count = 2;
-    osd_vertices = (GSPRIMUVPOINT *)calloc(osd_vertices_count, sizeof(GSPRIMUVPOINT));
-    
-    osd_vertices[0].xyz2 = vertex_to_XYZ2(gsGlobal, 0, 0, 1);
-    osd_vertices[0].uv = vertex_to_UV(osd, 0, 0);
-    osd_vertices[0].rgbaq = color_to_RGBAQ(0x80, 0x80, 0x80, 0x80, 0);
-
-    osd_vertices[1].xyz2 = vertex_to_XYZ2(gsGlobal, osd->Width, osd->Height, 1);
-    osd_vertices[1].uv = vertex_to_UV(osd, osd->Width, osd->Height);
-    osd_vertices[1].rgbaq = color_to_RGBAQ(0x80, 0x80, 0x80, 0x80, 0);
+	size_t osdSize = gsKit_texture_size_ee(gsGlobal->Width, 8, GS_PSM_CT16);
+	int num_osds = 4, i;
+
+	osd = (GSTEXTURE *)calloc(1, sizeof(GSTEXTURE));
+	osd->Mem = (uint32_t *)malloc(osdSize);
+
+	osd_vertices_count = 2*num_osds;
+	osd_vertices = (GSPRIMUVPOINT *)calloc(osd_vertices_count, sizeof(GSPRIMUVPOINT));
+
+	osd->Width = gsGlobal->Width;
+	osd->Height = 8;
+	osd->PSM = GS_PSM_CT16;
+
+	for (i = 0; i < 2*num_osds; i++)
+		osd_vertices[i].rgbaq = color_to_RGBAQ(0x80, 0x80, 0x80, 0x80, 0);
 }
 
 static void video_init(void)
 {
-    ee_sema_t sema;
+	ee_sema_t sema;
+
+	sema.init_count = 0;
+	sema.max_count  = 1;
+	sema.option     = 0;
 
-    sema.init_count = 0;
-    sema.max_count  = 1;
-    sema.option     = 0;
+	vsync_sema_id   = CreateSema(&sema);
 
-    vsync_sema_id   = CreateSema(&sema);
+	gsGlobal = gsKit_init_global();
+//	gsGlobal->Mode = GS_MODE_NTSC;
+//	gsGlobal->Height = 448;
 
-    gsGlobal = gsKit_init_global();
-    gsGlobal->Mode = GS_MODE_NTSC;
-    gsGlobal->Height = 448;
+	gsGlobal->PSM  = GS_PSM_CT16;
+	gsGlobal->PSMZ = GS_PSMZ_16S;
+	gsGlobal->ZBuffering = GS_SETTING_OFF;
+	gsGlobal->DoubleBuffering = GS_SETTING_ON;
+	gsGlobal->PrimAlphaEnable = GS_SETTING_OFF;
+	gsGlobal->Dithering = GS_SETTING_OFF;
 
-    gsGlobal->PSM  = GS_PSM_CT24;
-    gsGlobal->PSMZ = GS_PSMZ_16S;
-    gsGlobal->ZBuffering = GS_SETTING_OFF;
-    gsGlobal->DoubleBuffering = GS_SETTING_ON;
-    gsGlobal->PrimAlphaEnable = GS_SETTING_OFF;
-    gsGlobal->Dithering = GS_SETTING_OFF;
 
+	dmaKit_init(D_CTRL_RELE_OFF, D_CTRL_MFD_OFF, D_CTRL_STS_UNSPEC, D_CTRL_STD_OFF, D_CTRL_RCYC_8, 1 << DMA_CHANNEL_GIF);
+	dmaKit_chan_init(DMA_CHANNEL_GIF);
 
-    dmaKit_init(D_CTRL_RELE_OFF, D_CTRL_MFD_OFF, D_CTRL_STS_UNSPEC, D_CTRL_STD_OFF, D_CTRL_RCYC_8, 1 << DMA_CHANNEL_GIF);
-    dmaKit_chan_init(DMA_CHANNEL_GIF);
+	gsKit_set_clamp(gsGlobal, GS_CMODE_REPEAT);
 
-    gsKit_set_clamp(gsGlobal, GS_CMODE_REPEAT);
+	gsKit_vram_clear(gsGlobal);
+	gsKit_init_screen(gsGlobal);
+	gsKit_TexManager_init(gsGlobal);
+	gsKit_mode_switch(gsGlobal, GS_ONESHOT);
+	gsKit_clear(gsGlobal, GS_BLACK);
+	vsync = 0;
+	vsync_callback_id = gsKit_add_vsync_handler(vsync_handler);
 
-    gsKit_vram_clear(gsGlobal);
-    gsKit_init_screen(gsGlobal);
-    gsKit_TexManager_init(gsGlobal);
-    gsKit_mode_switch(gsGlobal, GS_ONESHOT);
-    gsKit_clear(gsGlobal, GS_BLACK);
-    vsync = 0;
-    vsync_callback_id = gsKit_add_vsync_handler(vsync_handler);
+	set_g_screen_values();
+	set_g_menuscreen_values();
+	set_osd_values();
+	set_cdleds_values();
 
-    set_g_menuscreen_values();
+	g_menubg_ptr = (uint8_t *)malloc(2 * g_menuscreen_pp * g_menuscreen_h);
+	g_menubg_src_w = g_screen->Width;
+	g_menubg_src_h = g_screen->Height;
+	g_menubg_src_pp = g_screen->Width;
 }
 
 static void video_deinit(void)
 {
-    free(g_menuscreen->Mem);
-    free(g_menuscreen);
-    free(g_menuscreen_vertices);
-    free(g_menubg_ptr);
+	free(g_screens[0]->Mem);
+	free(g_screens[0]);
+	free(g_screens[1]->Mem);
+	free(g_screens[1]);
+	free(g_screen_vertices);
+	free(g_screen_palette);
 
-    gsKit_clear(gsGlobal, GS_BLACK);
-    gsKit_vram_clear(gsGlobal);
-    gsKit_deinit_global(gsGlobal);
-    gsKit_remove_vsync_handler(vsync_callback_id);
+	free(g_menuscreen->Mem);
+	free(g_menuscreen);
+	free(g_menuscreen_vertices);
+	free(g_menubg_ptr);
 
-    if (vsync_sema_id >= 0)
-        DeleteSema(vsync_sema_id);
+	free(osd->Mem);
+	free(osd);
+	free(osd_vertices);
+
+	free(cdleds->Mem);
+	free(cdleds);
+	free(cdleds_vertices);
+
+	gsKit_clear(gsGlobal, GS_BLACK);
+	gsKit_vram_clear(gsGlobal);
+	gsKit_deinit_global(gsGlobal);
+	gsKit_remove_vsync_handler(vsync_callback_id);
+
+	if (vsync_sema_id >= 0)
+		DeleteSema(vsync_sema_id);
+}
+
+static void set_scaling_params(void)
+{
+	int width, height, xoffs, yoffs;
+	int u[2], v[2];
+
+	height = (int)(out_h * vscale + 0.5);
+	width  = (int)(out_w * hscale + 0.5);
+
+	if (width  & 1) width++;  // make even
+	if (height & 1) height++;
+
+	if (width >= gsGlobal->Width) {
+		u[0] = out_x + (width-gsGlobal->Width)/2;
+		u[1] = out_x + out_w - (width-gsGlobal->Width)/2 - 1;
+		width = gsGlobal->Width;
+		xoffs = 0;
+	} else {
+		u[0] = out_x;
+		u[1] = out_x + out_w;
+		xoffs = gsGlobal->Width/2 - width/2;
+	}
+
+	if (height >= gsGlobal->Height) {
+		v[0] = out_y + (height-gsGlobal->Height)/2;
+		v[1] = out_y + out_h - (height-gsGlobal->Height)/2;
+		height = gsGlobal->Height;
+		yoffs = 0;
+	} else {
+		v[0] = out_y;
+		v[1] = out_y + out_h;
+		yoffs = gsGlobal->Height/2 - height/2;
+	}
+
+	if (xoffs < 0) xoffs = 0;
+	if (yoffs < 0) yoffs = 0;
+	g_screen_vertices[0].xyz2 = vertex_to_XYZ2(gsGlobal, xoffs, yoffs, 0);
+	g_screen_vertices[1].xyz2 = vertex_to_XYZ2(gsGlobal, xoffs + width, yoffs + height, 0);
+
+	if (!is_16bit_mode()) {
+		// 8-bit modes have an 8 px overlap area on the left
+		u[0] += 8; u[1] += 8;
+	}
+	g_screen_vertices[0].uv = vertex_to_UV(g_screen, u[0], v[0]);
+	g_screen_vertices[1].uv = vertex_to_UV(g_screen, u[1], v[1]);
+
+//	lprintf("set_scaling_params: wxh = %ix%i\n",gsGlobal->Width,gsGlobal->Height);
+//	lprintf("offs: %i, %i  wh: %i, %i\n", xoffs, yoffs, width, height);
+//	lprintf("uv0, uv1: %i, %i; %i, %i\n", u[0], v[0], u[1], v[1]);
+}
+
+static void make_ps2_palette(void)
+{
+	PicoDrawUpdateHighPal();
+
+	// Rotate CLUT. PS2 is special since entries in CLUT are not in sequence.
+	unsigned short int *pal=(void *)g_screen_palette;
+	int i;
+
+	for (i = 0; i < 256; i+=8) {
+		if ((i&0x18) == 0x08)
+			memcpy(pal+i,Pico.est.HighPal+i+8,16);
+		else if ((i&0x18) == 0x10)
+			memcpy(pal+i,Pico.est.HighPal+i-8,16);
+		else
+			memcpy(pal+i,Pico.est.HighPal+i,16);
+	}
 }
 
 static int get_renderer(void)
 {
+	if (is_bg_frame)
+		return RT_16BIT;
 	if (PicoIn.AHW & PAHW_32X)
 		return currentConfig.renderer32x;
 	else
@@ -480,7 +569,6 @@ static void change_renderer(int diff)
 static void apply_renderer(void)
 {
 	PicoIn.opt &= ~(POPT_ALT_RENDERER|POPT_EN_SOFTSCALE);
-	PicoIn.opt |= POPT_DIS_32C_BORDER;
 
 	switch (get_renderer()) {
 	case RT_16BIT:
@@ -496,57 +584,65 @@ static void apply_renderer(void)
 	}
 }
 
-static void osd_text(int x, const char *text)
+static void blit_screen(void)
 {
-    // int len = strlen(text) * 8;
-	// int *p, h;
-	// void *tmp = g_screen_ptr;
-    // printf("osd_text, text: %s\n", text);
+	if (!is_16bit_mode() && Pico.m.dirtyPal)
+		make_ps2_palette();
 
-	// g_screen_ptr = osd_buf;
-	// for (h = 0; h < 8; h++) {
-	// 	p = (int *) (osd_buf+x+512*h);
-	// 	p = (int *) ((int)p & ~3); // align
-	// 	memset32_uncached(p, 0, len/2);
-	// }
-	// emu_text_out16(x, 0, text);
-	// g_screen_ptr = tmp;
+	g_screen->PSM = is_16bit_mode() ? GS_PSM_CT16 : GS_PSM_T8;
+	g_screen->Filter = (currentConfig.filter ? GS_FILTER_LINEAR : GS_FILTER_NEAREST);
 
-	// osd_buf_x[osd_buf_cnt] = x;
-	// osd_buf_l[osd_buf_cnt] = len;
-	// osd_buf_cnt ++;
+	gsKit_TexManager_bind(gsGlobal, g_screen);
+	gskit_prim_list_sprite_texture_uv_3d(gsGlobal, g_screen, 2, g_screen_vertices);
 }
 
-static void blit_screen(void)
+static void osd_text(int x, const char *text)
 {
-    gsKit_TexManager_invalidate(gsGlobal, g_screen);
+	void *old_ptr = g_screen_ptr;
+	int old_pitch = g_screen_ppitch;
+
+	int len = strlen(text) * 8;
+	u16 *osd_buf = osd->Mem;
+	int *p, h;
+
+	g_screen_ptr = osd_buf;
+	g_screen_ppitch = gsGlobal->Width;
+	for (h = 0; h < 8; h++) {
+		p = (int *) (osd_buf + x + gsGlobal->Width*h);
+		p = (int *) ((int)p & ~3); // align
+		memset32_uncached(p, 0, len/2);
+	}
+	emu_text_out16(x, 0, text);
+	g_screen_ptr = old_ptr;
+	g_screen_ppitch = old_pitch;
 
-    gsKit_TexManager_bind(gsGlobal, g_screen);
-    gskit_prim_list_sprite_texture_uv_3d(
-        gsGlobal, 
-        g_screen,
-        2,
-        g_screen_vertices
-    );
+	osd_vertices[osd_buf_cnt].xyz2 = vertex_to_XYZ2(gsGlobal, x, gsGlobal->Height-8, 1);
+	osd_vertices[osd_buf_cnt].uv = vertex_to_UV(osd, x, 0);
+	osd_vertices[osd_buf_cnt+1].xyz2 = vertex_to_XYZ2(gsGlobal, x+len, gsGlobal->Height, 1);
+	osd_vertices[osd_buf_cnt+1].uv = vertex_to_UV(osd, x+len, 8);
+	osd_buf_cnt += 2;
 }
 
 static void blit_osd(void)
 {
-
+	gsKit_TexManager_bind(gsGlobal, osd);
+	while (osd_buf_cnt > 0) {
+		osd_buf_cnt -= 2;
+		gskit_prim_list_sprite_texture_uv_3d(gsGlobal, osd, 2, osd_vertices+osd_buf_cnt);
+	}
 }
 
 static void cd_leds(void)
 {
-    unsigned int reg, col_g, col_r, *p;
-    gsKit_TexManager_invalidate(gsGlobal, cdleds);
+	unsigned int reg, col_g, col_r, *p;
 
 	reg = Pico_mcd->s68k_regs[0];
 
 	p = (unsigned int *)cdleds->Mem;
 	col_g = (reg & 2) ? 0x06000600 : 0;
 	col_r = (reg & 1) ? 0x00180018 : 0;
-	*p++ = col_g; *p++ = col_g; p+=2; *p++ = col_r; *p++ = col_r; p += 512/2 - 12/2;
-	*p++ = col_g; *p++ = col_g; p+=2; *p++ = col_r; *p++ = col_r; p += 512/2 - 12/2;
+	*p++ = col_g; *p++ = col_g; p+=2; *p++ = col_r; *p++ = col_r; p += gsGlobal->Width/2 - 12/2;
+	*p++ = col_g; *p++ = col_g; p+=2; *p++ = col_r; *p++ = col_r; p += gsGlobal->Width/2 - 12/2;
 	*p++ = col_g; *p++ = col_g; p+=2; *p++ = col_r; *p++ = col_r;
 
 	osd_cdleds = 1;
@@ -554,126 +650,131 @@ static void cd_leds(void)
 
 static void blit_cdleds(void)
 {
-    if (!osd_cdleds) return;
+	if (!osd_cdleds) return;
 
-    gsKit_TexManager_bind(gsGlobal, cdleds);
-    gskit_prim_list_sprite_texture_uv_3d(
-        gsGlobal, 
-        cdleds, 
-        2,
-        cdleds_vertices
-    );
+	gsKit_TexManager_bind(gsGlobal, cdleds);
+	gskit_prim_list_sprite_texture_uv_3d(gsGlobal, cdleds, 2, cdleds_vertices);
 }
 
 static void draw_pico_ptr(void)
 {
-	// unsigned char *p = (unsigned char *)g_screen_ptr + 8;
+	int x = pico_pen_x, y = pico_pen_y, offs;
+	int pp = g_screen_ppitch;
 
-	// // only if pen enabled and for 8bit mode
-	// if (pico_inp_mode == 0 || is_16bit_mode()) return;
+	x = (x * out_w * ((1ULL<<32) / 320)) >> 32;
+	y = (y * out_h * ((1ULL<<32) / 224)) >> 32;
 
-	// p += 512 * (pico_pen_y + PICO_PEN_ADJUST_Y);
-	// p += pico_pen_x + PICO_PEN_ADJUST_X;
-	// if (!(Pico.video.reg[12]&1) && !(PicoIn.opt & POPT_DIS_32C_BORDER))
-	// 	p += 32;
+	offs = g_screen_ppitch * (out_y+y) + (out_x+x);
 
-	// p[  -1] = 0xe0; p[   0] = 0xf0; p[   1] = 0xe0;
-	// p[ 511] = 0xf0; p[ 512] = 0xf0; p[ 513] = 0xf0;
-	// p[1023] = 0xe0; p[1024] = 0xf0; p[1025] = 0xe0;
-}
+	if (is_16bit_mode()) {
+		unsigned short *p = (unsigned short *)g_screen_ptr + offs;
 
-static void vidResetMode(void) {}
+		p[    -1] = 0x0000; p[   0] = 0x001f; p[     1] = 0x0000;
+		p[  pp-1] = 0x001f; p[  pp] = 0x001f; p[  pp+1] = 0x001f;
+		p[2*pp-1] = 0x0000; p[2*pp] = 0x001f; p[2*pp+1] = 0x0000;
+	} else {
+		unsigned char *p = (unsigned char *)g_screen_ptr + offs + 8;
 
-/* Copy of gsKit_sync_flip, but without the 'flip' */
-static void gsKit_sync(GSGLOBAL *gsGlobal)
+		p[    -1] = 0xe0; p[   0] = 0xf0; p[     1] = 0xe0;
+		p[  pp-1] = 0xf0; p[  pp] = 0xf0; p[  pp+1] = 0xf0;
+		p[2*pp-1] = 0xe0; p[2*pp] = 0xf0; p[2*pp+1] = 0xe0;
+	}
+}
+
+static void vidResetMode(void)
 {
-   if (!gsGlobal->FirstFrame)
-      WaitSema(vsync_sema_id);
+	set_scaling_params();
 
-   while (PollSema(vsync_sema_id) >= 0);
+	Pico.m.dirtyPal = 1;
 }
 
-/* Copy of gsKit_sync_flip, but without the 'sync' */
-static void gsKit_flip(GSGLOBAL *gsGlobal)
+/* finalize rendering a frame */
+void pemu_finalize_frame(const char *fps, const char *notice)
 {
-   if (!gsGlobal->FirstFrame)
-   {
-      if (gsGlobal->DoubleBuffering == GS_SETTING_ON)
-      {
-         GS_SET_DISPFB2(gsGlobal->ScreenBuffer[gsGlobal->ActiveBuffer & 1] / 8192,
-                        gsGlobal->Width / 64, gsGlobal->PSM, 0, 0);
+	int emu_opt = currentConfig.EmuOpt;
 
-         gsGlobal->ActiveBuffer ^= 1;
-      }
-   }
+	if ((PicoIn.AHW & PAHW_PICO) && (currentConfig.EmuOpt & EOPT_PICO_PEN))
+		if (pico_inp_mode) draw_pico_ptr();
 
-   gsKit_setactive(gsGlobal);
-}
+	osd_buf_cnt = 0;
+	if (notice)      osd_text(4, notice);
+	if (emu_opt & 2) osd_text(OSD_FPS_X, fps);
 
-static void flipScreen()
-{
-	gsKit_queue_exec(gsGlobal);
-	gsKit_finish();
-    gsKit_flip(gsGlobal);
+	osd_cdleds = 0;
+	if ((emu_opt & 0x400) && (PicoIn.AHW & PAHW_MCD))
+		cd_leds();
 
-	gsKit_TexManager_nextFrame(gsGlobal);
-    gsKit_clear(gsGlobal, GS_BLACK);
+	FlushCache(WRITEBACK_DCACHE);
 }
 
-
 /* display a completed frame buffer and prepare a new render buffer */
 void plat_video_flip(void)
 {
-    blit_screen();
-    blit_osd();
+	gsKit_TexManager_invalidate(gsGlobal, osd);
+	gsKit_TexManager_invalidate(gsGlobal, cdleds);
+	gsKit_TexManager_invalidate(gsGlobal, g_screen);
+
+	gsKit_finish();
+	flipScreen();
+
+	gsKit_clear(gsGlobal, GS_BLACK);
+	blit_screen();
+	blit_osd();
 	blit_cdleds();
-    flipScreen();
+	gsKit_queue_exec(gsGlobal);
+
+	g_screen_index ^= 1;
+	g_screen = g_screens[g_screen_index];
+	g_screen_ptr = g_screen->Mem;
+
+	plat_video_set_buffer(g_screen_ptr);
 }
 
 /* wait for start of vertical blanking */
 void plat_video_wait_vsync(void)
 {
-    gsKit_sync(gsGlobal);
+	gsKit_sync(gsGlobal);
 }
 
 /* switch from emulation display to menu display */
 void plat_video_menu_enter(int is_rom_loaded)
 {
+	g_screen_ptr = NULL;
 }
 
 /* start rendering a menu screen */
 void plat_video_menu_begin(void)
 {
-    gsKit_TexManager_invalidate(gsGlobal, g_menuscreen);
 }
 
 /* display a completed menu screen */
 void plat_video_menu_end(void)
 {
-    gsKit_TexManager_bind(gsGlobal, g_menuscreen);
-    gskit_prim_list_sprite_texture_uv_3d(
-        gsGlobal, 
-        g_menuscreen, 
-        2, 
-        g_menuscreen_vertices
-    );
-    flipScreen(1);
+	gsKit_TexManager_bind(gsGlobal, g_menuscreen);
+	gskit_prim_list_sprite_texture_uv_3d( gsGlobal, g_menuscreen, 2, g_menuscreen_vertices);
+	gsKit_queue_exec(gsGlobal);
+	gsKit_finish();
+	gsKit_TexManager_invalidate(gsGlobal, g_menuscreen);
+
+	flipScreen();
 }
 
 /* terminate menu display */
 void plat_video_menu_leave(void)
 {
+	g_screen_ptr = g_screen->Mem;
+	plat_video_set_buffer(g_screen_ptr);
 }
 
 /* set default configuration values */
 void pemu_prep_defconfig(void)
 {
-	defaultConfig.s_PsndRate = 44100;
+	defaultConfig.s_PsndRate = 22050;
 	defaultConfig.s_PicoCDBuffers = 64;
 	defaultConfig.filter = EOPT_FILTER_BILINEAR; // bilinear filtering
 	defaultConfig.scaling = EOPT_SCALE_43;
 	defaultConfig.vscaling = EOPT_VSCALE_FULL;
-	defaultConfig.renderer = RT_16BIT;
+	defaultConfig.renderer = RT_8BIT_ACC;
 	defaultConfig.renderer32x = RT_8BIT_ACC;
 	defaultConfig.EmuOpt |= EOPT_SHOW_RTC;
 }
@@ -681,47 +782,24 @@ void pemu_prep_defconfig(void)
 /* check configuration for inconsistencies */
 void pemu_validate_config(void)
 {
-	if (currentConfig.gamma < -4 || currentConfig.gamma >  16)
-		currentConfig.gamma = 0;
-	if (currentConfig.gamma2 < 0 || currentConfig.gamma2 > 2)
-		currentConfig.gamma2 = 0;
-}
-
-/* finalize rendering a frame */
-void pemu_finalize_frame(const char *fps, const char *notice)
-{
-	int emu_opt = currentConfig.EmuOpt;
-
-	if (PicoIn.AHW & PAHW_PICO)
-		draw_pico_ptr();
-
-	osd_buf_cnt = 0;
-	if (notice)      osd_text(4, notice);
-	if (emu_opt & 2) osd_text(OSD_FPS_X, fps);
-
-	osd_cdleds = 0;
-	if ((emu_opt & 0x400) && (PicoIn.AHW & PAHW_MCD))
-		cd_leds();
-
-	FlushCache(WRITEBACK_DCACHE);
 }
 
-void plat_init(void) 
+void plat_init(void)
 {
-    video_init();
-    init_joystick_driver(false);
-    in_ps2_init(in_ps2_defbinds);
-    in_probe();
-    init_audio_driver();
-    sound_init();
-    plat_get_data_dir(rom_fname_loaded, sizeof(rom_fname_loaded));
+	video_init();
+	init_joystick_driver(false);
+	in_ps2_init(in_ps2_defbinds);
+	in_probe();
+	init_audio_driver();
+	sound_init();
+	plat_get_data_dir(rom_fname_loaded, sizeof(rom_fname_loaded));
 }
 
 void plat_finish(void) {
-    sound_deinit();
-    deinit_audio_driver();
-    deinit_joystick_driver(false);
-    video_deinit();
+	sound_deinit();
+	deinit_audio_driver();
+	deinit_joystick_driver(false);
+	video_deinit();
 }
 
 /* display emulator status messages before holding emulation */
@@ -754,37 +832,38 @@ void emu_video_mode_change(int start_line, int line_count, int start_col, int co
 	int h43 = (col_count  >= 192 ? 320 : col_count); // ugh, mind GG...
 	int v43 = (line_count >= 192 ? Pico.m.pal ? 240 : 224 : line_count);
 
+	out_y = start_line; out_x = start_col;
+	out_h = line_count; out_w = col_count;
+
 	if (col_count == 248) // mind aspect ratio when blanking 1st column
 		col_count = 256;
-    
-    g_screen_vertices[0].uv = vertex_to_UV(g_screen, start_col, start_line);
-    g_screen_vertices[1].uv = vertex_to_UV(g_screen, col_count, line_count);
 
 	switch (currentConfig.vscaling) {
 	case EOPT_VSCALE_FULL:
 		line_count = v43;
-		vscale = (float)270/line_count;
+		vscale = (float)gsGlobal->Height/line_count;
 		break;
 	case EOPT_VSCALE_NOBORDER:
-		vscale = (float)270/line_count;
+		vscale = (float)gsGlobal->Height/line_count;
 		break;
 	default:
 		vscale = 1;
 		break;
 	}
 
+	hscale = vscale * (gsGlobal->Aspect == GS_ASPECT_16_9 ? (4./3)/(16./9) : 1);
 	switch (currentConfig.scaling) {
 	case EOPT_SCALE_43:
-		hscale = (vscale*h43)/col_count;
+		hscale = (hscale*h43)/col_count;
 		break;
 	case EOPT_SCALE_STRETCH:
-		hscale = (vscale*h43/2 + 480/2)/col_count;
+		hscale = (hscale*h43/2 + gsGlobal->Width/2)/col_count;
 		break;
 	case EOPT_SCALE_WIDE:
-		hscale = (float)480/col_count;
+		hscale = (float)gsGlobal->Width/col_count;
 		break;
 	default:
-		hscale = vscale;
+		// hscale = vscale, computed before switch
 		break;
 	}
 
@@ -794,11 +873,15 @@ void emu_video_mode_change(int start_line, int line_count, int start_col, int co
 /* render one frame in RGB */
 void pemu_forced_frame(int no_scale, int do_emu)
 {
+	is_bg_frame = 1;
 	Pico.m.dirtyPal = 1;
 
 	if (!no_scale)
 		no_scale = currentConfig.scaling == EOPT_SCALE_NONE;
 	emu_cmn_forced_frame(no_scale, do_emu, g_screen_ptr);
+
+	g_menubg_src_ptr = g_screen_ptr;
+	is_bg_frame = 0;
 }
 
 /* change the platform output rendering */
@@ -819,8 +902,17 @@ void plat_video_toggle_renderer(int change, int is_menu_call)
 		emu_status_msg(renderer_names[get_renderer()]);
 }
 
+/* set the buffer for emulator output rendering */
+void plat_video_set_buffer(void *buf)
+{
+	if (is_16bit_mode())
+		PicoDrawSetOutBuf(g_screen_ptr, g_screen_ppitch * 2);
+	else
+		PicoDrawSetOutBuf(g_screen_ptr, g_screen_ppitch);
+}
+
 /* prepare for emulator output rendering */
-void plat_video_loop_prepare(void) 
+void plat_video_loop_prepare(void)
 {
 	apply_renderer();
 	vidResetMode();
@@ -829,12 +921,11 @@ void plat_video_loop_prepare(void)
 /* prepare for entering the emulator loop */
 void pemu_loop_prep(void)
 {
-    set_g_screen_values();
-    set_cdleds_values();
 }
 
 /* terminate the emulator loop */
 void pemu_loop_end(void)
 {
-    pemu_sound_stop();
-}
\ No newline at end of file
+	pemu_sound_stop();
+	pemu_forced_frame(0, 1);
+}
diff --git a/platform/ps2/menu.c b/platform/ps2/menu.c
new file mode 100644
index 00000000..c4416448
--- /dev/null
+++ b/platform/ps2/menu.c
@@ -0,0 +1,21 @@
+
+static const char *men_vscaling_opts[] = { "OFF", "fullscreen", "borderless", NULL };
+static const char *men_hscaling_opts[] = { "1:1", "4:3", "extended", "fullwidth", NULL };
+static const char *men_filter_opts[] = { "nearest", "bilinear", NULL };
+
+#define MENU_OPTIONS_GFX \
+	mee_enum    ("Vertical scaling",   MA_OPT_VSCALING,   currentConfig.vscaling, men_vscaling_opts), \
+	mee_enum    ("Aspect ratio",       MA_OPT_SCALING,    currentConfig.scaling, men_hscaling_opts), \
+	mee_enum    ("Scaler type",        MA_OPT3_FILTERING, currentConfig.filter, men_filter_opts), \
+	mee_onoff   ("Wait for vsync",     MA_OPT3_VSYNC,     currentConfig.EmuOpt, EOPT_VSYNC), \
+
+#define MENU_OPTIONS_ADV
+
+static menu_entry e_menu_sms_options[];
+static menu_entry e_menu_keyconfig[];
+
+void psp_menu_init(void)
+{
+	me_enable(e_menu_sms_options, MA_SMSOPT_GHOSTING, 0);
+	me_enable(e_menu_keyconfig, MA_CTRL_DEADZONE, 0);
+}
diff --git a/platform/ps2/menu.h b/platform/ps2/menu.h
new file mode 100644
index 00000000..d5aa6082
--- /dev/null
+++ b/platform/ps2/menu.h
@@ -0,0 +1,10 @@
+
+void menu_loop(void);
+int  menu_loop_tray(void);
+void menu_romload_prepare(const char *rom_name);
+void menu_romload_end(void);
+
+
+#define CONFIGURABLE_KEYS (PBTN_UP|PBTN_LEFT|PBTN_RIGHT|PBTN_DOWN|PBTN_L|PBTN_R|PBTN_TRIANGLE|PBTN_CIRCLE|PBTN_X|PBTN_SQUARE|PBTN_START| \
+	PBTN_NUB_UP|PBTN_NUB_RIGHT|PBTN_NUB_DOWN|PBTN_NUB_LEFT|PBTN_NOTE)
+
diff --git a/platform/ps2/plat.c b/platform/ps2/plat.c
index 7b6d2267..d645b5ef 100644
--- a/platform/ps2/plat.c
+++ b/platform/ps2/plat.c
@@ -19,7 +19,8 @@
 
 #include "../libpicofe/plat.h"
 
-struct plat_target plat_target = {};
+static int sound_rates[] = { 11025, 22050, 44100, -1 };
+struct plat_target plat_target = { .sound_rates = sound_rates };
 
 static void reset_IOP() {
     SifInitRpc(0);
@@ -205,4 +206,4 @@ void lprintf(const char *fmt, ...)
 	va_end(vl);
 }
 
-void plat_debug_cat(char *str) {}
\ No newline at end of file
+void plat_debug_cat(char *str) {}