| 1 | /*\r |
| 2 | * PicoDrive\r |
| 3 | * (C) notaz, 2006-2010\r |
| 4 | * (C) irixxxx, 2019-2024\r |
| 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 |
| 9 | \r |
| 10 | #include <stdio.h>\r |
| 11 | #include <unistd.h>\r |
| 12 | #ifndef __MINGW32__\r |
| 13 | #include <sys/mman.h> // MAP_JIT\r |
| 14 | #endif\r |
| 15 | \r |
| 16 | #include "../libpicofe/menu.h"\r |
| 17 | #include "../libpicofe/plat.h"\r |
| 18 | #include "../common/emu.h"\r |
| 19 | #include "../common/arm_utils.h"\r |
| 20 | #include "../common/upscale.h"\r |
| 21 | #include "../common/keyboard.h"\r |
| 22 | #include "../common/version.h"\r |
| 23 | \r |
| 24 | #include <pico/pico_int.h>\r |
| 25 | \r |
| 26 | \r |
| 27 | const char *renderer_names[] = { "16bit accurate", " 8bit accurate", " 8bit fast", NULL };\r |
| 28 | const char *renderer_names32x[] = { "accurate", "faster", "fastest", NULL };\r |
| 29 | enum renderer_types { RT_16BIT, RT_8BIT_ACC, RT_8BIT_FAST, RT_COUNT };\r |
| 30 | \r |
| 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 |
| 34 | static u16 *ghost_buf; // backbuffer to simulate LCD ghosting\r |
| 35 | \r |
| 36 | void pemu_prep_defconfig(void)\r |
| 37 | {\r |
| 38 | }\r |
| 39 | \r |
| 40 | void pemu_validate_config(void)\r |
| 41 | {\r |
| 42 | #if !defined(DRC_SH2)\r |
| 43 | PicoIn.opt &= ~POPT_EN_DRC;\r |
| 44 | #endif\r |
| 45 | }\r |
| 46 | \r |
| 47 | #define is_16bit_mode() \\r |
| 48 | (currentConfig.renderer == RT_16BIT || (PicoIn.AHW & PAHW_32X) || render_bg)\r |
| 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 |
| 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 |
| 78 | pitch = g_screen_ppitch;\r |
| 79 | led_offs = 4;\r |
| 80 | scr_offs = pitch * 2 + 4;\r |
| 81 | \r |
| 82 | #define p(x) px[(x)*2 >> 2] = px[((x)*2 >> 2) + 1]\r |
| 83 | // 16-bit modes\r |
| 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 |
| 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 |
| 90 | }\r |
| 91 | \r |
| 92 | static void draw_pico_ptr(void)\r |
| 93 | {\r |
| 94 | int up = (PicoPicohw.pen_pos[0]|PicoPicohw.pen_pos[1]) & 0x8000;\r |
| 95 | int o = (up ? 0x0000 : 0xffff), _ = (up ? 0xffff : 0x0000);\r |
| 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 |
| 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 |
| 102 | \r |
| 103 | x = (x * out_w * ((1ULL<<32) / 320 + 1)) >> 32;\r |
| 104 | y = (y * h * ((1ULL<<32) / 224 + 1)) >> 32;\r |
| 105 | p += (screen_y+y)*pitch + (screen_x+x);\r |
| 106 | \r |
| 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 |
| 111 | }\r |
| 112 | \r |
| 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 |
| 124 | static inline u16 *screen_buffer(u16 *buf)\r |
| 125 | {\r |
| 126 | return buf + screen_y * g_screen_ppitch + screen_x -\r |
| 127 | (out_y * g_screen_ppitch + out_x);\r |
| 128 | }\r |
| 129 | \r |
| 130 | void screen_blit(u16 *pd, int pp, u8* ps, int ss, u16 *pal)\r |
| 131 | {\r |
| 132 | typedef void (*upscale_t)\r |
| 133 | (u16 *di,int ds, u8 *si,int ss, int w,int h, u16 *pal);\r |
| 134 | static const upscale_t upscale_256_224_hv[] = {\r |
| 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 |
| 138 | static const upscale_t upscale_256_____h[] = {\r |
| 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 |
| 142 | static const upscale_t upscale_____224_v[] = {\r |
| 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 |
| 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 |
| 150 | static const upscale_t upscale_160_____h[] = {\r |
| 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 |
| 154 | static const upscale_t upscale_____144_v[] = {\r |
| 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 |
| 159 | int y;\r |
| 160 | \r |
| 161 | // handle software upscaling\r |
| 162 | upscale = NULL;\r |
| 163 | if (currentConfig.scaling == EOPT_SCALE_SW && out_w <= 256) {\r |
| 164 | if (currentConfig.vscaling == EOPT_SCALE_SW && out_h <= 224)\r |
| 165 | // h+v scaling\r |
| 166 | upscale = out_w >= 240 ? upscale_256_224_hv: upscale_160_144_hv;\r |
| 167 | else\r |
| 168 | // h scaling\r |
| 169 | upscale = out_w >= 240 ? upscale_256_____h : upscale_160_____h;\r |
| 170 | } else if (currentConfig.vscaling == EOPT_SCALE_SW && out_h <= 224)\r |
| 171 | // v scaling\r |
| 172 | upscale = out_w >= 240 ? upscale_____224_v : upscale_____144_v;\r |
| 173 | if (!upscale) {\r |
| 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 |
| 181 | }\r |
| 182 | \r |
| 183 | void pemu_finalize_frame(const char *fps, const char *notice)\r |
| 184 | {\r |
| 185 | if (!is_16bit_mode()) {\r |
| 186 | // convert the 8 bit CLUT output to 16 bit RGB\r |
| 187 | u16 *pd = screen_buffer(g_screen_ptr) +\r |
| 188 | out_y * g_screen_ppitch + out_x;\r |
| 189 | u8 *ps = Pico.est.Draw2FB + out_y * 328 + out_x + 8;\r |
| 190 | \r |
| 191 | PicoDrawUpdateHighPal();\r |
| 192 | \r |
| 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 |
| 195 | screen_blit(pd, g_screen_ppitch, ps, 328, Pico.est.HighPal);\r |
| 196 | }\r |
| 197 | \r |
| 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 |
| 205 | \r |
| 206 | if (currentConfig.ghosting == 1)\r |
| 207 | for (y = 0; y < h; y++) {\r |
| 208 | v_blend((u32 *)pd, (u32 *)ps, w/2, p_075_round);\r |
| 209 | pd += g_screen_ppitch;\r |
| 210 | ps += w;\r |
| 211 | }\r |
| 212 | else\r |
| 213 | for (y = 0; y < h; y++) {\r |
| 214 | v_blend((u32 *)pd, (u32 *)ps, w/2, p_05_round);\r |
| 215 | pd += g_screen_ppitch;\r |
| 216 | ps += w;\r |
| 217 | }\r |
| 218 | }\r |
| 219 | \r |
| 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 |
| 231 | // draw virtual keyboard on display\r |
| 232 | if (kbd_mode && currentConfig.keyboard == 1 && vkbd)\r |
| 233 | vkbd_draw(vkbd);\r |
| 234 | \r |
| 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 |
| 239 | if ((PicoIn.AHW & PAHW_MCD) && (currentConfig.EmuOpt & EOPT_EN_CD_LEDS))\r |
| 240 | draw_cd_leds();\r |
| 241 | }\r |
| 242 | \r |
| 243 | void plat_video_set_buffer(void *buf)\r |
| 244 | {\r |
| 245 | if (is_16bit_mode())\r |
| 246 | PicoDrawSetOutBuf(screen_buffer(buf), g_screen_ppitch * 2);\r |
| 247 | }\r |
| 248 | \r |
| 249 | static void apply_renderer(void)\r |
| 250 | {\r |
| 251 | PicoIn.opt |= POPT_DIS_32C_BORDER;\r |
| 252 | PicoIn.opt &= ~(POPT_ALT_RENDERER|POPT_EN_SOFTSCALE);\r |
| 253 | if (is_16bit_mode()) {\r |
| 254 | if (currentConfig.scaling == EOPT_SCALE_SW)\r |
| 255 | PicoIn.opt |= POPT_EN_SOFTSCALE;\r |
| 256 | PicoIn.filter = currentConfig.filter;\r |
| 257 | }\r |
| 258 | \r |
| 259 | switch (get_renderer()) {\r |
| 260 | case RT_16BIT:\r |
| 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 |
| 267 | break;\r |
| 268 | case RT_8BIT_ACC:\r |
| 269 | // for simplification the 8 bit accurate renderer uses the same\r |
| 270 | // storage format as the fast renderer\r |
| 271 | PicoDrawSetOutFormat(PDF_8BIT, 0);\r |
| 272 | PicoDrawSetOutBuf(Pico.est.Draw2FB, 328);\r |
| 273 | break;\r |
| 274 | case RT_8BIT_FAST:\r |
| 275 | PicoIn.opt |= POPT_ALT_RENDERER;\r |
| 276 | PicoDrawSetOutFormat(PDF_NONE, 0);\r |
| 277 | break;\r |
| 278 | }\r |
| 279 | \r |
| 280 | if (PicoIn.AHW & PAHW_32X)\r |
| 281 | PicoDrawSetOutBuf(screen_buffer(g_screen_ptr), g_screen_ppitch * 2);\r |
| 282 | Pico.m.dirtyPal = 1;\r |
| 283 | }\r |
| 284 | \r |
| 285 | void plat_video_toggle_renderer(int change, int is_menu)\r |
| 286 | {\r |
| 287 | change_renderer(change);\r |
| 288 | plat_video_clear_buffers();\r |
| 289 | \r |
| 290 | if (!is_menu) {\r |
| 291 | apply_renderer();\r |
| 292 | \r |
| 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 |
| 298 | }\r |
| 299 | \r |
| 300 | void plat_status_msg_clear(void)\r |
| 301 | {\r |
| 302 | plat_video_clear_status();\r |
| 303 | }\r |
| 304 | \r |
| 305 | void plat_status_msg_busy_next(const char *msg)\r |
| 306 | {\r |
| 307 | plat_status_msg_clear();\r |
| 308 | pemu_finalize_frame("", msg);\r |
| 309 | plat_video_flip();\r |
| 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 |
| 316 | plat_status_msg_busy_next(msg);\r |
| 317 | }\r |
| 318 | \r |
| 319 | void plat_status_msg_busy_done(void)\r |
| 320 | {\r |
| 321 | }\r |
| 322 | \r |
| 323 | void plat_update_volume(int has_changed, int is_up)\r |
| 324 | {\r |
| 325 | }\r |
| 326 | \r |
| 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 |
| 336 | void pemu_forced_frame(int no_scale, int do_emu)\r |
| 337 | {\r |
| 338 | int hs = currentConfig.scaling, vs = currentConfig.vscaling;\r |
| 339 | \r |
| 340 | // create centered and sw scaled (if scaling enabled) 16 bit output\r |
| 341 | PicoIn.opt &= ~POPT_DIS_32C_BORDER;\r |
| 342 | Pico.m.dirtyPal = 1;\r |
| 343 | if (currentConfig.scaling) currentConfig.scaling = EOPT_SCALE_SW;\r |
| 344 | if (currentConfig.vscaling) currentConfig.vscaling = EOPT_SCALE_SW;\r |
| 345 | \r |
| 346 | // render a frame in 16 bit mode\r |
| 347 | render_bg = 1;\r |
| 348 | emu_cmn_forced_frame(no_scale, do_emu, screen_buffer(g_screen_ptr));\r |
| 349 | render_bg = 0;\r |
| 350 | \r |
| 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 |
| 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 |
| 357 | currentConfig.scaling = hs, currentConfig.vscaling = vs;\r |
| 358 | }\r |
| 359 | \r |
| 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 |
| 364 | {\r |
| 365 | // at start of new frame?\r |
| 366 | if (line <= out_y) {\r |
| 367 | // set y frame offset (see emu_video_mode_change)\r |
| 368 | Pico.est.DrawLineDest = screen_buffer(g_screen_ptr) +\r |
| 369 | (out_y * g_screen_ppitch /*+ out_x*/);\r |
| 370 | vscale_state = 0;\r |
| 371 | return out_y - line;\r |
| 372 | } else if (line > out_y + out_h)\r |
| 373 | return 1;\r |
| 374 | \r |
| 375 | return 0;\r |
| 376 | }\r |
| 377 | \r |
| 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 |
| 384 | {\r |
| 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 |
| 389 | \r |
| 390 | if (out_h == 144)\r |
| 391 | switch (currentConfig.filter) {\r |
| 392 | case 0: v_upscale_nn_3_5(dest32, pp/2, out_w/2, vscale_state);\r |
| 393 | break;\r |
| 394 | default: v_upscale_snn_3_5(dest32, pp/2, out_w/2, vscale_state);\r |
| 395 | break;\r |
| 396 | }\r |
| 397 | else\r |
| 398 | switch (currentConfig.filter) {\r |
| 399 | case 3: v_upscale_bl4_16_17(dest32, pp/2, out_w/2, vscale_state);\r |
| 400 | break;\r |
| 401 | case 2: v_upscale_bl2_16_17(dest32, pp/2, out_w/2, vscale_state);\r |
| 402 | break;\r |
| 403 | case 1: v_upscale_snn_16_17(dest32, pp/2, out_w/2, vscale_state);\r |
| 404 | break;\r |
| 405 | default: v_upscale_nn_16_17(dest32, pp/2, out_w/2, vscale_state);\r |
| 406 | break;\r |
| 407 | }\r |
| 408 | Pico.est.DrawLineDest = (u16 *)dest32 - out_x;\r |
| 409 | return 0;\r |
| 410 | }\r |
| 411 | \r |
| 412 | void emu_video_mode_change(int start_line, int line_count, int start_col, int col_count)\r |
| 413 | {\r |
| 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 |
| 418 | if (! render_bg)\r |
| 419 | plat_video_loop_prepare(); // recalculates g_screen_w/h\r |
| 420 | PicoDrawSetCallbacks(NULL, NULL);\r |
| 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 |
| 425 | switch (currentConfig.scaling) {\r |
| 426 | case EOPT_SCALE_HW:\r |
| 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 |
| 430 | break;\r |
| 431 | case EOPT_SCALE_SW:\r |
| 432 | screen_x = (screen_w - 320)/2;\r |
| 433 | break;\r |
| 434 | }\r |
| 435 | switch (currentConfig.vscaling) {\r |
| 436 | case EOPT_SCALE_HW:\r |
| 437 | screen_h = (out_h < 224 && out_h > 144 ? 224 : out_h);\r |
| 438 | screen_y = 0;\r |
| 439 | // NTSC always has 224 visible lines, anything smaller has bars\r |
| 440 | if (out_h < 224 && out_h > 144)\r |
| 441 | screen_y += (224 - out_h)/2;\r |
| 442 | // handle vertical centering for 16 bit mode\r |
| 443 | if (is_16bit_mode())\r |
| 444 | PicoDrawSetCallbacks(cb_vscaling_begin,cb_vscaling_nop);\r |
| 445 | break;\r |
| 446 | case EOPT_SCALE_SW:\r |
| 447 | screen_y = (screen_h - 240)/2 + (out_h < 240 && out_h > 144);\r |
| 448 | // NTSC always has 224 visible lines, anything smaller has bars\r |
| 449 | if (out_h < 224 && out_h > 144)\r |
| 450 | screen_y += (224 - out_h)/2;\r |
| 451 | // in 16 bit mode sw scaling is divided between core and platform\r |
| 452 | if (is_16bit_mode() && out_h < 240)\r |
| 453 | PicoDrawSetCallbacks(cb_vscaling_begin,cb_vscaling_end);\r |
| 454 | break;\r |
| 455 | }\r |
| 456 | \r |
| 457 | if (! render_bg)\r |
| 458 | plat_video_set_size(screen_w, screen_h);\r |
| 459 | \r |
| 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 |
| 469 | plat_video_set_buffer(g_screen_ptr);\r |
| 470 | \r |
| 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 |
| 479 | // clear whole screen in all buffers\r |
| 480 | if (!is_16bit_mode())\r |
| 481 | memset32(Pico.est.Draw2FB, 0xe0e0e0e0, (320+8) * (8+240+8) / 4);\r |
| 482 | plat_video_clear_buffers();\r |
| 483 | }\r |
| 484 | \r |
| 485 | void pemu_loop_prep(void)\r |
| 486 | {\r |
| 487 | apply_renderer();\r |
| 488 | plat_video_clear_buffers();\r |
| 489 | plat_show_cursor(!(PicoIn.opt & POPT_EN_MOUSE));\r |
| 490 | }\r |
| 491 | \r |
| 492 | void pemu_loop_end(void)\r |
| 493 | {\r |
| 494 | /* do one more frame for menu bg */\r |
| 495 | plat_video_set_shadow(320, 240);\r |
| 496 | pemu_forced_frame(0, 1);\r |
| 497 | if (ghost_buf) {\r |
| 498 | free(ghost_buf);\r |
| 499 | ghost_buf = NULL;\r |
| 500 | }\r |
| 501 | plat_show_cursor(1);\r |
| 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 |
| 517 | void *plat_mem_get_for_drc(size_t size)\r |
| 518 | {\r |
| 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 |
| 523 | return NULL;\r |
| 524 | #endif\r |
| 525 | }\r |