1 /*
2  * PicoDrive
3  * (C) notaz, 2013
4  *
5  * This work is licensed under the terms of MAME license.
6  * See COPYING file in the top-level directory.
7  */
8 
9 #include <stdio.h>
10 
11 #include "../libpicofe/input.h"
12 #include "../libpicofe/plat.h"
13 #include "../libpicofe/plat_sdl.h"
14 #include "../libpicofe/in_sdl.h"
15 #include "../libpicofe/gl.h"
16 #include "emu.h"
17 #include "menu_pico.h"
18 #include "input_pico.h"
19 #include "plat_sdl.h"
20 #include "version.h"
21 
22 #include <pico/pico.h>
23 
24 static void *shadow_fb;
25 
26 static struct in_pdata in_sdl_platform_data = {
27 	.defbinds = in_sdl_defbinds,
28 	.key_map = in_sdl_key_map,
29 	.joy_map = in_sdl_joy_map,
30 };
31 
32 /* YUV stuff */
33 static int yuv_ry[32], yuv_gy[32], yuv_by[32];
34 static unsigned char yuv_u[32 * 2], yuv_v[32 * 2];
35 static unsigned char yuv_y[256];
36 static struct uyvy {  unsigned int y:8; unsigned int vyu:24; } yuv_uyvy[65536];
37 
bgr_to_uyvy_init(void)38 void bgr_to_uyvy_init(void)
39 {
40   int i, v;
41 
42   /* init yuv converter:
43     y0 = (int)((0.299f * r0) + (0.587f * g0) + (0.114f * b0));
44     y1 = (int)((0.299f * r1) + (0.587f * g1) + (0.114f * b1));
45     u = (int)(8 * 0.565f * (b0 - y0)) + 128;
46     v = (int)(8 * 0.713f * (r0 - y0)) + 128;
47   */
48   for (i = 0; i < 32; i++) {
49     yuv_ry[i] = (int)(0.299f * i * 65536.0f + 0.5f);
50     yuv_gy[i] = (int)(0.587f * i * 65536.0f + 0.5f);
51     yuv_by[i] = (int)(0.114f * i * 65536.0f + 0.5f);
52   }
53   for (i = -32; i < 32; i++) {
54     v = (int)(8 * 0.565f * i) + 128;
55     if (v < 0)
56       v = 0;
57     if (v > 255)
58       v = 255;
59     yuv_u[i + 32] = v;
60     v = (int)(8 * 0.713f * i) + 128;
61     if (v < 0)
62       v = 0;
63     if (v > 255)
64       v = 255;
65     yuv_v[i + 32] = v;
66   }
67   // valid Y range seems to be 16..235
68   for (i = 0; i < 256; i++) {
69     yuv_y[i] = 16 + 219 * i / 32;
70   }
71   // everything combined into one large array for speed
72   for (i = 0; i < 65536; i++) {
73      int r = (i >> 11) & 0x1f, g = (i >> 6) & 0x1f, b = (i >> 0) & 0x1f;
74      int y = (yuv_ry[r] + yuv_gy[g] + yuv_by[b]) >> 16;
75      yuv_uyvy[i].y = yuv_y[y];
76      yuv_uyvy[i].vyu = (yuv_v[r-y + 32] << 16) | (yuv_y[y] << 8) | yuv_u[b-y + 32];
77   }
78 }
79 
rgb565_to_uyvy(void * d,const void * s,int pixels,int x2)80 void rgb565_to_uyvy(void *d, const void *s, int pixels, int x2)
81 {
82   uint32_t *dst = d;
83   const uint16_t *src = s;
84 
85   if (x2)
86   for (; pixels > 0; src += 4, dst += 4, pixels -= 4)
87   {
88     struct uyvy *uyvy0 = yuv_uyvy + src[0], *uyvy1 = yuv_uyvy + src[1];
89     struct uyvy *uyvy2 = yuv_uyvy + src[2], *uyvy3 = yuv_uyvy + src[3];
90     dst[0] = (uyvy0->y << 24) | uyvy0->vyu;
91     dst[1] = (uyvy1->y << 24) | uyvy1->vyu;
92     dst[2] = (uyvy2->y << 24) | uyvy2->vyu;
93     dst[3] = (uyvy3->y << 24) | uyvy3->vyu;
94   } else
95   for (; pixels > 0; src += 4, dst += 2, pixels -= 4)
96   {
97     struct uyvy *uyvy0 = yuv_uyvy + src[0], *uyvy1 = yuv_uyvy + src[1];
98     struct uyvy *uyvy2 = yuv_uyvy + src[2], *uyvy3 = yuv_uyvy + src[3];
99     dst[0] = (uyvy1->y << 24) | uyvy0->vyu;
100     dst[1] = (uyvy3->y << 24) | uyvy2->vyu;
101   }
102 }
103 
104 static int clear_buf_cnt, clear_stat_cnt;
105 
plat_video_flip(void)106 void plat_video_flip(void)
107 {
108 	if (plat_sdl_overlay != NULL) {
109 		SDL_Rect dstrect =
110 			{ 0, 0, plat_sdl_screen->w, plat_sdl_screen->h };
111 
112 		SDL_LockYUVOverlay(plat_sdl_overlay);
113 		rgb565_to_uyvy(plat_sdl_overlay->pixels[0], shadow_fb,
114 				g_screen_ppitch * g_screen_height,
115 				plat_sdl_overlay->w > 2*plat_sdl_overlay->h);
116 		SDL_UnlockYUVOverlay(plat_sdl_overlay);
117 		SDL_DisplayYUVOverlay(plat_sdl_overlay, &dstrect);
118 	}
119 	else if (plat_sdl_gl_active) {
120 		gl_flip(shadow_fb, g_screen_ppitch, g_screen_height);
121 	}
122 	else {
123 		if (SDL_MUSTLOCK(plat_sdl_screen)) {
124 			SDL_UnlockSurface(plat_sdl_screen);
125 			SDL_Flip(plat_sdl_screen);
126 			SDL_LockSurface(plat_sdl_screen);
127 		} else
128 			SDL_Flip(plat_sdl_screen);
129 		g_screen_ptr = plat_sdl_screen->pixels;
130 		plat_video_set_buffer(g_screen_ptr);
131 		if (clear_buf_cnt) {
132 			memset(g_screen_ptr, 0, plat_sdl_screen->w*plat_sdl_screen->h * 2);
133 			clear_buf_cnt--;
134 		}
135 	}
136 	if (clear_stat_cnt) {
137 		unsigned short *d = (unsigned short *)g_screen_ptr + g_screen_ppitch * g_screen_height;
138 		int l = g_screen_ppitch * 8;
139 		memset((int *)(d - l), 0, l * 2);
140 		clear_stat_cnt--;
141 	}
142 }
143 
plat_video_wait_vsync(void)144 void plat_video_wait_vsync(void)
145 {
146 }
147 
plat_video_clear_status(void)148 void plat_video_clear_status(void)
149 {
150 	clear_stat_cnt = 3; // do it thrice in case of triple buffering
151 }
152 
plat_video_clear_buffers(void)153 void plat_video_clear_buffers(void)
154 {
155 	if (plat_sdl_overlay != NULL || plat_sdl_gl_active)
156 		memset(shadow_fb, 0, plat_sdl_screen->w*plat_sdl_screen->h * 2);
157 	else {
158 		memset(g_screen_ptr, 0, plat_sdl_screen->w*plat_sdl_screen->h * 2);
159 		clear_buf_cnt = 3; // do it thrice in case of triple buffering
160 	}
161 }
162 
plat_video_menu_enter(int is_rom_loaded)163 void plat_video_menu_enter(int is_rom_loaded)
164 {
165 	if (SDL_MUSTLOCK(plat_sdl_screen))
166 		SDL_UnlockSurface(plat_sdl_screen);
167 	plat_sdl_change_video_mode(g_menuscreen_w, g_menuscreen_h, 0);
168 	g_screen_ptr = shadow_fb;
169 	plat_video_set_buffer(g_screen_ptr);
170 }
171 
plat_video_menu_begin(void)172 void plat_video_menu_begin(void)
173 {
174 	if (plat_sdl_overlay != NULL || plat_sdl_gl_active) {
175 		g_menuscreen_ptr = shadow_fb;
176 	}
177 	else {
178 		if (SDL_MUSTLOCK(plat_sdl_screen))
179 			SDL_LockSurface(plat_sdl_screen);
180 		g_menuscreen_ptr = plat_sdl_screen->pixels;
181 	}
182 }
183 
plat_video_menu_end(void)184 void plat_video_menu_end(void)
185 {
186 	if (plat_sdl_overlay != NULL) {
187 		SDL_Rect dstrect =
188 			{ 0, 0, plat_sdl_screen->w, plat_sdl_screen->h };
189 
190 		SDL_LockYUVOverlay(plat_sdl_overlay);
191 		rgb565_to_uyvy(plat_sdl_overlay->pixels[0], shadow_fb,
192 				g_menuscreen_pp * g_menuscreen_h, 0);
193 		SDL_UnlockYUVOverlay(plat_sdl_overlay);
194 
195 		SDL_DisplayYUVOverlay(plat_sdl_overlay, &dstrect);
196 	}
197 	else if (plat_sdl_gl_active) {
198 		gl_flip(g_menuscreen_ptr, g_menuscreen_pp, g_menuscreen_h);
199 	}
200 	else {
201 		if (SDL_MUSTLOCK(plat_sdl_screen))
202 			SDL_UnlockSurface(plat_sdl_screen);
203 		SDL_Flip(plat_sdl_screen);
204 	}
205 	g_menuscreen_ptr = NULL;
206 }
207 
plat_video_menu_leave(void)208 void plat_video_menu_leave(void)
209 {
210 }
211 
plat_video_loop_prepare(void)212 void plat_video_loop_prepare(void)
213 {
214 	plat_sdl_change_video_mode(g_screen_width, g_screen_height, 0);
215 
216 	if (plat_sdl_overlay != NULL || plat_sdl_gl_active) {
217 		g_screen_ptr = shadow_fb;
218 	}
219 	else {
220 		if (SDL_MUSTLOCK(plat_sdl_screen))
221 			SDL_LockSurface(plat_sdl_screen);
222 		g_screen_ptr = plat_sdl_screen->pixels;
223 	}
224 	plat_video_set_buffer(g_screen_ptr);
225 }
226 
plat_early_init(void)227 void plat_early_init(void)
228 {
229 }
230 
plat_sdl_quit(void)231 static void plat_sdl_quit(void)
232 {
233 	// for now..
234 	exit(1);
235 }
236 
plat_init(void)237 void plat_init(void)
238 {
239 	int shadow_size;
240 	int ret;
241 
242 	ret = plat_sdl_init();
243 	if (ret != 0)
244 		exit(1);
245 	SDL_ShowCursor(0);
246 #if defined(__RG350__) || defined(__GCW0__)
247 	// opendingux on JZ47x0 may falsely report a HW overlay, fix to window
248 	plat_target.vout_method = 0;
249 #endif
250 
251 	plat_sdl_quit_cb = plat_sdl_quit;
252 
253 	SDL_WM_SetCaption("PicoDrive " VERSION, NULL);
254 
255 	g_menuscreen_w = plat_sdl_screen->w;
256 	g_menuscreen_h = plat_sdl_screen->h;
257 	g_menuscreen_pp = g_menuscreen_w;
258 	g_menuscreen_ptr = NULL;
259 
260 	shadow_size = g_menuscreen_w * g_menuscreen_h * 2;
261 	if (shadow_size < 320 * 480 * 2)
262 		shadow_size = 320 * 480 * 2;
263 
264 	shadow_fb = calloc(1, shadow_size);
265 	g_menubg_ptr = calloc(1, shadow_size);
266 	if (shadow_fb == NULL || g_menubg_ptr == NULL) {
267 		fprintf(stderr, "OOM\n");
268 		exit(1);
269 	}
270 
271 	g_screen_width = 320;
272 	g_screen_height = 240;
273 	g_screen_ppitch = 320;
274 	g_screen_ptr = shadow_fb;
275 
276 	in_sdl_platform_data.kmap_size = in_sdl_key_map_sz,
277 	in_sdl_platform_data.jmap_size = in_sdl_joy_map_sz,
278 	in_sdl_platform_data.key_names = *in_sdl_key_names,
279 	in_sdl_init(&in_sdl_platform_data, plat_sdl_event_handler);
280 	in_probe();
281 
282 	bgr_to_uyvy_init();
283 }
284 
plat_finish(void)285 void plat_finish(void)
286 {
287 	free(shadow_fb);
288 	shadow_fb = NULL;
289 	free(g_menubg_ptr);
290 	g_menubg_ptr = NULL;
291 	plat_sdl_finish();
292 }
293