From 15cc45c0da19205557139b0c3f30792beb883565 Mon Sep 17 00:00:00 2001
From: kub <derkub@gmail.com>
Date: Wed, 20 Mar 2024 23:43:10 +0100
Subject: [PATCH] platforms, display pad overlay and storyware pages for Pico

---
 Makefile                       |  18 +++--
 pico/pico/pico.c               |   3 +-
 platform/common/emu.c          | 117 +++++++++++++++++++++++++++------
 platform/common/emu.h          |   2 +
 platform/common/input_pico.h   |  12 ++--
 platform/common/inputmap_kbd.c |   6 +-
 platform/common/menu_pico.c    |  47 ++++++-------
 platform/gp2x/emu.c            |  30 ++++++---
 platform/libpicofe             |   2 +-
 platform/linux/blit.c          |   1 +
 platform/linux/emu.c           |  27 ++++++--
 platform/pandora/plat.c        |  32 ++++++---
 platform/ps2/emu.c             |  30 ++++++---
 platform/psp/emu.c             |  32 ++++++---
 14 files changed, 255 insertions(+), 104 deletions(-)

diff --git a/Makefile b/Makefile
index cbed7900..3fe87cbe 100644
--- a/Makefile
+++ b/Makefile
@@ -246,18 +246,24 @@ endif
 ifeq "$(PLATFORM)" "libretro"
 OBJS += platform/libretro/libretro.o
 ifneq ($(STATIC_LINKING), 1)
+CFLAGS += -DHAVE_ZLIB
+OBJS += platform/libretro/libretro-common/formats/png/rpng.o
+OBJS += platform/libretro/libretro-common/streams/trans_stream.o
+OBJS += platform/libretro/libretro-common/streams/trans_stream_pipe.o
+OBJS += platform/libretro/libretro-common/streams/trans_stream_zlib.o
+OBJS += platform/libretro/libretro-common/file/file_path_io.o
+OBJS += platform/libretro/libretro-common/file/file_path.o
+OBJS += platform/libretro/libretro-common/vfs/vfs_implementation.o
+OBJS += platform/libretro/libretro-common/time/rtime.o
+OBJS += platform/libretro/libretro-common/string/stdstring.o
 OBJS += platform/libretro/libretro-common/compat/compat_strcasestr.o
+OBJS += platform/libretro/libretro-common/encodings/encoding_utf.o
+OBJS += platform/libretro/libretro-common/compat/compat_strl.o
 ifeq "$(USE_LIBRETRO_VFS)" "1"
 OBJS += platform/libretro/libretro-common/compat/compat_posix_string.o
-OBJS += platform/libretro/libretro-common/compat/compat_strl.o
 OBJS += platform/libretro/libretro-common/compat/fopen_utf8.o
-OBJS += platform/libretro/libretro-common/encodings/encoding_utf.o
-OBJS += platform/libretro/libretro-common/string/stdstring.o
-OBJS += platform/libretro/libretro-common/time/rtime.o
 OBJS += platform/libretro/libretro-common/streams/file_stream.o
 OBJS += platform/libretro/libretro-common/streams/file_stream_transforms.o
-OBJS += platform/libretro/libretro-common/file/file_path.o
-OBJS += platform/libretro/libretro-common/vfs/vfs_implementation.o
 endif
 endif
 ifeq "$(USE_LIBRETRO_VFS)" "1"
diff --git a/pico/pico/pico.c b/pico/pico/pico.c
index 99f21f4a..94c80f20 100644
--- a/pico/pico/pico.c
+++ b/pico/pico/pico.c
@@ -60,6 +60,5 @@ PICO_INTERNAL void PicoInitPico(void)
 
   PicoIn.AHW = PAHW_PICO;
   memset(&PicoPicohw, 0, sizeof(PicoPicohw));
-  PicoPicohw.pen_pos[0] = 0x03c + 320/2;
-  PicoPicohw.pen_pos[1] = 0x200 + 240/2;
+  PicoPicohw.pen_pos[0] = PicoPicohw.pen_pos[1] = 0x8000;
 }
diff --git a/platform/common/emu.c b/platform/common/emu.c
index d576b439..051f8b4d 100644
--- a/platform/common/emu.c
+++ b/platform/common/emu.c
@@ -1,6 +1,7 @@
 /*
  * PicoDrive
  * (C) notaz, 2007-2010
+ * (C) irixxxx, 2019-2024
  *
  * This work is licensed under the terms of MAME license.
  * See COPYING file in the top-level directory.
@@ -19,6 +20,7 @@
 #include "../libpicofe/fonts.h"
 #include "../libpicofe/sndout.h"
 #include "../libpicofe/lprintf.h"
+#include "../libpicofe/readpng.h"
 #include "../libpicofe/plat.h"
 #include "emu.h"
 #include "input_pico.h"
@@ -618,7 +620,7 @@ void emu_prep_defconfig(void)
 {
 	memset(&defaultConfig, 0, sizeof(defaultConfig));
 	defaultConfig.EmuOpt    = EOPT_EN_SRAM | EOPT_EN_SOUND | EOPT_16BPP |
-				  EOPT_EN_CD_LEDS | EOPT_GZIP_SAVES | 0x10/*?*/;
+				  EOPT_EN_CD_LEDS | EOPT_GZIP_SAVES | EOPT_PICO_PEN;
 	defaultConfig.s_PicoOpt = POPT_EN_SNDFILTER|POPT_EN_GG_LCD|POPT_EN_YM2413 |
 				  POPT_EN_STEREO|POPT_EN_FM|POPT_EN_PSG|POPT_EN_Z80 |
 				  POPT_EN_MCD_PCM|POPT_EN_MCD_CDDA|POPT_EN_MCD_GFX |
@@ -1041,19 +1043,76 @@ void emu_reset_game(void)
 	reset_timing = 1;
 }
 
-void run_events_pico(unsigned int events)
+static int pico_page;
+static int pico_w, pico_h;
+static u16 *pico_overlay;
+
+static u16 *load_pico_overlay(int page, int w, int h)
 {
-	if (events & PEV_PICO_SWINP) {
-		pico_inp_mode++;
-		if (pico_inp_mode > 2)
-			pico_inp_mode = 0;
-		switch (pico_inp_mode) {
-			case 2: emu_status_msg("Input: Pen on Pad"); break;
-			case 1: emu_status_msg("Input: Pen on Storyware"); break;
-			case 0: emu_status_msg("Input: Joystick"); break;
+	static const char *pic_exts[] = { "png", "PNG" };
+	char *ext, *fname = NULL;
+	int extpos, i;
+
+	if (pico_page == page && pico_w == w && pico_h == h)
+		return pico_overlay;
+	pico_page = page;
+	pico_w = w, pico_h = h;
+
+	ext = strrchr(rom_fname_loaded, '.');
+	extpos = ext ? ext-rom_fname_loaded : strlen(rom_fname_loaded);
+	strcpy(static_buff, rom_fname_loaded);
+	static_buff[extpos++] = '_';
+	if (page < 0) {
+		static_buff[extpos++] = 'p';
+		static_buff[extpos++] = 'a';
+		static_buff[extpos++] = 'd';
+	} else
+		static_buff[extpos++] = '0'+PicoPicohw.page;
+	static_buff[extpos++] = '.';
+
+	for (i = 0; i < ARRAY_SIZE(pic_exts); i++) {
+		strcpy(static_buff+extpos, pic_exts[i]);
+		if (access(static_buff, R_OK) == 0) {
+			printf("found Pico file: %s\n", static_buff);
+			fname = static_buff;
+			break;
 		}
-		PicoPicohw.pen_pos[0] = PicoPicohw.pen_pos[1] = 0x8000;
 	}
+
+	pico_overlay = realloc(pico_overlay, w*h*2);
+	memset(pico_overlay, 0, w*h*2);
+	if (!fname || !pico_overlay || readpng(pico_overlay, fname, READPNG_SCALE, w, h)) {
+		if (pico_overlay)
+			free(pico_overlay);
+		pico_overlay = NULL;
+	}
+
+	return pico_overlay;
+}
+
+void emu_pico_overlay(u16 *pd, int w, int h, int pitch)
+{
+	u16 *overlay = NULL;
+	int y, oh = h;
+
+	// get overlay
+	if (pico_inp_mode == 1) {
+		oh = (w/2 < h ? w/2 : h); // storyware has squished h
+		overlay = load_pico_overlay(PicoPicohw.page, w, oh);
+	} else if (pico_inp_mode == 2)
+		overlay = load_pico_overlay(-1, w, oh);
+
+	// copy overlay onto buffer
+	if (overlay) {
+		for (y = 0; y < oh; y++)
+			memcpy(pd + y*pitch, overlay + y*w, w*2);
+		if (y < h)
+			memset(pd + y*pitch, 0, w*2);
+	}
+}
+
+void run_events_pico(unsigned int events)
+{
 	if (events & PEV_PICO_PPREV) {
 		PicoPicohw.page--;
 		if (PicoPicohw.page < 0)
@@ -1066,16 +1125,35 @@ void run_events_pico(unsigned int events)
 			PicoPicohw.page = 6;
 		emu_status_msg("Page %i", PicoPicohw.page);
 	}
-	if (events & PEV_PICO_SHPEN) {
-		currentConfig.EmuOpt ^= EOPT_PICO_PEN;
-		emu_status_msg("%s Pen", currentConfig.EmuOpt & EOPT_PICO_PEN ? "Show" : "Hide");
+	if (events & PEV_PICO_STORY) {
+		if (pico_inp_mode == 1) {
+			pico_inp_mode = 0;
+			emu_status_msg("Input: D-Pad");
+		} else {
+			pico_inp_mode = 1;
+			emu_status_msg("Input: Pen on Storyware");
+		}
+	}
+	if (events & PEV_PICO_PAD) {
+		if (pico_inp_mode == 2) {
+			pico_inp_mode = 0;
+			emu_status_msg("Input: D-Pad");
+		} else {
+			pico_inp_mode = 2;
+			emu_status_msg("Input: Pen on Pad");
+		}
 	}
-	if (events & PEV_PICO_PPOSV) {
+	if (events & PEV_PICO_PENST) {
 		PicoPicohw.pen_pos[0] ^= 0x8000;
 		PicoPicohw.pen_pos[1] ^= 0x8000;
 		emu_status_msg("Pen %s", PicoPicohw.pen_pos[0] & 0x8000 ? "Up" : "Down");
 	}
 
+	if ((currentConfig.EmuOpt & EOPT_PICO_PEN) &&
+			(PicoIn.pad[0]&0x20) && pico_inp_mode && pico_overlay) {
+		pico_inp_mode = 0;
+		emu_status_msg("Input: D-Pad");
+	}
 	if (pico_inp_mode == 0)
 		return;
 
@@ -1086,14 +1164,15 @@ void run_events_pico(unsigned int events)
 	if (PicoIn.pad[0] & 8) pico_pen_x++;
 	PicoIn.pad[0] &= ~0x0f; // release UDLR
 
+	/* cursor position, cursor drawing must not cross screen borders */
 	if (pico_pen_y < PICO_PEN_ADJUST_Y)
 		pico_pen_y = PICO_PEN_ADJUST_Y;
-	if (pico_pen_y > 224-1 - PICO_PEN_ADJUST_Y)
-		pico_pen_y = 224-1 - PICO_PEN_ADJUST_Y;
+	if (pico_pen_y > 223-1 - PICO_PEN_ADJUST_Y)
+		pico_pen_y = 223-1 - PICO_PEN_ADJUST_Y;
 	if (pico_pen_x < PICO_PEN_ADJUST_X)
 		pico_pen_x = PICO_PEN_ADJUST_X;
-	if (pico_pen_x > 320-1 - PICO_PEN_ADJUST_X)
-		pico_pen_x = 320-1 - PICO_PEN_ADJUST_X;
+	if (pico_pen_x > 319-1 - PICO_PEN_ADJUST_X)
+		pico_pen_x = 319-1 - PICO_PEN_ADJUST_X;
 
 	PicoPicohw.pen_pos[0] &= 0x8000;
 	PicoPicohw.pen_pos[1] &= 0x8000;
diff --git a/platform/common/emu.h b/platform/common/emu.h
index 959ce0b5..c477883e 100644
--- a/platform/common/emu.h
+++ b/platform/common/emu.h
@@ -160,6 +160,8 @@ void  emu_get_game_name(char *str150);
 void  emu_set_fastforward(int set_on);
 void  emu_status_msg(const char *format, ...);
 
+void  emu_pico_overlay(unsigned short *pd, int w, int h, int pitch);
+
 /* default sound code */
 void  emu_sound_start(void);
 void  emu_sound_stop(void);
diff --git a/platform/common/input_pico.h b/platform/common/input_pico.h
index 10d65118..2f3de418 100644
--- a/platform/common/input_pico.h
+++ b/platform/common/input_pico.h
@@ -27,9 +27,9 @@
 #define PEVB_FF         22
 #define PEVB_PICO_PNEXT 21
 #define PEVB_PICO_PPREV 20
-#define PEVB_PICO_SWINP 19
-#define PEVB_PICO_SHPEN 18
-#define PEVB_PICO_PPOSV 17
+#define PEVB_PICO_STORY 19
+#define PEVB_PICO_PAD   18
+#define PEVB_PICO_PENST 17
 #define PEVB_RESET      16
 
 #define PEV_VOL_DOWN    (1 << PEVB_VOL_DOWN)
@@ -43,9 +43,9 @@
 #define PEV_FF          (1 << PEVB_FF)
 #define PEV_PICO_PNEXT  (1 << PEVB_PICO_PNEXT)
 #define PEV_PICO_PPREV  (1 << PEVB_PICO_PPREV)
-#define PEV_PICO_SWINP  (1 << PEVB_PICO_SWINP)
-#define PEV_PICO_SHPEN  (1 << PEVB_PICO_SHPEN)
-#define PEV_PICO_PPOSV  (1 << PEVB_PICO_PPOSV)
+#define PEV_PICO_STORY  (1 << PEVB_PICO_STORY)
+#define PEV_PICO_PAD    (1 << PEVB_PICO_PAD)
+#define PEV_PICO_PENST  (1 << PEVB_PICO_PENST)
 #define PEV_RESET       (1 << PEVB_RESET)
 
 #define PEV_MASK 0x7fff0000
diff --git a/platform/common/inputmap_kbd.c b/platform/common/inputmap_kbd.c
index 821a8ee0..c41abbb3 100644
--- a/platform/common/inputmap_kbd.c
+++ b/platform/common/inputmap_kbd.c
@@ -29,9 +29,9 @@ const struct in_default_bind in_sdl_defbinds[] = {
 	{ 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_F9,     IN_BINDTYPE_EMU, PEVB_PICO_SHPEN },
-	{ SDLK_F10,    IN_BINDTYPE_EMU, PEVB_PICO_PPOSV },
+	{ SDLK_F8,     IN_BINDTYPE_EMU, PEVB_PICO_STORY },
+	{ SDLK_F9,     IN_BINDTYPE_EMU, PEVB_PICO_PAD },
+	{ SDLK_F10,    IN_BINDTYPE_EMU, PEVB_PICO_PENST },
 	{ SDLK_BACKSPACE, IN_BINDTYPE_EMU, PEVB_FF },
 	{ 0, 0, 0 }
 };
diff --git a/platform/common/menu_pico.c b/platform/common/menu_pico.c
index ffd91612..00f2aa69 100644
--- a/platform/common/menu_pico.c
+++ b/platform/common/menu_pico.c
@@ -1,6 +1,7 @@
 /*
  * PicoDrive
  * (C) notaz, 2010,2011
+ * (C) irixxxx, 2023,2024
  *
  * This work is licensed under the terms of MAME license.
  * See COPYING file in the top-level directory.
@@ -349,21 +350,21 @@ me_bind_action me_ctrl_actions[] =
 
 me_bind_action emuctrl_actions[] =
 {
-	{ "Load State       ", PEV_STATE_LOAD },
-	{ "Save State       ", PEV_STATE_SAVE },
-	{ "Prev Save Slot   ", PEV_SSLOT_PREV },
-	{ "Next Save Slot   ", PEV_SSLOT_NEXT },
-	{ "Switch Renderer  ", PEV_SWITCH_RND },
-	{ "Volume Down      ", PEV_VOL_DOWN },
-	{ "Volume Up        ", PEV_VOL_UP },
-	{ "Fast forward     ", PEV_FF },
-	{ "Reset Game       ", PEV_RESET },
-	{ "Enter Menu       ", PEV_MENU },
-	{ "Pico Next page   ", PEV_PICO_PNEXT },
-	{ "Pico Prev page   ", PEV_PICO_PPREV },
-	{ "Pico Switch input", PEV_PICO_SWINP },
-	{ "Pico Pen sensor  ", PEV_PICO_PPOSV },
-	{ "Pico Show pen    ", PEV_PICO_SHPEN },
+	{ "Load State     ", PEV_STATE_LOAD },
+	{ "Save State     ", PEV_STATE_SAVE },
+	{ "Prev Save Slot ", PEV_SSLOT_PREV },
+	{ "Next Save Slot ", PEV_SSLOT_NEXT },
+	{ "Switch Renderer", PEV_SWITCH_RND },
+	{ "Volume Down    ", PEV_VOL_DOWN },
+	{ "Volume Up      ", PEV_VOL_UP },
+	{ "Fast forward   ", PEV_FF },
+	{ "Reset Game     ", PEV_RESET },
+	{ "Enter Menu     ", PEV_MENU },
+	{ "Pico Next page ", PEV_PICO_PNEXT },
+	{ "Pico Prev page ", PEV_PICO_PPREV },
+	{ "Pico Storyware ", PEV_PICO_STORY },
+	{ "Pico Pad       ", PEV_PICO_PAD },
+	{ "Pico Pen state ", PEV_PICO_PENST },
 	{ NULL,                0 }
 };
 
@@ -463,15 +464,17 @@ static const char h_fmsound[]  = "Disabling improves performance, but breaks sou
 static const char h_dacnoise[] = "FM chips in the 1st Mega Drive model have DAC noise,\n"
 				"newer models used different chips without this";
 static const char h_fmfilter[] = "Improves sound accuracy but is noticeably slower,\n"
-				"best´quality if native rate isn't working";
+				"best quality if native rate isn't working";
+static const char h_picopen[]  = "Enabling resets Pico display and d-pad input back to\n"
+				"screen if the Pico pen button is pressed";
 
 static menu_entry e_menu_md_options[] =
 {
-	mee_enum_h    ("Renderer",        MA_OPT_RENDERER, currentConfig.renderer, renderer_names, h_renderer),
-	mee_onoff_h   ("FM audio",        MA_OPT2_ENABLE_YM2612, PicoIn.opt, POPT_EN_FM, h_fmsound),
-	mee_onoff_h   ("FM filter",       MA_OPT_FM_FILTER, PicoIn.opt, POPT_EN_FM_FILTER, h_fmfilter),
-	mee_onoff_h   ("FM DAC noise",    MA_OPT2_ENABLE_YM_DAC, PicoIn.opt, POPT_EN_FM_DAC, h_dacnoise),
-	mee_onoff     ("Show Pico pen",   MA_OPT_PICO_PEN, currentConfig.EmuOpt, EOPT_PICO_PEN),
+	mee_enum_h    ("Renderer",                  MA_OPT_RENDERER, currentConfig.renderer, renderer_names, h_renderer),
+	mee_onoff_h   ("FM audio",                  MA_OPT2_ENABLE_YM2612, PicoIn.opt, POPT_EN_FM, h_fmsound),
+	mee_onoff_h   ("FM filter",                 MA_OPT_FM_FILTER, PicoIn.opt, POPT_EN_FM_FILTER, h_fmfilter),
+	mee_onoff_h   ("FM DAC noise",              MA_OPT2_ENABLE_YM_DAC, PicoIn.opt, POPT_EN_FM_DAC, h_dacnoise),
+	mee_onoff_h   ("Pen button shows screen",   MA_OPT_PICO_PEN, currentConfig.EmuOpt, EOPT_PICO_PEN, h_picopen),
 	mee_end,
 };
 
@@ -1295,8 +1298,6 @@ static const char *mgn_picopage(int id, int *offs)
 
 static int mh_picopage(int id, int keys)
 {
-	int ret;
-
 	if (keys & (PBTN_LEFT|PBTN_RIGHT)) { // multi choice
 		PicoPicohw.page += (keys & PBTN_LEFT) ? -1 : 1;
 		if (PicoPicohw.page < 0) PicoPicohw.page = 6;
diff --git a/platform/gp2x/emu.c b/platform/gp2x/emu.c
index f20f29a2..aae7fbd5 100644
--- a/platform/gp2x/emu.c
+++ b/platform/gp2x/emu.c
@@ -196,9 +196,12 @@ static void draw_pico_ptr(void)
 {
 	int up = (PicoPicohw.pen_pos[0]|PicoPicohw.pen_pos[1]) & 0x8000;
 	int x, y, pitch = 320, offs;
+	// storyware pages are actually squished, 2:1
+	int h = (pico_inp_mode == 1 ? 160 : linecount);
+	if (h < 224) y++;
 
 	x = ((pico_pen_x * colcount  * ((1ULL<<32)/320 + 1)) >> 32) + firstcol;
-	y = ((pico_pen_y * linecount * ((1ULL<<32)/224 + 1)) >> 32) + firstline;
+	y = ((pico_pen_y * h         * ((1ULL<<32)/224 + 1)) >> 32) + firstline;
 
 	if (currentConfig.EmuOpt & EOPT_WIZ_TEAR_FIX) {
 		pitch = 240;
@@ -210,16 +213,18 @@ static void draw_pico_ptr(void)
 		unsigned short *p = (unsigned short *)g_screen_ptr + offs;
 		int o = (up ? 0x0000 : 0xffff), _ = (up ? 0xffff : 0x0000);
 
-		p[-pitch-1] ^= _; p[-pitch] ^= o; p[-pitch+1] ^= _;
-		p[1]        ^= o; p[0]      ^= o; p[1]        ^= o;
-		p[pitch-1]  ^= _; p[pitch]  ^= o; p[pitch+1]  ^= _;
+		p[-pitch-1] ^= o; p[-pitch] ^= _; p[-pitch+1] ^= _; p[-pitch+2] ^= o;
+		p[-1]       ^= _; p[0]      ^= o; p[1]        ^= o; p[2]        ^= _;
+		p[pitch-1]  ^= _; p[pitch]  ^= o; p[pitch+1]  ^= o; p[pitch+2]  ^= _;
+		p[2*pitch-1]^= o; p[2*pitch]^= _; p[2*pitch+1]^= _; p[2*pitch+2]^= o;
 	} else {
 		unsigned char *p = (unsigned char *)g_screen_ptr + offs;
 		int o = (up ? 0xe0 : 0xf0), _ = (up ? 0xf0 : 0xe0);
 
-		p[-pitch-1]  = _; p[-pitch]  = o; p[-pitch+1]  = _;
-		p[-1]        = o; p[0]       = o; p[1]         = o;
-		p[pitch-1]   = _; p[pitch]   = o; p[pitch+1]   = _;
+		p[-pitch-1] = o; p[-pitch] = _; p[-pitch+1] = _; p[-pitch+2] = o;
+		p[-1]       = _; p[0]      = o; p[1]        = o; p[2]        = _;
+		p[pitch-1]  = _; p[pitch]  = o; p[pitch+1]  = o; p[pitch+2]  = _;
+		p[2*pitch-1]= o; p[2*pitch]= _; p[2*pitch+1]= _; p[2*pitch+2]= o;
 	}
 }
 
@@ -434,8 +439,15 @@ void pemu_finalize_frame(const char *fps, const char *notice)
 		osd_text(osd_fps_x, osd_y, fps);
 	if ((PicoIn.AHW & PAHW_MCD) && (emu_opt & EOPT_EN_CD_LEDS))
 		draw_cd_leds();
-	if ((PicoIn.AHW & PAHW_PICO) && (currentConfig.EmuOpt & EOPT_PICO_PEN))
-		if (pico_inp_mode) draw_pico_ptr();
+	if (PicoIn.AHW & PAHW_PICO) {
+		int h = linecount, w = colcount;
+		u16 *pd = g_screen_ptr + firstline*g_screen_ppitch + firstcol;
+
+		if (pico_inp_mode && is_16bit_mode())
+			emu_pico_overlay(pd, w, h, g_screen_ppitch);
+		if (pico_inp_mode /*== 2 || overlay*/)
+			draw_pico_ptr();
+	}
 }
 
 void plat_video_flip(void)
diff --git a/platform/libpicofe b/platform/libpicofe
index 82b48547..4563c131 160000
--- a/platform/libpicofe
+++ b/platform/libpicofe
@@ -1 +1 @@
-Subproject commit 82b4854771302e23201de274eee2969fc28be8be
+Subproject commit 4563c13167deb97851397789bf2fb95bb44cf1ed
diff --git a/platform/linux/blit.c b/platform/linux/blit.c
index ee2b6d0e..e10aa6b7 100644
--- a/platform/linux/blit.c
+++ b/platform/linux/blit.c
@@ -1,6 +1,7 @@
 /*
  * PicoDrive
  * (C) notaz, 2006,2009
+ * (C) irixxxx, 2022
  *
  * This work is licensed under the terms of MAME license.
  * See COPYING file in the top-level directory.
diff --git a/platform/linux/emu.c b/platform/linux/emu.c
index ba384ba4..5d65ad5e 100644
--- a/platform/linux/emu.c
+++ b/platform/linux/emu.c
@@ -1,6 +1,7 @@
 /*
  * PicoDrive
  * (C) notaz, 2006-2010
+ * (C) irixxxx, 2019-2024
  *
  * This work is licensed under the terms of MAME license.
  * See COPYING file in the top-level directory.
@@ -92,14 +93,18 @@ static void draw_pico_ptr(void)
 	int pitch = g_screen_ppitch;
 	u16 *p = g_screen_ptr;
 	int x = pico_pen_x, y = pico_pen_y;
+	// storyware pages are actually squished, 2:1
+	int h = (pico_inp_mode == 1 ? 160 : out_h);
+	if (h < 224) y++;
 
 	x = (x * out_w * ((1ULL<<32) / 320 + 1)) >> 32;
-	y = (y * out_h * ((1ULL<<32) / 224 + 1)) >> 32;
+	y = (y *     h * ((1ULL<<32) / 224 + 1)) >> 32;
 	p += (screen_y+y)*pitch + (screen_x+x);
 
-	p[-pitch-1] ^= _; p[-pitch] ^= o; p[-pitch+1] ^= _;
-	p[-1]       ^= o; p[0]      ^= o; p[1]        ^= o;
-	p[pitch-1]  ^= _; p[pitch]  ^= o; p[pitch+1]  ^= _;
+	p[-pitch-1] ^= o; p[-pitch] ^= _; p[-pitch+1] ^= _; p[-pitch+2] ^= o;
+	p[-1]       ^= _; p[0]      ^= o; p[1]        ^= o; p[2]        ^= _;
+	p[pitch-1]  ^= _; p[pitch]  ^= o; p[pitch+1]  ^= o; p[pitch+2]  ^= _;
+	p[2*pitch-1]^= o; p[2*pitch]^= _; p[2*pitch+1]^= _; p[2*pitch+2]^= o;
 }
 
 /* render/screen buffer handling:
@@ -194,6 +199,7 @@ void pemu_finalize_frame(const char *fps, const char *notice)
 		u16 *ps = ghost_buf;
 		int y, h = currentConfig.vscaling == EOPT_SCALE_SW ? 240:out_h;
 		int w = currentConfig.scaling == EOPT_SCALE_SW ? 320:out_w;
+
 		if (currentConfig.ghosting == 1)
 			for (y = 0; y < h; y++) {
 				v_blend((u32 *)pd, (u32 *)ps, w/2, p_075_round);
@@ -208,8 +214,17 @@ void pemu_finalize_frame(const char *fps, const char *notice)
 			}
 	}
 
-	if ((PicoIn.AHW & PAHW_PICO) && (currentConfig.EmuOpt & EOPT_PICO_PEN))
-		if (pico_inp_mode) draw_pico_ptr();
+	if (PicoIn.AHW & PAHW_PICO) {
+		int h = currentConfig.vscaling == EOPT_SCALE_SW ? 240:out_h;
+		int w = currentConfig.scaling == EOPT_SCALE_SW ? 320:out_w;
+		u16 *pd = screen_buffer(g_screen_ptr) + out_y*g_screen_ppitch + out_x;
+
+		if (pico_inp_mode)
+			emu_pico_overlay(pd, w, h, g_screen_ppitch);
+		if (pico_inp_mode /*== 2 || overlay*/)
+			draw_pico_ptr();
+	}
+
 	if (notice)
 		emu_osd_text16(4, g_screen_height - 8, notice);
 	if (currentConfig.EmuOpt & EOPT_SHOW_FPS)
diff --git a/platform/pandora/plat.c b/platform/pandora/plat.c
index 32177ace..20f9ca8f 100644
--- a/platform/pandora/plat.c
+++ b/platform/pandora/plat.c
@@ -75,9 +75,9 @@ static struct in_default_bind in_evdev_defbinds[] =
 	{ KEY_4,	IN_BINDTYPE_EMU, PEVB_SSLOT_NEXT },
 	{ KEY_5,	IN_BINDTYPE_EMU, PEVB_PICO_PPREV },
 	{ KEY_6,	IN_BINDTYPE_EMU, PEVB_PICO_PNEXT },
-	{ KEY_7,	IN_BINDTYPE_EMU, PEVB_PICO_SWINP },
-	{ KEY_8,	IN_BINDTYPE_EMU, PEVB_PICO_SHPEN },
-	{ KEY_9,	IN_BINDTYPE_EMU, PEVB_PICO_PPOSV },
+	{ KEY_7,	IN_BINDTYPE_EMU, PEVB_PICO_STORY },
+	{ KEY_8,	IN_BINDTYPE_EMU, PEVB_PICO_PAD },
+	{ KEY_9,	IN_BINDTYPE_EMU, PEVB_PICO_PENST },
 	{ 0, 0, 0 }
 };
 
@@ -143,20 +143,32 @@ static void draw_pico_ptr(void)
 	int o = (up ? 0x0000 : 0xffff), _ = (up ? 0xffff : 0x0000);
 	int x = pico_pen_x, y = pico_pen_y, pitch = g_screen_ppitch;
 	unsigned short *p = (unsigned short *)g_screen_ptr;
+	// storyware pages are actually squished, 2:1
+	int h = (pico_inp_mode == 1 ? 160 : saved_line_count);
+	if (h < 224) y++;
 
-	x = (x * saved_col_count  * ((1ULL<<32) / 320 + 1)) >> 32;
-	y = (y * saved_line_count * ((1ULL<<32) / 224 + 1)) >> 32;
+	x = (x * saved_col_count * ((1ULL<<32) / 320 + 1)) >> 32;
+	y = (y * h               * ((1ULL<<32) / 224 + 1)) >> 32;
 	p += (saved_start_col+x) + (saved_start_line+y) * pitch;
 
-	p[-pitch-1] ^= _; p[-pitch] ^= o; p[-pitch+1] ^= _;
-	p[-1]       ^= o; p[0]      ^= o; p[1]        ^= o;
-	p[pitch-1]  ^= _; p[pitch]  ^= o; p[pitch+1]  ^= _;
+	p[-pitch-1] ^= o; p[-pitch] ^= _; p[-pitch+1] ^= _; p[-pitch+2] ^= o;
+	p[-1]       ^= _; p[0]      ^= o; p[1]        ^= o; p[2]        ^= _;
+	p[pitch-1]  ^= _; p[pitch]  ^= o; p[pitch+1]  ^= o; p[pitch+2]  ^= _;
+	p[2*pitch-1]^= o; p[2*pitch]^= _; p[2*pitch+1]^= _; p[2*pitch+2]^= o;
 }
 
 void pemu_finalize_frame(const char *fps, const char *notice)
 {
-	if ((PicoIn.AHW & PAHW_PICO) && (currentConfig.EmuOpt & EOPT_PICO_PEN))
-		if (pico_inp_mode) draw_pico_ptr();
+	if (PicoIn.AHW & PAHW_PICO) {
+		int h = saved_line_count, w = saved_col_count;
+		u16 *pd = g_screen_ptr + saved_start_line*g_screen_ppitch
+					+ saved_start_col;
+
+		if (pico_inp_mode)
+			emu_pico_overlay(pd, w, h, g_screen_ppitch);
+		if (pico_inp_mode /*== 2 || overlay*/)
+			draw_pico_ptr();
+	}
 	if (notice && notice[0])
 		emu_osd_text16(2 + g_osd_start_x, g_osd_y, notice);
 	if (fps && fps[0] && (currentConfig.EmuOpt & EOPT_SHOW_FPS))
diff --git a/platform/ps2/emu.c b/platform/ps2/emu.c
index b15c6036..35db9744 100644
--- a/platform/ps2/emu.c
+++ b/platform/ps2/emu.c
@@ -713,9 +713,12 @@ static void draw_pico_ptr(void)
 	int up = (PicoPicohw.pen_pos[0]|PicoPicohw.pen_pos[1]) & 0x8000;
 	int x = pico_pen_x, y = pico_pen_y, offs;
 	int pitch = g_screen_ppitch;
+	// storyware pages are actually squished, 2:1
+	int h = (pico_inp_mode == 1 ? 160 : out_h);
+	if (h < 224) y++;
 
 	x = (x * out_w * ((1ULL<<32) / 320 + 1)) >> 32;
-	y = (y * out_h * ((1ULL<<32) / 224 + 1)) >> 32;
+	y = (y *     h * ((1ULL<<32) / 224 + 1)) >> 32;
 
 	offs = pitch * (out_y+y) + (out_x+x);
 
@@ -723,16 +726,18 @@ static void draw_pico_ptr(void)
 		unsigned short *p = (unsigned short *)g_screen_ptr + offs;
 		int o = (up ? 0x0000 : 0x7fff), _ = (up ? 0x7fff : 0x0000);
 
-		p[-pitch-1] ^= _; p[-pitch] ^= o; p[-pitch+1] ^= _;
-		p[1]        ^= o; p[0]      ^= o; p[1]        ^= o;
-		p[pitch-1]  ^= _; p[pitch]  ^= o; p[pitch+1]  ^= _;
+		p[-pitch-1] ^= o; p[-pitch] ^= _; p[-pitch+1] ^= _; p[-pitch+2] ^= o;
+		p[-1]       ^= _; p[0]      ^= o; p[1]        ^= o; p[2]        ^= _;
+		p[pitch-1]  ^= _; p[pitch]  ^= o; p[pitch+1]  ^= o; p[pitch+2]  ^= _;
+		p[2*pitch-1]^= o; p[2*pitch]^= _; p[2*pitch+1]^= _; p[2*pitch+2]^= o;
 	} else {
 		unsigned char *p = (unsigned char *)g_screen_ptr + offs + 8;
 		int o = (up ? 0xe0 : 0xf0), _ = (up ? 0xf0 : 0xe0);
 
-		p[-pitch-1]  = _; p[-pitch]  = o; p[-pitch+1]  = _;
-		p[-1]        = o; p[0]       = o; p[1]         = o;
-		p[pitch-1]   = _; p[pitch]   = o; p[pitch+1]   = _;
+		p[-pitch-1] = o; p[-pitch] = _; p[-pitch+1] = _; p[-pitch+2] = o;
+		p[-1]       = _; p[0]      = o; p[1]        = o; p[2]        = _;
+		p[pitch-1]  = _; p[pitch]  = o; p[pitch+1]  = o; p[pitch+2]  = _;
+		p[2*pitch-1]= o; p[2*pitch]= _; p[2*pitch+1]= _; p[2*pitch+2]= o;
 	}
 }
 
@@ -748,8 +753,15 @@ void pemu_finalize_frame(const char *fps, const char *notice)
 {
 	int emu_opt = currentConfig.EmuOpt;
 
-	if ((PicoIn.AHW & PAHW_PICO) && (currentConfig.EmuOpt & EOPT_PICO_PEN))
-		if (pico_inp_mode) draw_pico_ptr();
+	if (PicoIn.AHW & PAHW_PICO) {
+		int h = out_h, w = out_w;
+		u16 *pd = g_screen_ptr + out_y*g_screen_ppitch + out_x;
+
+		if (pico_inp_mode && is_16bit_mode())
+			emu_pico_overlay(pd, w, h, g_screen_ppitch);
+		if (pico_inp_mode /*== 2 || overlay*/)
+			draw_pico_ptr();
+	}
 
 	osd_buf_cnt = 0;
 	if (notice)      osd_text(4, notice);
diff --git a/platform/psp/emu.c b/platform/psp/emu.c
index 624d02a9..948102bb 100644
--- a/platform/psp/emu.c
+++ b/platform/psp/emu.c
@@ -373,26 +373,31 @@ static void draw_pico_ptr(void)
 	int up = (PicoPicohw.pen_pos[0]|PicoPicohw.pen_pos[1]) & 0x8000;
 	int x = pico_pen_x, y = pico_pen_y, offs;
 	int pitch = g_screen_ppitch;
+	// storyware pages are actually squished, 2:1
+	int h = (pico_inp_mode == 1 ? 160 : out_h);
+	if (h < 224) y++;
 
 	x = (x * out_w * ((1ULL<<32) / 320 + 1)) >> 32;
-	y = (y * out_h * ((1ULL<<32) / 224 + 1)) >> 32;
+	y = (y *     h * ((1ULL<<32) / 224 + 1)) >> 32;
 
-	offs = 512 * (out_y+y) + (out_x+x);
+	offs = pitch * (out_y+y) + (out_x+x);
 
 	if (is_16bit_mode()) {
 		unsigned short *p = (unsigned short *)g_screen_ptr + offs;
 		int o = (up ? 0x0000 : 0xffff), _ = (up ? 0xffff : 0x0000);
 
-		p[-pitch-1] ^= _; p[-pitch] ^= o; p[-pitch+1] ^= _;
-		p[1]        ^= o; p[0]      ^= o; p[1]        ^= o;
-		p[pitch-1]  ^= _; p[pitch]  ^= o; p[pitch+1]  ^= _;
+		p[-pitch-1] ^= o; p[-pitch] ^= _; p[-pitch+1] ^= _; p[-pitch+2] ^= o;
+		p[-1]       ^= _; p[0]      ^= o; p[1]        ^= o; p[2]        ^= _;
+		p[pitch-1]  ^= _; p[pitch]  ^= o; p[pitch+1]  ^= o; p[pitch+2]  ^= _;
+		p[2*pitch-1]^= o; p[2*pitch]^= _; p[2*pitch+1]^= _; p[2*pitch+2]^= o;
 	} else {
 		unsigned char *p = (unsigned char *)g_screen_ptr + offs + 8;
 		int o = (up ? 0xe0 : 0xf0), _ = (up ? 0xf0 : 0xe0);
 
-		p[-pitch-1]  = _; p[-pitch]  = o; p[-pitch+1]  = _;
-		p[-1]        = o; p[0]       = o; p[1]         = o;
-		p[pitch-1]   = _; p[pitch]   = o; p[pitch+1]   = _;
+		p[-pitch-1] = o; p[-pitch] = _; p[-pitch+1] = _; p[-pitch+2] = o;
+		p[-1]       = _; p[0]      = o; p[1]        = o; p[2]        = _;
+		p[pitch-1]  = _; p[pitch]  = o; p[pitch+1]  = o; p[pitch+2]  = _;
+		p[2*pitch-1]= o; p[2*pitch]= _; p[2*pitch+1]= _; p[2*pitch+2]= o;
 	}
 }
 
@@ -697,8 +702,15 @@ void pemu_finalize_frame(const char *fps, const char *notice)
 {
 	int emu_opt = currentConfig.EmuOpt;
 
-	if ((PicoIn.AHW & PAHW_PICO) && (currentConfig.EmuOpt & EOPT_PICO_PEN))
-		if (pico_inp_mode) draw_pico_ptr();
+	if (PicoIn.AHW & PAHW_PICO) {
+		int h = out_h, w = out_w;
+		u16 *pd = g_screen_ptr + out_y*g_screen_ppitch + out_x;
+
+		if (pico_inp_mode && is_16bit_mode())
+			emu_pico_overlay(pd, w, h, g_screen_ppitch);
+		if (pico_inp_mode /*== 2 || overlay*/)
+			draw_pico_ptr();
+	}
 
 	osd_buf_cnt = 0;
 	if (notice)
-- 
2.39.5