platform ps2, handle audio similar to psp
[picodrive.git] / platform / common / plat_sdl.c
index 3948cc4..d1e833f 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * PicoDrive
  * (C) notaz, 2013
+ * (C) irixxxx, 2020-2024
  *
  * 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.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 "plat_sdl.h"
 #include "version.h"
 
-#include <pico/pico.h>
+#include <pico/pico_int.h>
 
 static void *shadow_fb;
+static int shadow_size;
+static struct area { int w, h; } area;
+
+static struct in_pdata in_sdl_platform_data;
+
+static int sound_rates[] = { 8000, 11025, 16000, 22050, 32000, 44100, 53000, -1 };
+struct plat_target plat_target = { .sound_rates = sound_rates };
+
+#if defined __MIYOO__
+const char *plat_device = "miyoo";
+#elif defined __GCW0__
+const char *plat_device = "gcw0";
+#elif defined __RETROFW__
+const char *plat_device = "retrofw";
+#elif defined __DINGUX__
+const char *plat_device = "dingux";
+#else
+const char *plat_device = "";
+#endif
+
+int plat_parse_arg(int argc, char *argv[], int *x)
+{
+#if defined __OPENDINGUX__
+       if (*plat_device == '\0' && strcasecmp(argv[*x], "-device") == 0) {
+               plat_device = argv[++(*x)];
+               return 0;
+       }
+#endif
+       return 1;
+}
 
-const struct in_default_bind in_sdl_defbinds[] __attribute__((weak)) = {
-       { 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_TAB,    IN_BINDTYPE_EMU, PEVB_RESET },
-       { 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 }
-};
-
-const struct menu_keymap in_sdl_key_map[] __attribute__((weak)) =
+void plat_early_init(void)
 {
-       { SDLK_UP,      PBTN_UP },
-       { SDLK_DOWN,    PBTN_DOWN },
-       { SDLK_LEFT,    PBTN_LEFT },
-       { SDLK_RIGHT,   PBTN_RIGHT },
-       { SDLK_RETURN,  PBTN_MOK },
-       { SDLK_ESCAPE,  PBTN_MBACK },
-       { SDLK_SEMICOLON,       PBTN_MA2 },
-       { SDLK_QUOTE,   PBTN_MA3 },
-       { SDLK_LEFTBRACKET,  PBTN_L },
-       { SDLK_RIGHTBRACKET, PBTN_R },
-};
-
-const struct menu_keymap in_sdl_joy_map[] __attribute__((weak)) =
+}
+
+int plat_target_init(void)
 {
-       { SDLK_UP,      PBTN_UP },
-       { SDLK_DOWN,    PBTN_DOWN },
-       { SDLK_LEFT,    PBTN_LEFT },
-       { SDLK_RIGHT,   PBTN_RIGHT },
-       /* joystick */
-       { SDLK_WORLD_0, PBTN_MOK },
-       { SDLK_WORLD_1, PBTN_MBACK },
-       { SDLK_WORLD_2, PBTN_MA2 },
-       { SDLK_WORLD_3, PBTN_MA3 },
-};
-
-extern const char * const in_sdl_key_names[] __attribute__((weak));
-
-static const struct in_pdata in_sdl_platform_data = {
-       .defbinds = in_sdl_defbinds,
-       .key_map = in_sdl_key_map,
-       .kmap_size = sizeof(in_sdl_key_map) / sizeof(in_sdl_key_map[0]),
-       .joy_map = in_sdl_joy_map,
-       .jmap_size = sizeof(in_sdl_joy_map) / sizeof(in_sdl_joy_map[0]),
-       .key_names = in_sdl_key_names,
-};
+#if defined __ODBETA__
+       if (*plat_device == '\0') {
+               /* ODbeta should always have a device tree, get the model info from there */
+               FILE *f = fopen("/proc/device-tree/compatible", "r");
+               if (f) {
+                       char buf[10];
+                       int c = fread(buf, 1, sizeof(buf), f);
+                       if (strncmp(buf, "gcw,", 4) == 0)
+                               plat_device = "gcw0";
+               }
+       }
+#endif
+       return 0;
+}
+
+void plat_target_finish(void)
+{
+}
 
 /* YUV stuff */
 static int yuv_ry[32], yuv_gy[32], yuv_by[32];
 static unsigned char yuv_u[32 * 2], yuv_v[32 * 2];
+static unsigned char yuv_y[256];
+static struct uyvy { uint32_t y:8; uint32_t vyu:24; } yuv_uyvy[65536];
 
 void bgr_to_uyvy_init(void)
 {
@@ -119,46 +114,121 @@ void bgr_to_uyvy_init(void)
       v = 255;
     yuv_v[i + 32] = v;
   }
+  // valid Y range seems to be 16..235
+  for (i = 0; i < 256; i++) {
+    yuv_y[i] = 16 + 219 * i / 32;
+  }
+  // everything combined into one large array for speed
+  for (i = 0; i < 65536; i++) {
+     int r = (i >> 11) & 0x1f, g = (i >> 6) & 0x1f, b = (i >> 0) & 0x1f;
+     int y = (yuv_ry[r] + yuv_gy[g] + yuv_by[b]) >> 16;
+     yuv_uyvy[i].y = yuv_y[y];
+#if CPU_IS_LE
+     yuv_uyvy[i].vyu = (yuv_v[r-y + 32] << 16) | (yuv_y[y] << 8) | yuv_u[b-y + 32];
+#else
+     yuv_uyvy[i].vyu = (yuv_v[b-y + 32] << 16) | (yuv_y[y] << 8) | yuv_u[r-y + 32];
+#endif
+  }
 }
 
-void rgb565_to_uyvy(void *d, const void *s, int pixels)
+void rgb565_to_uyvy(void *d, const void *s, int w, int h, int pitch, int dpitch, int x2)
 {
-  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;
+  uint32_t *dst = d;
+  const uint16_t *src = s;
+  int i;
+
+  if (x2) while (h--) {
+    for (i = w; i >= 4; src += 4, dst += 4, i -= 4)
+    {
+      struct uyvy *uyvy0 = yuv_uyvy + src[0], *uyvy1 = yuv_uyvy + src[1];
+      struct uyvy *uyvy2 = yuv_uyvy + src[2], *uyvy3 = yuv_uyvy + src[3];
+#if CPU_IS_LE
+      dst[0] = (uyvy0->y << 24) | uyvy0->vyu;
+      dst[1] = (uyvy1->y << 24) | uyvy1->vyu;
+      dst[2] = (uyvy2->y << 24) | uyvy2->vyu;
+      dst[3] = (uyvy3->y << 24) | uyvy3->vyu;
+#else
+      dst[0] = uyvy0->y | (uyvy0->vyu << 8);
+      dst[1] = uyvy1->y | (uyvy1->vyu << 8);
+      dst[2] = uyvy2->y | (uyvy2->vyu << 8);
+      dst[3] = uyvy3->y | (uyvy3->vyu << 8);
+#endif
+    }
+    src += pitch - (w-i);
+    dst += (dpitch - 2*(w-i))/2;
+  } else while (h--) {
+    for (i = w; i >= 4; src += 4, dst += 2, i -= 4)
+    {
+      struct uyvy *uyvy0 = yuv_uyvy + src[0], *uyvy1 = yuv_uyvy + src[1];
+      struct uyvy *uyvy2 = yuv_uyvy + src[2], *uyvy3 = yuv_uyvy + src[3];
+#if CPU_IS_LE
+      dst[0] = (uyvy1->y << 24) | uyvy0->vyu;
+      dst[1] = (uyvy3->y << 24) | uyvy2->vyu;
+#else
+      dst[0] = uyvy1->y | (uyvy0->vyu << 8);
+      dst[1] = uyvy3->y | (uyvy2->vyu << 8);
+#endif
+    }
+    src += pitch - (w-i);
+    dst += (dpitch - (w-i))/2;
   }
 }
 
+static int clear_buf_cnt, clear_stat_cnt;
+
+static void resize_buffers(void)
+{
+       // make sure the shadow buffers are big enough in case of resize
+       if (shadow_size < g_menuscreen_w * g_menuscreen_h * 2) {
+               shadow_size = g_menuscreen_w * g_menuscreen_h * 2;
+               shadow_fb = realloc(shadow_fb, shadow_size);
+               g_menubg_ptr = realloc(g_menubg_ptr, shadow_size);
+       }
+}
+
+void plat_video_set_size(int w, int h)
+{
+       if (area.w != w || area.h != h) {
+               area = (struct area) { w, h };
+               if (plat_sdl_change_video_mode(w, h, 0) < 0) {
+                       // failed, revert to original resolution
+                       area = (struct area) { g_screen_width,g_screen_height };
+                       plat_sdl_change_video_mode(g_screen_width, g_screen_height, 0);
+               }
+               if (!plat_sdl_overlay && !plat_sdl_gl_active) {
+                       g_screen_width = plat_sdl_screen->w;
+                       g_screen_height = plat_sdl_screen->h;
+                       g_screen_ppitch = plat_sdl_screen->pitch/2;
+                       g_screen_ptr = plat_sdl_screen->pixels;
+               } else {
+                       g_screen_width = w;
+                       g_screen_height = h;
+                       g_screen_ppitch = w;
+               }
+       }
+}
+
+void plat_video_set_shadow(int w, int h)
+{
+       g_screen_width = w;
+       g_screen_height = h;
+       g_screen_ppitch = w;
+       g_screen_ptr = shadow_fb;
+}
+
 void plat_video_flip(void)
 {
+       resize_buffers();
+
        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_ppitch * g_screen_height);
+               if (area.w <= plat_sdl_overlay->w && area.h <= plat_sdl_overlay->h)
+                       rgb565_to_uyvy(plat_sdl_overlay->pixels[0], shadow_fb,
+                                       area.w, area.h, g_screen_ppitch,
+                                       plat_sdl_overlay->pitches[0]/2,
+                                       plat_sdl_overlay->w >= 2*area.w);
                SDL_UnlockYUVOverlay(plat_sdl_overlay);
                SDL_DisplayYUVOverlay(plat_sdl_overlay, &dstrect);
        }
@@ -166,11 +236,33 @@ void plat_video_flip(void)
                gl_flip(shadow_fb, g_screen_ppitch, g_screen_height);
        }
        else {
-               if (SDL_MUSTLOCK(plat_sdl_screen))
+               if (SDL_MUSTLOCK(plat_sdl_screen)) {
                        SDL_UnlockSurface(plat_sdl_screen);
-               SDL_Flip(plat_sdl_screen);
+                       SDL_Flip(plat_sdl_screen);
+                       SDL_LockSurface(plat_sdl_screen);
+               } else
+                       SDL_Flip(plat_sdl_screen);
+               g_screen_ppitch = plat_sdl_screen->pitch/2;
                g_screen_ptr = plat_sdl_screen->pixels;
-               PicoDrawSetOutBuf(g_screen_ptr, g_screen_ppitch * 2);
+               plat_video_set_buffer(g_screen_ptr);
+               if (clear_buf_cnt) {
+                       memset(g_screen_ptr, 0, plat_sdl_screen->pitch*plat_sdl_screen->h);
+                       clear_buf_cnt--;
+               }
+       }
+
+       // for overlay/gl modes buffer ptr may change on resize
+       if ((plat_sdl_overlay || plat_sdl_gl_active) &&
+           (g_screen_ptr != shadow_fb || g_screen_ppitch != g_screen_width)) {
+               g_screen_ppitch = g_screen_width;
+               g_screen_ptr = shadow_fb;
+               plat_video_set_buffer(g_screen_ptr);
+       }
+       if (clear_stat_cnt) {
+               unsigned short *d = (unsigned short *)g_screen_ptr + g_screen_ppitch * g_screen_height;
+               int l = g_screen_ppitch * 8;
+               memset((int *)(d - l), 0, l * 2);
+               clear_stat_cnt--;
        }
 }
 
@@ -178,20 +270,39 @@ void plat_video_wait_vsync(void)
 {
 }
 
+void plat_video_clear_status(void)
+{
+       clear_stat_cnt = 3; // do it thrice in case of triple buffering
+}
+
+void plat_video_clear_buffers(void)
+{
+       if (plat_sdl_overlay || plat_sdl_gl_active)
+               memset(shadow_fb, 0, g_menuscreen_w * g_menuscreen_h * 2);
+       else {
+               memset(g_screen_ptr, 0, plat_sdl_screen->pitch*plat_sdl_screen->h);
+               clear_buf_cnt = 3; // do it thrice in case of triple buffering
+       }
+}
+
 void plat_video_menu_enter(int is_rom_loaded)
 {
-       plat_sdl_change_video_mode(g_menuscreen_w, g_menuscreen_h, 0);
-       g_screen_ptr = shadow_fb;
+       if (SDL_MUSTLOCK(plat_sdl_screen))
+               SDL_UnlockSurface(plat_sdl_screen);
 }
 
 void plat_video_menu_begin(void)
 {
-       if (plat_sdl_overlay != NULL || plat_sdl_gl_active) {
+       plat_sdl_change_video_mode(g_menuscreen_w, g_menuscreen_h, 1);
+       resize_buffers();
+       if (plat_sdl_overlay || plat_sdl_gl_active) {
+               g_menuscreen_pp = g_menuscreen_w;
                g_menuscreen_ptr = shadow_fb;
        }
        else {
                if (SDL_MUSTLOCK(plat_sdl_screen))
                        SDL_LockSurface(plat_sdl_screen);
+               g_menuscreen_pp = plat_sdl_screen->pitch / 2;
                g_menuscreen_ptr = plat_sdl_screen->pixels;
        }
 }
@@ -203,8 +314,11 @@ void plat_video_menu_end(void)
                        { 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_pp * g_menuscreen_h);
+               if (g_menuscreen_w <= plat_sdl_overlay->w && g_menuscreen_h <= plat_sdl_overlay->h)
+                       rgb565_to_uyvy(plat_sdl_overlay->pixels[0], shadow_fb,
+                               g_menuscreen_w, g_menuscreen_h, g_menuscreen_pp,
+                               plat_sdl_overlay->pitches[0]/2,
+                               plat_sdl_overlay->w >= 2 * g_menuscreen_w);
                SDL_UnlockYUVOverlay(plat_sdl_overlay);
 
                SDL_DisplayYUVOverlay(plat_sdl_overlay, &dstrect);
@@ -218,7 +332,6 @@ void plat_video_menu_end(void)
                SDL_Flip(plat_sdl_screen);
        }
        g_menuscreen_ptr = NULL;
-
 }
 
 void plat_video_menu_leave(void)
@@ -227,21 +340,56 @@ 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) {
+       // take over any new vout settings
+       plat_sdl_change_video_mode(0, 0, 0);
+       area.w = g_menuscreen_w, area.h = g_menuscreen_h;
+       resize_buffers();
+
+       // switch over to scaled output if available, but keep the aspect ratio
+       if (plat_sdl_overlay || plat_sdl_gl_active) {
+               if (g_menuscreen_w * 240 >= g_menuscreen_h * 320) {
+                       g_screen_width = (240 * g_menuscreen_w/g_menuscreen_h) & ~1;
+                       g_screen_height= 240;
+               } else {
+                       g_screen_width = 320;
+                       g_screen_height= (320 * g_menuscreen_h/g_menuscreen_w) & ~1;
+               }
+               g_screen_ppitch = g_screen_width;
                g_screen_ptr = shadow_fb;
        }
        else {
+               g_screen_width = plat_sdl_screen->w;
+               g_screen_height = plat_sdl_screen->h;
+               g_screen_ppitch = plat_sdl_screen->pitch/2;
                if (SDL_MUSTLOCK(plat_sdl_screen))
                        SDL_LockSurface(plat_sdl_screen);
                g_screen_ptr = plat_sdl_screen->pixels;
        }
-       PicoDrawSetOutBuf(g_screen_ptr, g_screen_ppitch * 2);
+
+       plat_video_set_size(g_screen_width, g_screen_height);
+       plat_video_set_buffer(g_screen_ptr);
 }
 
-void plat_early_init(void)
+static void plat_sdl_resize(int w, int h)
 {
+       // take over new settings
+       if (plat_sdl_screen->w != area.w || plat_sdl_screen->h != area.h) {
+#if defined(__OPENDINGUX__)
+        if (currentConfig.vscaling != EOPT_SCALE_HW &&
+                plat_sdl_screen->w == 320 &&
+                plat_sdl_screen->h == 480) {
+                   g_menuscreen_h = 240;
+                   g_menuscreen_w = 320;
+
+        } else
+#endif
+        {
+                   g_menuscreen_h = plat_sdl_screen->h;
+                   g_menuscreen_w = plat_sdl_screen->w;
+        }
+        resize_buffers();
+               rendstatus_old = -1;
+       }
 }
 
 static void plat_sdl_quit(void)
@@ -252,19 +400,22 @@ static void plat_sdl_quit(void)
 
 void plat_init(void)
 {
-       int shadow_size;
        int ret;
 
        ret = plat_sdl_init();
        if (ret != 0)
                exit(1);
+#if defined(__OPENDINGUX__)
+       // opendingux on JZ47x0 may falsely report a HW overlay, fix to window
+       plat_target.vout_method = 0;
+#endif
 
        plat_sdl_quit_cb = plat_sdl_quit;
+       plat_sdl_resize_cb = plat_sdl_resize;
 
+       SDL_ShowCursor(0);
        SDL_WM_SetCaption("PicoDrive " VERSION, NULL);
 
-       g_menuscreen_w = plat_sdl_screen->w;
-       g_menuscreen_h = plat_sdl_screen->h;
        g_menuscreen_pp = g_menuscreen_w;
        g_menuscreen_ptr = NULL;
 
@@ -272,8 +423,8 @@ void plat_init(void)
        if (shadow_size < 320 * 480 * 2)
                shadow_size = 320 * 480 * 2;
 
-       shadow_fb = malloc(shadow_size);
-       g_menubg_ptr = malloc(shadow_size);
+       shadow_fb = calloc(1, shadow_size);
+       g_menubg_ptr = calloc(1, shadow_size);
        if (shadow_fb == NULL || g_menubg_ptr == NULL) {
                fprintf(stderr, "OOM\n");
                exit(1);
@@ -284,10 +435,18 @@ void plat_init(void)
        g_screen_ppitch = 320;
        g_screen_ptr = shadow_fb;
 
+       plat_target_setup_input();
+       in_sdl_platform_data.defbinds = in_sdl_defbinds,
+       in_sdl_platform_data.kmap_size = in_sdl_key_map_sz,
+       in_sdl_platform_data.key_map = in_sdl_key_map,
+       in_sdl_platform_data.jmap_size = in_sdl_joy_map_sz,
+       in_sdl_platform_data.joy_map = in_sdl_joy_map,
+       in_sdl_platform_data.key_names = in_sdl_key_names,
        in_sdl_init(&in_sdl_platform_data, plat_sdl_event_handler);
        in_probe();
 
        bgr_to_uyvy_init();
+       linux_menu_init();
 }
 
 void plat_finish(void)