/*
* 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)
{
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);
}
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--;
}
}
{
}
+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;
}
}
{ 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);
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) {
+ // 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)
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;
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);
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)