cff531af |
1 | /*\r |
2 | * PicoDrive\r |
3 | * (C) notaz, 2006-2010\r |
15cc45c0 |
4 | * (C) irixxxx, 2019-2024\r |
cff531af |
5 | *\r |
6 | * This work is licensed under the terms of MAME license.\r |
7 | * See COPYING file in the top-level directory.\r |
8 | */\r |
697746df |
9 | \r |
10 | #include <stdio.h>\r |
11 | #include <unistd.h>\r |
1909e86c |
12 | #ifndef __MINGW32__\r |
13 | #include <sys/mman.h> // MAP_JIT\r |
14 | #endif\r |
697746df |
15 | \r |
e743be20 |
16 | #include "../libpicofe/menu.h"\r |
17 | #include "../libpicofe/plat.h"\r |
697746df |
18 | #include "../common/emu.h"\r |
697746df |
19 | #include "../common/arm_utils.h"\r |
d5d17782 |
20 | #include "../common/upscale.h"\r |
bac44b18 |
21 | #include "../common/keyboard.h"\r |
90f0dedf |
22 | #include "../common/version.h"\r |
697746df |
23 | \r |
24 | #include <pico/pico_int.h>\r |
25 | \r |
26 | \r |
fcdefcf6 |
27 | const char *renderer_names[] = { "16bit accurate", " 8bit accurate", " 8bit fast", NULL };\r |
28 | const char *renderer_names32x[] = { "accurate", "faster", "fastest", NULL };\r |
5a681086 |
29 | enum renderer_types { RT_16BIT, RT_8BIT_ACC, RT_8BIT_FAST, RT_COUNT };\r |
697746df |
30 | \r |
d5d17782 |
31 | static int out_x, out_y, out_w, out_h; // renderer output in render buffer\r |
32 | static int screen_x, screen_y, screen_w, screen_h; // final render destination \r |
33 | static int render_bg; // force 16bit mode for bg render\r |
f55ce7bf |
34 | static u16 *ghost_buf; // backbuffer to simulate LCD ghosting\r |
697746df |
35 | \r |
36 | void pemu_prep_defconfig(void)\r |
37 | {\r |
697746df |
38 | }\r |
39 | \r |
40 | void pemu_validate_config(void)\r |
41 | {\r |
31efd454 |
42 | #if !defined(DRC_SH2)\r |
93f9619e |
43 | PicoIn.opt &= ~POPT_EN_DRC;\r |
05eb243d |
44 | #endif\r |
697746df |
45 | }\r |
46 | \r |
43f79c5b |
47 | #define is_16bit_mode() \\r |
d5d17782 |
48 | (currentConfig.renderer == RT_16BIT || (PicoIn.AHW & PAHW_32X) || render_bg)\r |
43f79c5b |
49 | \r |
50 | static int get_renderer(void)\r |
51 | {\r |
52 | if (PicoIn.AHW & PAHW_32X)\r |
53 | return currentConfig.renderer32x;\r |
54 | else\r |
55 | return currentConfig.renderer;\r |
56 | }\r |
57 | \r |
58 | static void change_renderer(int diff)\r |
59 | {\r |
60 | int *r;\r |
61 | if (PicoIn.AHW & PAHW_32X)\r |
62 | r = ¤tConfig.renderer32x;\r |
63 | else\r |
64 | r = ¤tConfig.renderer;\r |
65 | *r += diff;\r |
66 | \r |
67 | if (*r >= RT_COUNT)\r |
68 | *r = 0;\r |
69 | else if (*r < 0)\r |
70 | *r = RT_COUNT - 1;\r |
71 | }\r |
72 | \r |
697746df |
73 | static void draw_cd_leds(void)\r |
74 | {\r |
75 | int led_reg, pitch, scr_offs, led_offs;\r |
76 | led_reg = Pico_mcd->s68k_regs[0];\r |
77 | \r |
7a7265ee |
78 | pitch = g_screen_ppitch;\r |
697746df |
79 | led_offs = 4;\r |
80 | scr_offs = pitch * 2 + 4;\r |
81 | \r |
758abbeb |
82 | #define p(x) px[(x)*2 >> 2] = px[((x)*2 >> 2) + 1]\r |
83 | // 16-bit modes\r |
f821bb70 |
84 | uint32_t *px = (uint32_t *)((short *)g_screen_ptr + scr_offs);\r |
85 | uint32_t col_g = (led_reg & 2) ? 0x06000600 : 0;\r |
86 | uint32_t col_r = (led_reg & 1) ? 0xc000c000 : 0;\r |
758abbeb |
87 | p(pitch*0) = p(pitch*1) = p(pitch*2) = col_g;\r |
88 | p(pitch*0 + led_offs) = p(pitch*1 + led_offs) = p(pitch*2 + led_offs) = col_r;\r |
89 | #undef p\r |
697746df |
90 | }\r |
91 | \r |
dca20eff |
92 | static void draw_pico_ptr(void)\r |
93 | {\r |
c87e36d7 |
94 | int up = (PicoPicohw.pen_pos[0]|PicoPicohw.pen_pos[1]) & 0x8000;\r |
95 | int o = (up ? 0x0000 : 0xffff), _ = (up ? 0xffff : 0x0000);\r |
dca20eff |
96 | int pitch = g_screen_ppitch;\r |
97 | u16 *p = g_screen_ptr;\r |
98 | int x = pico_pen_x, y = pico_pen_y;\r |
15cc45c0 |
99 | // storyware pages are actually squished, 2:1\r |
100 | int h = (pico_inp_mode == 1 ? 160 : out_h);\r |
101 | if (h < 224) y++;\r |
dca20eff |
102 | \r |
c87e36d7 |
103 | x = (x * out_w * ((1ULL<<32) / 320 + 1)) >> 32;\r |
15cc45c0 |
104 | y = (y * h * ((1ULL<<32) / 224 + 1)) >> 32;\r |
dca20eff |
105 | p += (screen_y+y)*pitch + (screen_x+x);\r |
106 | \r |
15cc45c0 |
107 | p[-pitch-1] ^= o; p[-pitch] ^= _; p[-pitch+1] ^= _; p[-pitch+2] ^= o;\r |
108 | p[-1] ^= _; p[0] ^= o; p[1] ^= o; p[2] ^= _;\r |
109 | p[pitch-1] ^= _; p[pitch] ^= o; p[pitch+1] ^= o; p[pitch+2] ^= _;\r |
110 | p[2*pitch-1]^= o; p[2*pitch]^= _; p[2*pitch+1]^= _; p[2*pitch+2]^= o;\r |
dca20eff |
111 | }\r |
112 | \r |
d5d17782 |
113 | /* render/screen buffer handling:\r |
114 | * In 16 bit mode, render output is directly placed in the screen buffer.\r |
115 | * SW scaling is handled in renderer (x) and in vscaling callbacks here (y).\r |
116 | * In 8 bit modes, output goes to the internal Draw2FB buffer in alternate\r |
117 | * renderer format (8 pix overscan at left/top/bottom), left aligned (DIS_32C).\r |
118 | * It is converted to 16 bit and SW scaled in pemu_finalize_frame.\r |
119 | *\r |
120 | * HW scaling always aligns the image to the left/top, since selecting an area\r |
121 | * for display isn't always possible.\r |
122 | */\r |
123 | \r |
466fa079 |
124 | static inline u16 *screen_buffer(u16 *buf)\r |
d5d17782 |
125 | {\r |
ce79590a |
126 | return buf + screen_y * g_screen_ppitch + screen_x -\r |
127 | (out_y * g_screen_ppitch + out_x);\r |
d5d17782 |
128 | }\r |
129 | \r |
130 | void screen_blit(u16 *pd, int pp, u8* ps, int ss, u16 *pal)\r |
832faed3 |
131 | {\r |
d5d17782 |
132 | typedef void (*upscale_t)\r |
133 | (u16 *di,int ds, u8 *si,int ss, int w,int h, u16 *pal);\r |
466fa079 |
134 | static const upscale_t upscale_256_224_hv[] = {\r |
d5d17782 |
135 | upscale_rgb_nn_x_4_5_y_16_17, upscale_rgb_snn_x_4_5_y_16_17,\r |
136 | upscale_rgb_bl2_x_4_5_y_16_17, upscale_rgb_bl4_x_4_5_y_16_17,\r |
137 | };\r |
b01c1ec5 |
138 | static const upscale_t upscale_256_____h[] = {\r |
d5d17782 |
139 | upscale_rgb_nn_x_4_5, upscale_rgb_snn_x_4_5,\r |
140 | upscale_rgb_bl2_x_4_5, upscale_rgb_bl4_x_4_5,\r |
141 | };\r |
b01c1ec5 |
142 | static const upscale_t upscale_____224_v[] = {\r |
d5d17782 |
143 | upscale_rgb_nn_y_16_17, upscale_rgb_snn_y_16_17,\r |
144 | upscale_rgb_bl2_y_16_17, upscale_rgb_bl4_y_16_17,\r |
145 | };\r |
466fa079 |
146 | static const upscale_t upscale_160_144_hv[] = {\r |
147 | upscale_rgb_nn_x_1_2_y_3_5, upscale_rgb_nn_x_1_2_y_3_5,\r |
148 | upscale_rgb_bl2_x_1_2_y_3_5, upscale_rgb_bl4_x_1_2_y_3_5,\r |
149 | };\r |
b01c1ec5 |
150 | static const upscale_t upscale_160_____h[] = {\r |
466fa079 |
151 | upscale_rgb_nn_x_1_2, upscale_rgb_nn_x_1_2,\r |
152 | upscale_rgb_bl2_x_1_2, upscale_rgb_bl2_x_1_2,\r |
153 | };\r |
b01c1ec5 |
154 | static const upscale_t upscale_____144_v[] = {\r |
466fa079 |
155 | upscale_rgb_nn_y_3_5, upscale_rgb_nn_y_3_5,\r |
156 | upscale_rgb_bl2_y_3_5, upscale_rgb_bl4_y_3_5,\r |
157 | };\r |
158 | const upscale_t *upscale;\r |
d5d17782 |
159 | int y;\r |
160 | \r |
161 | // handle software upscaling\r |
162 | upscale = NULL;\r |
b01c1ec5 |
163 | if (currentConfig.scaling == EOPT_SCALE_SW && out_w <= 256) {\r |
466fa079 |
164 | if (currentConfig.vscaling == EOPT_SCALE_SW && out_h <= 224)\r |
165 | // h+v scaling\r |
96948bdf |
166 | upscale = out_w >= 240 ? upscale_256_224_hv: upscale_160_144_hv;\r |
466fa079 |
167 | else\r |
168 | // h scaling\r |
96948bdf |
169 | upscale = out_w >= 240 ? upscale_256_____h : upscale_160_____h;\r |
466fa079 |
170 | } else if (currentConfig.vscaling == EOPT_SCALE_SW && out_h <= 224)\r |
171 | // v scaling\r |
96948bdf |
172 | upscale = out_w >= 240 ? upscale_____224_v : upscale_____144_v;\r |
466fa079 |
173 | if (!upscale) {\r |
d5d17782 |
174 | // no scaling\r |
175 | for (y = 0; y < out_h; y++)\r |
176 | h_copy(pd, pp, ps, 328, out_w, f_pal);\r |
177 | return;\r |
178 | }\r |
179 | \r |
180 | upscale[currentConfig.filter & 0x3](pd, pp, ps, ss, out_w, out_h, pal);\r |
832faed3 |
181 | }\r |
182 | \r |
d08e7326 |
183 | void pemu_finalize_frame(const char *fps, const char *notice)\r |
697746df |
184 | {\r |
43f79c5b |
185 | if (!is_16bit_mode()) {\r |
186 | // convert the 8 bit CLUT output to 16 bit RGB\r |
d5d17782 |
187 | u16 *pd = screen_buffer(g_screen_ptr) +\r |
ce79590a |
188 | out_y * g_screen_ppitch + out_x;\r |
189 | u8 *ps = Pico.est.Draw2FB + out_y * 328 + out_x + 8;\r |
b1a047c9 |
190 | \r |
191 | PicoDrawUpdateHighPal();\r |
d5d17782 |
192 | \r |
96948bdf |
193 | if (out_w == 248 && currentConfig.scaling == EOPT_SCALE_SW)\r |
194 | pd += (320 - out_w*320/256) / 2; // SMS with 1st tile blanked, recenter\r |
d5d17782 |
195 | screen_blit(pd, g_screen_ppitch, ps, 328, Pico.est.HighPal);\r |
5a681086 |
196 | }\r |
197 | \r |
f55ce7bf |
198 | if (currentConfig.ghosting && out_h == 144) {\r |
199 | // GG LCD ghosting emulation\r |
200 | u16 *pd = screen_buffer(g_screen_ptr) +\r |
201 | out_y * g_screen_ppitch + out_x;\r |
202 | u16 *ps = ghost_buf;\r |
203 | int y, h = currentConfig.vscaling == EOPT_SCALE_SW ? 240:out_h;\r |
204 | int w = currentConfig.scaling == EOPT_SCALE_SW ? 320:out_w;\r |
15cc45c0 |
205 | \r |
44e4bf2b |
206 | if (currentConfig.ghosting == 1)\r |
207 | for (y = 0; y < h; y++) {\r |
f55ce7bf |
208 | v_blend((u32 *)pd, (u32 *)ps, w/2, p_075_round);\r |
44e4bf2b |
209 | pd += g_screen_ppitch;\r |
210 | ps += w;\r |
211 | }\r |
212 | else\r |
213 | for (y = 0; y < h; y++) {\r |
f55ce7bf |
214 | v_blend((u32 *)pd, (u32 *)ps, w/2, p_05_round);\r |
44e4bf2b |
215 | pd += g_screen_ppitch;\r |
216 | ps += w;\r |
217 | }\r |
f55ce7bf |
218 | }\r |
219 | \r |
15cc45c0 |
220 | if (PicoIn.AHW & PAHW_PICO) {\r |
221 | int h = currentConfig.vscaling == EOPT_SCALE_SW ? 240:out_h;\r |
222 | int w = currentConfig.scaling == EOPT_SCALE_SW ? 320:out_w;\r |
223 | u16 *pd = screen_buffer(g_screen_ptr) + out_y*g_screen_ppitch + out_x;\r |
224 | \r |
225 | if (pico_inp_mode)\r |
226 | emu_pico_overlay(pd, w, h, g_screen_ppitch);\r |
227 | if (pico_inp_mode /*== 2 || overlay*/)\r |
228 | draw_pico_ptr();\r |
229 | }\r |
230 | \r |
bac44b18 |
231 | // draw virtual keyboard on display\r |
4814d19e |
232 | if (kbd_mode && currentConfig.keyboard == 1 && vkbd)\r |
bac44b18 |
233 | vkbd_draw(vkbd);\r |
234 | \r |
758abbeb |
235 | if (notice)\r |
236 | emu_osd_text16(4, g_screen_height - 8, notice);\r |
237 | if (currentConfig.EmuOpt & EOPT_SHOW_FPS)\r |
238 | emu_osd_text16(g_screen_width - 60, g_screen_height - 8, fps);\r |
93f9619e |
239 | if ((PicoIn.AHW & PAHW_MCD) && (currentConfig.EmuOpt & EOPT_EN_CD_LEDS))\r |
697746df |
240 | draw_cd_leds();\r |
697746df |
241 | }\r |
242 | \r |
758abbeb |
243 | void plat_video_set_buffer(void *buf)\r |
244 | {\r |
43f79c5b |
245 | if (is_16bit_mode())\r |
d5d17782 |
246 | PicoDrawSetOutBuf(screen_buffer(buf), g_screen_ppitch * 2);\r |
758abbeb |
247 | }\r |
248 | \r |
5a681086 |
249 | static void apply_renderer(void)\r |
250 | {\r |
96948bdf |
251 | PicoIn.opt |= POPT_DIS_32C_BORDER;\r |
252 | PicoIn.opt &= ~(POPT_ALT_RENDERER|POPT_EN_SOFTSCALE);\r |
1bbe9abf |
253 | if (is_16bit_mode()) {\r |
c3fcdf3f |
254 | if (currentConfig.scaling == EOPT_SCALE_SW)\r |
1bbe9abf |
255 | PicoIn.opt |= POPT_EN_SOFTSCALE;\r |
c3fcdf3f |
256 | PicoIn.filter = currentConfig.filter;\r |
96948bdf |
257 | }\r |
d1ae0810 |
258 | \r |
43f79c5b |
259 | switch (get_renderer()) {\r |
5a681086 |
260 | case RT_16BIT:\r |
d5d17782 |
261 | // 32X uses line mode for vscaling with accurate renderer, since\r |
262 | // the MD VDP layer must be unscaled and merging the scaled 32X\r |
263 | // image data will fail.\r |
264 | PicoDrawSetOutFormat(PDF_RGB555,\r |
265 | (PicoIn.AHW & PAHW_32X) && currentConfig.vscaling);\r |
266 | PicoDrawSetOutBuf(screen_buffer(g_screen_ptr), g_screen_ppitch * 2);\r |
5a681086 |
267 | break;\r |
268 | case RT_8BIT_ACC:\r |
d5d17782 |
269 | // for simplification the 8 bit accurate renderer uses the same\r |
270 | // storage format as the fast renderer\r |
5a681086 |
271 | PicoDrawSetOutFormat(PDF_8BIT, 0);\r |
35247900 |
272 | PicoDrawSetOutBuf(Pico.est.Draw2FB, 328);\r |
5a681086 |
273 | break;\r |
274 | case RT_8BIT_FAST:\r |
93f9619e |
275 | PicoIn.opt |= POPT_ALT_RENDERER;\r |
5a681086 |
276 | PicoDrawSetOutFormat(PDF_NONE, 0);\r |
277 | break;\r |
278 | }\r |
279 | \r |
93f9619e |
280 | if (PicoIn.AHW & PAHW_32X)\r |
d5d17782 |
281 | PicoDrawSetOutBuf(screen_buffer(g_screen_ptr), g_screen_ppitch * 2);\r |
b1a047c9 |
282 | Pico.m.dirtyPal = 1;\r |
5a681086 |
283 | }\r |
284 | \r |
285 | void plat_video_toggle_renderer(int change, int is_menu)\r |
697746df |
286 | {\r |
43f79c5b |
287 | change_renderer(change);\r |
ebd9c86a |
288 | plat_video_clear_buffers();\r |
5a681086 |
289 | \r |
43f79c5b |
290 | if (!is_menu) {\r |
5a681086 |
291 | apply_renderer();\r |
292 | \r |
43f79c5b |
293 | if (PicoIn.AHW & PAHW_32X)\r |
294 | emu_status_msg(renderer_names32x[get_renderer()]);\r |
295 | else\r |
296 | emu_status_msg(renderer_names[get_renderer()]);\r |
297 | }\r |
697746df |
298 | }\r |
299 | \r |
697746df |
300 | void plat_status_msg_clear(void)\r |
301 | {\r |
69b7b264 |
302 | plat_video_clear_status();\r |
697746df |
303 | }\r |
304 | \r |
305 | void plat_status_msg_busy_next(const char *msg)\r |
306 | {\r |
307 | plat_status_msg_clear();\r |
d08e7326 |
308 | pemu_finalize_frame("", msg);\r |
309 | plat_video_flip();\r |
697746df |
310 | emu_status_msg("");\r |
311 | reset_timing = 1;\r |
312 | }\r |
313 | \r |
314 | void plat_status_msg_busy_first(const char *msg)\r |
315 | {\r |
697746df |
316 | plat_status_msg_busy_next(msg);\r |
317 | }\r |
318 | \r |
95b08943 |
319 | void plat_status_msg_busy_done(void)\r |
320 | {\r |
321 | }\r |
322 | \r |
697746df |
323 | void plat_update_volume(int has_changed, int is_up)\r |
324 | {\r |
325 | }\r |
326 | \r |
d5d17782 |
327 | void pemu_sound_start(void)\r |
328 | {\r |
329 | emu_sound_start();\r |
330 | }\r |
331 | \r |
332 | void plat_debug_cat(char *str)\r |
333 | {\r |
334 | }\r |
335 | \r |
45285368 |
336 | void pemu_forced_frame(int no_scale, int do_emu)\r |
697746df |
337 | {\r |
d5d17782 |
338 | int hs = currentConfig.scaling, vs = currentConfig.vscaling;\r |
832faed3 |
339 | \r |
d5d17782 |
340 | // create centered and sw scaled (if scaling enabled) 16 bit output\r |
627648e4 |
341 | PicoIn.opt &= ~POPT_DIS_32C_BORDER;\r |
697746df |
342 | Pico.m.dirtyPal = 1;\r |
d5d17782 |
343 | if (currentConfig.scaling) currentConfig.scaling = EOPT_SCALE_SW;\r |
344 | if (currentConfig.vscaling) currentConfig.vscaling = EOPT_SCALE_SW;\r |
a4edca53 |
345 | \r |
d5d17782 |
346 | // render a frame in 16 bit mode\r |
347 | render_bg = 1;\r |
ce79590a |
348 | emu_cmn_forced_frame(no_scale, do_emu, screen_buffer(g_screen_ptr));\r |
d5d17782 |
349 | render_bg = 0;\r |
697746df |
350 | \r |
23e47196 |
351 | g_menubg_src_ptr = realloc(g_menubg_src_ptr, g_screen_height * g_screen_ppitch * 2);\r |
352 | memcpy(g_menubg_src_ptr, g_screen_ptr, g_screen_height * g_screen_ppitch * 2);\r |
7f4bfa96 |
353 | g_menubg_src_w = g_screen_width;\r |
354 | g_menubg_src_h = g_screen_height;\r |
355 | g_menubg_src_pp = g_screen_ppitch;\r |
356 | \r |
d5d17782 |
357 | currentConfig.scaling = hs, currentConfig.vscaling = vs;\r |
697746df |
358 | }\r |
359 | \r |
d5d17782 |
360 | /* vertical sw scaling, 16 bit mode */\r |
361 | static int vscale_state;\r |
362 | \r |
363 | static int cb_vscaling_begin(unsigned int line)\r |
697746df |
364 | {\r |
d5d17782 |
365 | // at start of new frame?\r |
466fa079 |
366 | if (line <= out_y) {\r |
367 | // set y frame offset (see emu_video_mode_change)\r |
ce79590a |
368 | Pico.est.DrawLineDest = screen_buffer(g_screen_ptr) +\r |
466fa079 |
369 | (out_y * g_screen_ppitch /*+ out_x*/);\r |
d5d17782 |
370 | vscale_state = 0;\r |
466fa079 |
371 | return out_y - line;\r |
372 | } else if (line > out_y + out_h)\r |
373 | return 1;\r |
374 | \r |
d5d17782 |
375 | return 0;\r |
697746df |
376 | }\r |
377 | \r |
d5d17782 |
378 | static int cb_vscaling_nop(unsigned int line)\r |
379 | {\r |
380 | return 0;\r |
381 | }\r |
382 | \r |
383 | static int cb_vscaling_end(unsigned int line)\r |
697746df |
384 | {\r |
f55ce7bf |
385 | u16 *dest = (u16 *)Pico.est.DrawLineDest + out_x;\r |
386 | // helpers for 32 bit operation (2 pixels at once):\r |
387 | u32 *dest32 = (u32 *)dest;\r |
388 | int pp = g_screen_ppitch;\r |
466fa079 |
389 | \r |
390 | if (out_h == 144)\r |
391 | switch (currentConfig.filter) {\r |
f55ce7bf |
392 | case 0: v_upscale_nn_3_5(dest32, pp/2, out_w/2, vscale_state);\r |
466fa079 |
393 | break;\r |
f55ce7bf |
394 | default: v_upscale_snn_3_5(dest32, pp/2, out_w/2, vscale_state);\r |
466fa079 |
395 | break;\r |
396 | }\r |
397 | else\r |
398 | switch (currentConfig.filter) {\r |
f55ce7bf |
399 | case 3: v_upscale_bl4_16_17(dest32, pp/2, out_w/2, vscale_state);\r |
466fa079 |
400 | break;\r |
f55ce7bf |
401 | case 2: v_upscale_bl2_16_17(dest32, pp/2, out_w/2, vscale_state);\r |
466fa079 |
402 | break;\r |
f55ce7bf |
403 | case 1: v_upscale_snn_16_17(dest32, pp/2, out_w/2, vscale_state);\r |
466fa079 |
404 | break;\r |
f55ce7bf |
405 | default: v_upscale_nn_16_17(dest32, pp/2, out_w/2, vscale_state);\r |
466fa079 |
406 | break;\r |
407 | }\r |
f55ce7bf |
408 | Pico.est.DrawLineDest = (u16 *)dest32 - out_x;\r |
d5d17782 |
409 | return 0;\r |
697746df |
410 | }\r |
411 | \r |
d5d17782 |
412 | void emu_video_mode_change(int start_line, int line_count, int start_col, int col_count)\r |
697746df |
413 | {\r |
d5d17782 |
414 | // relative position in core fb and screen fb\r |
415 | out_y = start_line; out_x = start_col;\r |
416 | out_h = line_count; out_w = col_count;\r |
417 | \r |
db8af214 |
418 | if (! render_bg)\r |
419 | plat_video_loop_prepare(); // recalculates g_screen_w/h\r |
d5d17782 |
420 | PicoDrawSetCallbacks(NULL, NULL);\r |
ce79590a |
421 | // center output in screen\r |
422 | screen_w = g_screen_width, screen_x = (screen_w - out_w)/2;\r |
423 | screen_h = g_screen_height, screen_y = (screen_h - out_h)/2;\r |
424 | \r |
d5d17782 |
425 | switch (currentConfig.scaling) {\r |
426 | case EOPT_SCALE_HW:\r |
96948bdf |
427 | // mind aspect ratio for SMS with 1st column blanked\r |
428 | screen_w = (out_w == 248 ? 256 : out_w);\r |
429 | screen_x = (screen_w - out_w)/2;\r |
d5d17782 |
430 | break;\r |
ce79590a |
431 | case EOPT_SCALE_SW:\r |
432 | screen_x = (screen_w - 320)/2;\r |
d5d17782 |
433 | break;\r |
434 | }\r |
435 | switch (currentConfig.vscaling) {\r |
436 | case EOPT_SCALE_HW:\r |
466fa079 |
437 | screen_h = (out_h < 224 && out_h > 144 ? 224 : out_h);\r |
ce79590a |
438 | screen_y = 0;\r |
439 | // NTSC always has 224 visible lines, anything smaller has bars\r |
466fa079 |
440 | if (out_h < 224 && out_h > 144)\r |
ce79590a |
441 | screen_y += (224 - out_h)/2;\r |
d5d17782 |
442 | // handle vertical centering for 16 bit mode\r |
d5d17782 |
443 | if (is_16bit_mode())\r |
ce79590a |
444 | PicoDrawSetCallbacks(cb_vscaling_begin,cb_vscaling_nop);\r |
d5d17782 |
445 | break;\r |
446 | case EOPT_SCALE_SW:\r |
44e4bf2b |
447 | screen_y = (screen_h - 240)/2 + (out_h < 240 && out_h > 144);\r |
d5d17782 |
448 | // NTSC always has 224 visible lines, anything smaller has bars\r |
466fa079 |
449 | if (out_h < 224 && out_h > 144)\r |
ce79590a |
450 | screen_y += (224 - out_h)/2;\r |
d5d17782 |
451 | // in 16 bit mode sw scaling is divided between core and platform\r |
452 | if (is_16bit_mode() && out_h < 240)\r |
ce79590a |
453 | PicoDrawSetCallbacks(cb_vscaling_begin,cb_vscaling_end);\r |
d5d17782 |
454 | break;\r |
455 | }\r |
456 | \r |
db8af214 |
457 | if (! render_bg)\r |
458 | plat_video_set_size(screen_w, screen_h);\r |
d5d17782 |
459 | \r |
f8aaa200 |
460 | if (screen_w < g_screen_width)\r |
461 | screen_x = (g_screen_width - screen_w)/2;\r |
462 | if (screen_h < g_screen_height) {\r |
463 | screen_y = (g_screen_height - screen_h)/2;\r |
464 | // NTSC always has 224 visible lines, anything smaller has bars\r |
465 | if (out_h < 224 && out_h > 144)\r |
466 | screen_y += (224 - out_h)/2;\r |
467 | }\r |
468 | \r |
23e47196 |
469 | plat_video_set_buffer(g_screen_ptr);\r |
470 | \r |
f55ce7bf |
471 | // create a backing buffer for emulating the bad GG lcd display\r |
472 | if (currentConfig.ghosting && out_h == 144) {\r |
473 | int h = currentConfig.vscaling == EOPT_SCALE_SW ? 240:out_h;\r |
474 | int w = currentConfig.scaling == EOPT_SCALE_SW ? 320:out_w;\r |
475 | ghost_buf = realloc(ghost_buf, w * h * 2);\r |
476 | memset(ghost_buf, 0, w * h * 2);\r |
477 | }\r |
478 | \r |
697746df |
479 | // clear whole screen in all buffers\r |
43f79c5b |
480 | if (!is_16bit_mode())\r |
7980d477 |
481 | memset32(Pico.est.Draw2FB, 0xe0e0e0e0, (320+8) * (8+240+8) / 4);\r |
69b7b264 |
482 | plat_video_clear_buffers();\r |
697746df |
483 | }\r |
484 | \r |
485 | void pemu_loop_prep(void)\r |
486 | {\r |
5a681086 |
487 | apply_renderer();\r |
69b7b264 |
488 | plat_video_clear_buffers();\r |
44be7e5c |
489 | plat_show_cursor(!(PicoIn.opt & POPT_EN_MOUSE));\r |
697746df |
490 | }\r |
491 | \r |
492 | void pemu_loop_end(void)\r |
493 | {\r |
697746df |
494 | /* do one more frame for menu bg */\r |
5aa57006 |
495 | plat_video_set_shadow(320, 240);\r |
45285368 |
496 | pemu_forced_frame(0, 1);\r |
f55ce7bf |
497 | if (ghost_buf) {\r |
498 | free(ghost_buf);\r |
499 | ghost_buf = NULL;\r |
500 | }\r |
037f58e1 |
501 | plat_show_cursor(1);\r |
697746df |
502 | }\r |
503 | \r |
504 | void plat_wait_till_us(unsigned int us_to)\r |
505 | {\r |
506 | unsigned int now;\r |
507 | \r |
508 | now = plat_get_ticks_us();\r |
509 | \r |
510 | while ((signed int)(us_to - now) > 512)\r |
511 | {\r |
512 | usleep(1024);\r |
513 | now = plat_get_ticks_us();\r |
514 | }\r |
515 | }\r |
516 | \r |
df925153 |
517 | void *plat_mem_get_for_drc(size_t size)\r |
518 | {\r |
8b60ec30 |
519 | #ifdef MAP_JIT\r |
520 | // newer versions of OSX, IOS or TvOS need this\r |
521 | return plat_mmap(0, size, 1, 0);\r |
522 | #else\r |
df925153 |
523 | return NULL;\r |
8b60ec30 |
524 | #endif\r |
df925153 |
525 | }\r |