#include #include #include #include #include #include #include #include #include #include #include #include "in_ps2.h" #include "../libpicofe/input.h" #include "../libpicofe/plat.h" #include "../libpicofe/menu.h" #include "../common/input_pico.h" #include "../common/emu.h" #include #define OSD_FPS_X (gsGlobal->Width - 80) /* turn black GS Screen */ #define GS_BLACK GS_SETREG_RGBA(0x00, 0x00, 0x00, 0x80) /* Generic tint color */ #define GS_TEXT GS_SETREG_RGBA(0x80, 0x80, 0x80, 0x80) static int osd_buf_cnt, osd_cdleds; static int out_x, out_y; static int out_w, out_h; static float hscale, vscale; static struct in_default_bind in_ps2_defbinds[] = { { PAD_UP, IN_BINDTYPE_PLAYER12, GBTN_UP }, { PAD_DOWN, IN_BINDTYPE_PLAYER12, GBTN_DOWN }, { PAD_LEFT, IN_BINDTYPE_PLAYER12, GBTN_LEFT }, { PAD_RIGHT, IN_BINDTYPE_PLAYER12, GBTN_RIGHT }, { PAD_SQUARE, IN_BINDTYPE_PLAYER12, GBTN_A }, { PAD_CROSS, IN_BINDTYPE_PLAYER12, GBTN_B }, { PAD_CIRCLE, IN_BINDTYPE_PLAYER12, GBTN_C }, { PAD_START, IN_BINDTYPE_PLAYER12, GBTN_START }, { PAD_TRIANGLE, IN_BINDTYPE_EMU, PEVB_SWITCH_RND }, { PAD_L1, IN_BINDTYPE_EMU, PEVB_STATE_SAVE }, { PAD_R1, IN_BINDTYPE_EMU, PEVB_STATE_LOAD }, { PAD_SELECT, IN_BINDTYPE_EMU, PEVB_MENU }, { 0, 0, 0 } }; const char *renderer_names[] = { "16bit accurate", " 8bit accurate", " 8bit fast", NULL }; const char *renderer_names32x[] = { "accurate", "faster", "fastest", NULL }; enum renderer_types { RT_16BIT, RT_8BIT_ACC, RT_8BIT_FAST, RT_COUNT }; static int is_bg_frame; static GSGLOBAL *gsGlobal; static GSTEXTURE *g_menuscreen; static GSPRIMUVPOINT *g_menuscreen_vertices; static GSTEXTURE *g_screens[2]; static int g_screen_index; static GSTEXTURE *g_screen; static GSPRIMUVPOINT *g_screen_vertices; static u16 *g_screen_palette; static GSTEXTURE *osd; static uint32_t osd_vertices_count; static GSPRIMUVPOINT *osd_vertices; static GSTEXTURE *cdleds; static GSPRIMUVPOINT *cdleds_vertices; static int vsync_sema_id; static int32_t vsync_callback_id; static uint8_t vsync; /* 0 (Disabled), 1 (Enabled), 2 (Dynamic) */ /* sound stuff */ #define SOUND_BLOCK_COUNT 8 #define SOUND_BUFFER_SIZE (2*54000/50*SOUND_BLOCK_COUNT) // max.rate/min.frames static short __attribute__((aligned(4))) sndBuffer[SOUND_BUFFER_SIZE]; static short *snd_playptr = NULL, *sndBuffer_endptr = NULL; static int samples_made = 0, samples_done = 0, samples_block = 0; static int sound_thread_exit = 0; static int32_t sound_sem = -1; static uint8_t stack[0x4000] __attribute__((aligned(16))); extern void *_gp; static int mp3_init(void) { return 0; } static void writeSound(int len) { int ret, l; PicoIn.sndOut += len / 2; l = PicoIn.sndOut - sndBuffer; if (l > sizeof(sndBuffer)/2) lprintf("ovfl %d %d\n", len, PicoIn.sndOut - sndBuffer); if (l > samples_block * 6) { sndBuffer_endptr = PicoIn.sndOut; PicoIn.sndOut = sndBuffer; } if (sndBuffer_endptr < PicoIn.sndOut) sndBuffer_endptr = PicoIn.sndOut; // signal the snd thread samples_made += len / 2; // lprintf("signal, %i/%i\n", samples_done, samples_made); ret = SignalSema(sound_sem); // if (ret < 0) lprintf("snd signal ret %08x\n", ret); } static int sound_thread(void *argp) { while (!sound_thread_exit) { int ret = 0; if (samples_made - samples_done < samples_block) { // wait for data (use at least 2 blocks) // lprintf("sthr: wait... (%i)\n", samples_made - samples_done); while (samples_made - samples_done < samples_block*2 && !sound_thread_exit) ret = WaitSema(sound_sem); if (ret < 0) lprintf("sthr: WaitSema: %i\n", ret); continue; } // lprintf("sthr: got data: %i\n", samples_made - samples_done); short *sndOut = PicoIn.sndOut, *sndEnd = sndBuffer_endptr; int buflen = samples_block * 2; if (sndOut >= snd_playptr) buflen = sndOut - snd_playptr; else buflen = sndEnd - snd_playptr; if (buflen > samples_block) buflen = samples_block; ret = audsrv_play_audio((char *)snd_playptr, buflen*2); // if (ret != buflen*2 && ret >= 0) lprintf("sthr: play ret: %i, buflen: %i\n", ret, buflen*2); if (ret < 0) lprintf("sthr: play: ret %08x; pos %i/%i\n", ret, samples_done, samples_made); samples_done += buflen; snd_playptr += buflen; if (snd_playptr >= sndBuffer_endptr) snd_playptr = sndBuffer; } lprintf("sthr: exit\n"); ExitDeleteThread(); return 0; } static void sound_init(void) { int thid; int ret; ee_sema_t sema; ee_thread_t thread; sema.max_count = 1; sema.init_count = 0; sema.option = (u32) "sndsem"; if ((sound_sem = CreateSema(&sema)) < 0) return; audsrv_init(); thread.func = &sound_thread; thread.stack = stack; thread.stack_size = sizeof(stack); thread.gp_reg = &_gp; thread.option = (u32) "sndthread"; thread.initial_priority = 40; thid = CreateThread(&thread); samples_block = 22050/50; // needs to be initialized before thread start if (thid >= 0) { ret = StartThread(thid, NULL); if (ret < 0) lprintf("sound_init: StartThread returned %08x\n", ret); ChangeThreadPriority(0, 64); } else { DeleteSema(sound_sem); lprintf("CreateThread failed: %i\n", thid); } } void pemu_sound_start(void) { static int PsndRate_old = 0, PicoOpt_old = 0, pal_old = 0; static int mp3_init_done; int ret, stereo; struct audsrv_fmt_t format; samples_made = samples_done = 0; if (!(currentConfig.EmuOpt & EOPT_EN_SOUND)) return; if (PicoIn.AHW & PAHW_MCD) { // mp3... if (!mp3_init_done) { ret = mp3_init(); mp3_init_done = 1; if (ret) emu_status_msg("mp3 init failed (%i)", ret); } } if (PicoIn.sndRate > 52000 && PicoIn.sndRate < 54000) PicoIn.sndRate = YM2612_NATIVE_RATE(); ret = POPT_EN_FM|POPT_EN_PSG|POPT_EN_STEREO; if (PicoIn.sndRate != PsndRate_old || (PicoIn.opt&ret) != (PicoOpt_old&ret) || Pico.m.pal != pal_old) { PsndRerate(Pico.m.frame_count ? 1 : 0); } stereo=(PicoIn.opt&8)>>3; samples_block = PicoIn.sndRate * (stereo ? 2 : 1) / (Pico.m.pal ? 50 : 60); lprintf("starting audio: %i, len: %i, stereo: %i, pal: %i, block samples: %i\n", PicoIn.sndRate, Pico.snd.len, stereo, Pico.m.pal, samples_block); format.bits = 16; format.freq = PicoIn.sndRate; format.channels = 2; ret = audsrv_set_format(&format); audsrv_set_volume(MAX_VOLUME); if (ret < 0) { lprintf("audsrv_set_format() failed: %i\n", ret); emu_status_msg("sound init failed (%i), snd disabled", ret); currentConfig.EmuOpt &= ~EOPT_EN_SOUND; } else { PicoIn.writeSound = writeSound; snd_playptr = PicoIn.sndOut = sndBuffer_endptr = sndBuffer; PsndRate_old = PicoIn.sndRate; PicoOpt_old = PicoIn.opt; pal_old = Pico.m.pal; } ret = audsrv_play_audio((char *)snd_playptr, 4); } void pemu_sound_stop(void) { samples_made = samples_done = 0; plat_sleep_ms(100); audsrv_stop_audio(); } static void sound_deinit(void) { sound_thread_exit = 1; SignalSema(sound_sem); DeleteSema(sound_sem); sound_sem = -1; } #define is_16bit_mode() \ (currentConfig.renderer == RT_16BIT || (PicoIn.AHW & PAHW_32X) || is_bg_frame) static int vsync_handler(void) { iSignalSema(vsync_sema_id); ExitHandler(); return 0; } /* Copy of gsKit_sync_flip, but without the 'flip' */ static void gsKit_sync(GSGLOBAL *gsGlobal) { if (!gsGlobal->FirstFrame) WaitSema(vsync_sema_id); while (PollSema(vsync_sema_id) >= 0); } /* Copy of gsKit_sync_flip, but without the 'sync' */ static void gsKit_flip(GSGLOBAL *gsGlobal) { if (!gsGlobal->FirstFrame) { if (gsGlobal->DoubleBuffering == GS_SETTING_ON) { GS_SET_DISPFB2(gsGlobal->ScreenBuffer[gsGlobal->ActiveBuffer & 1] / 8192, gsGlobal->Width / 64, gsGlobal->PSM, 0, 0); gsGlobal->ActiveBuffer ^= 1; } } gsKit_setactive(gsGlobal); } static void flipScreen() { gsKit_flip(gsGlobal); gsKit_TexManager_nextFrame(gsGlobal); } static void set_g_menuscreen_values(void) { size_t g_menuscreenSize = gsKit_texture_size_ee(gsGlobal->Width, gsGlobal->Height, GS_PSM_CT16); g_menuscreen = (GSTEXTURE *)calloc(1, sizeof(GSTEXTURE)); g_menuscreen->Mem = malloc(g_menuscreenSize); g_menuscreen_vertices = (GSPRIMUVPOINT *)calloc(2, sizeof(GSPRIMUVPOINT)); g_menuscreen->Width = gsGlobal->Width; g_menuscreen->Height = gsGlobal->Height; g_menuscreen->PSM = GS_PSM_CT16; g_menuscreen_w = g_menuscreen->Width; g_menuscreen_h = g_menuscreen->Height; g_menuscreen_pp = g_menuscreen->Width; g_menuscreen_ptr = g_menuscreen->Mem; g_menuscreen_vertices[0].xyz2 = vertex_to_XYZ2(gsGlobal, 0, 0, 2); g_menuscreen_vertices[0].uv = vertex_to_UV(g_menuscreen, 0, 0); g_menuscreen_vertices[0].rgbaq = color_to_RGBAQ(0x80, 0x80, 0x80, 0x80, 0); g_menuscreen_vertices[1].xyz2 = vertex_to_XYZ2(gsGlobal, g_menuscreen->Width, g_menuscreen->Height, 2); g_menuscreen_vertices[1].uv = vertex_to_UV(g_menuscreen, g_menuscreen->Width, g_menuscreen->Height); g_menuscreen_vertices[1].rgbaq = color_to_RGBAQ(0x80, 0x80, 0x80, 0x80, 0); } void set_g_screen_values() { size_t g_screenSize = gsKit_texture_size_ee(328, 256, GS_PSM_CT16); int i; g_screen_palette = malloc(gsKit_texture_size_ee(16, 16, GS_PSM_CT16)); g_screen_vertices = (GSPRIMUVPOINT *)calloc(2, sizeof(GSPRIMUVPOINT)); for (i = 0; i < 2; i++) { g_screens[i] = (GSTEXTURE *)calloc(1, sizeof(GSTEXTURE)); g_screens[i]->Mem = (uint32_t *)malloc(g_screenSize); g_screens[i]->Width = 328; g_screens[i]->Height = 256; g_screens[i]->PSM = GS_PSM_CT16; g_screens[i]->Filter = GS_FILTER_LINEAR; g_screens[i]->Clut = g_screen_palette; g_screens[i]->ClutPSM = GS_PSM_CT16; } g_screen = g_screens[g_screen_index]; g_screen_ptr = g_screen->Mem; g_screen_width = 328; g_screen_height = 256; g_screen_ppitch = 328; g_screen_vertices[0].xyz2 = vertex_to_XYZ2(gsGlobal, 0, 0, 0); g_screen_vertices[0].uv = vertex_to_UV(g_screen, 0, 0); g_screen_vertices[0].rgbaq = color_to_RGBAQ(0x80, 0x80, 0x80, 0x80, 0); g_screen_vertices[1].xyz2 = vertex_to_XYZ2(gsGlobal, gsGlobal->Width, gsGlobal->Height, 0); g_screen_vertices[1].uv = vertex_to_UV(g_screen, g_screen->Width, g_screen->Height); g_screen_vertices[1].rgbaq = color_to_RGBAQ(0x80, 0x80, 0x80, 0x80, 0); } void set_cdleds_values() { size_t cdledsSize = gsKit_texture_size_ee(14, 5, GS_PSM_CT16); cdleds = (GSTEXTURE *)calloc(1, sizeof(GSTEXTURE)); cdleds->Mem = (uint32_t *)malloc(cdledsSize); cdleds_vertices = (GSPRIMUVPOINT *)calloc(2, sizeof(GSPRIMUVPOINT)); cdleds->Width = 14; cdleds->Height = 5; cdleds->PSM = GS_PSM_CT16; cdleds_vertices[0].xyz2 = vertex_to_XYZ2(gsGlobal, 4, 1, 1); cdleds_vertices[0].uv = vertex_to_UV(cdleds, 0, 0); cdleds_vertices[0].rgbaq = color_to_RGBAQ(0x80, 0x80, 0x80, 0x80, 0); cdleds_vertices[1].xyz2 = vertex_to_XYZ2(gsGlobal, cdleds->Width, cdleds->Height, 1); cdleds_vertices[1].uv = vertex_to_UV(cdleds, cdleds->Width, cdleds->Height); cdleds_vertices[1].rgbaq = color_to_RGBAQ(0x80, 0x80, 0x80, 0x80, 0); } void set_osd_values() { size_t osdSize = gsKit_texture_size_ee(gsGlobal->Width, 8, GS_PSM_CT16); int num_osds = 4, i; osd = (GSTEXTURE *)calloc(1, sizeof(GSTEXTURE)); osd->Mem = (uint32_t *)malloc(osdSize); osd_vertices_count = 2*num_osds; osd_vertices = (GSPRIMUVPOINT *)calloc(osd_vertices_count, sizeof(GSPRIMUVPOINT)); osd->Width = gsGlobal->Width; osd->Height = 8; osd->PSM = GS_PSM_CT16; for (i = 0; i < 2*num_osds; i++) osd_vertices[i].rgbaq = color_to_RGBAQ(0x80, 0x80, 0x80, 0x80, 0); } static void video_init(void) { ee_sema_t sema; sema.init_count = 0; sema.max_count = 1; sema.option = 0; vsync_sema_id = CreateSema(&sema); gsGlobal = gsKit_init_global(); // gsGlobal->Mode = GS_MODE_NTSC; // gsGlobal->Height = 448; gsGlobal->PSM = GS_PSM_CT16; gsGlobal->PSMZ = GS_PSMZ_16S; gsGlobal->ZBuffering = GS_SETTING_OFF; gsGlobal->DoubleBuffering = GS_SETTING_ON; gsGlobal->PrimAlphaEnable = GS_SETTING_OFF; gsGlobal->Dithering = GS_SETTING_OFF; dmaKit_init(D_CTRL_RELE_OFF, D_CTRL_MFD_OFF, D_CTRL_STS_UNSPEC, D_CTRL_STD_OFF, D_CTRL_RCYC_8, 1 << DMA_CHANNEL_GIF); dmaKit_chan_init(DMA_CHANNEL_GIF); gsKit_set_clamp(gsGlobal, GS_CMODE_REPEAT); gsKit_vram_clear(gsGlobal); gsKit_init_screen(gsGlobal); gsKit_TexManager_init(gsGlobal); gsKit_mode_switch(gsGlobal, GS_ONESHOT); gsKit_clear(gsGlobal, GS_BLACK); vsync = 0; vsync_callback_id = gsKit_add_vsync_handler(vsync_handler); set_g_screen_values(); set_g_menuscreen_values(); set_osd_values(); set_cdleds_values(); g_menubg_ptr = (uint8_t *)malloc(2 * g_menuscreen_pp * g_menuscreen_h); g_menubg_src_w = g_screen->Width; g_menubg_src_h = g_screen->Height; g_menubg_src_pp = g_screen->Width; } static void video_deinit(void) { free(g_screens[0]->Mem); free(g_screens[0]); free(g_screens[1]->Mem); free(g_screens[1]); free(g_screen_vertices); free(g_screen_palette); free(g_menuscreen->Mem); free(g_menuscreen); free(g_menuscreen_vertices); free(g_menubg_ptr); free(osd->Mem); free(osd); free(osd_vertices); free(cdleds->Mem); free(cdleds); free(cdleds_vertices); gsKit_clear(gsGlobal, GS_BLACK); gsKit_vram_clear(gsGlobal); gsKit_deinit_global(gsGlobal); gsKit_remove_vsync_handler(vsync_callback_id); if (vsync_sema_id >= 0) DeleteSema(vsync_sema_id); } static void set_scaling_params(void) { int width, height, xoffs, yoffs; int u[2], v[2]; height = (int)(out_h * vscale + 0.5); width = (int)(out_w * hscale + 0.5); if (width & 1) width++; // make even if (height & 1) height++; if (width >= gsGlobal->Width) { u[0] = out_x + (width-gsGlobal->Width)/2; u[1] = out_x + out_w - (width-gsGlobal->Width)/2 - 1; width = gsGlobal->Width; xoffs = 0; } else { u[0] = out_x; u[1] = out_x + out_w; xoffs = gsGlobal->Width/2 - width/2; } if (height >= gsGlobal->Height) { v[0] = out_y + (height-gsGlobal->Height)/2; v[1] = out_y + out_h - (height-gsGlobal->Height)/2; height = gsGlobal->Height; yoffs = 0; } else { v[0] = out_y; v[1] = out_y + out_h; yoffs = gsGlobal->Height/2 - height/2; } if (xoffs < 0) xoffs = 0; if (yoffs < 0) yoffs = 0; g_screen_vertices[0].xyz2 = vertex_to_XYZ2(gsGlobal, xoffs, yoffs, 0); g_screen_vertices[1].xyz2 = vertex_to_XYZ2(gsGlobal, xoffs + width, yoffs + height, 0); if (!is_16bit_mode()) { // 8-bit modes have an 8 px overlap area on the left u[0] += 8; u[1] += 8; } g_screen_vertices[0].uv = vertex_to_UV(g_screen, u[0], v[0]); g_screen_vertices[1].uv = vertex_to_UV(g_screen, u[1], v[1]); // lprintf("set_scaling_params: wxh = %ix%i\n",gsGlobal->Width,gsGlobal->Height); // lprintf("offs: %i, %i wh: %i, %i\n", xoffs, yoffs, width, height); // lprintf("uv0, uv1: %i, %i; %i, %i\n", u[0], v[0], u[1], v[1]); } static void make_ps2_palette(void) { PicoDrawUpdateHighPal(); // Rotate CLUT. PS2 is special since entries in CLUT are not in sequence. unsigned short int *pal=(void *)g_screen_palette; int i; for (i = 0; i < 256; i+=8) { if ((i&0x18) == 0x08) memcpy(pal+i,Pico.est.HighPal+i+8,16); else if ((i&0x18) == 0x10) memcpy(pal+i,Pico.est.HighPal+i-8,16); else memcpy(pal+i,Pico.est.HighPal+i,16); } } static int get_renderer(void) { if (is_bg_frame) return RT_16BIT; if (PicoIn.AHW & PAHW_32X) return currentConfig.renderer32x; else return currentConfig.renderer; } static void change_renderer(int diff) { int *r; if (PicoIn.AHW & PAHW_32X) r = ¤tConfig.renderer32x; else r = ¤tConfig.renderer; *r += diff; if (*r >= RT_COUNT) *r = 0; else if (*r < 0) *r = RT_COUNT - 1; } static void apply_renderer(void) { PicoIn.opt &= ~(POPT_ALT_RENDERER|POPT_EN_SOFTSCALE); switch (get_renderer()) { case RT_16BIT: PicoDrawSetOutFormat(PDF_RGB555, 0); break; case RT_8BIT_ACC: PicoDrawSetOutFormat(PDF_8BIT, 0); break; case RT_8BIT_FAST: PicoIn.opt |= POPT_ALT_RENDERER; PicoDrawSetOutFormat(PDF_NONE, 0); break; } } static void blit_screen(void) { if (!is_16bit_mode() && Pico.m.dirtyPal) make_ps2_palette(); g_screen->PSM = is_16bit_mode() ? GS_PSM_CT16 : GS_PSM_T8; g_screen->Filter = (currentConfig.filter ? GS_FILTER_LINEAR : GS_FILTER_NEAREST); gsKit_TexManager_bind(gsGlobal, g_screen); gskit_prim_list_sprite_texture_uv_3d(gsGlobal, g_screen, 2, g_screen_vertices); } static void osd_text(int x, const char *text) { void *old_ptr = g_screen_ptr; int old_pitch = g_screen_ppitch; int len = strlen(text) * 8; u16 *osd_buf = osd->Mem; int *p, h; g_screen_ptr = osd_buf; g_screen_ppitch = gsGlobal->Width; for (h = 0; h < 8; h++) { p = (int *) (osd_buf + x + gsGlobal->Width*h); p = (int *) ((int)p & ~3); // align memset32_uncached(p, 0, len/2); } emu_text_out16(x, 0, text); g_screen_ptr = old_ptr; g_screen_ppitch = old_pitch; osd_vertices[osd_buf_cnt].xyz2 = vertex_to_XYZ2(gsGlobal, x, gsGlobal->Height-8, 1); osd_vertices[osd_buf_cnt].uv = vertex_to_UV(osd, x, 0); osd_vertices[osd_buf_cnt+1].xyz2 = vertex_to_XYZ2(gsGlobal, x+len, gsGlobal->Height, 1); osd_vertices[osd_buf_cnt+1].uv = vertex_to_UV(osd, x+len, 8); osd_buf_cnt += 2; } static void blit_osd(void) { gsKit_TexManager_bind(gsGlobal, osd); while (osd_buf_cnt > 0) { osd_buf_cnt -= 2; gskit_prim_list_sprite_texture_uv_3d(gsGlobal, osd, 2, osd_vertices+osd_buf_cnt); } } static void cd_leds(void) { unsigned int reg, col_g, col_r, *p; reg = Pico_mcd->s68k_regs[0]; p = (unsigned int *)cdleds->Mem; col_g = (reg & 2) ? 0x06000600 : 0; col_r = (reg & 1) ? 0x00180018 : 0; *p++ = col_g; *p++ = col_g; p+=2; *p++ = col_r; *p++ = col_r; p += gsGlobal->Width/2 - 12/2; *p++ = col_g; *p++ = col_g; p+=2; *p++ = col_r; *p++ = col_r; p += gsGlobal->Width/2 - 12/2; *p++ = col_g; *p++ = col_g; p+=2; *p++ = col_r; *p++ = col_r; osd_cdleds = 1; } static void blit_cdleds(void) { if (!osd_cdleds) return; gsKit_TexManager_bind(gsGlobal, cdleds); gskit_prim_list_sprite_texture_uv_3d(gsGlobal, cdleds, 2, cdleds_vertices); } static void draw_pico_ptr(void) { int x = pico_pen_x, y = pico_pen_y, offs; int pp = g_screen_ppitch; x = (x * out_w * ((1ULL<<32) / 320)) >> 32; y = (y * out_h * ((1ULL<<32) / 224)) >> 32; offs = g_screen_ppitch * (out_y+y) + (out_x+x); if (is_16bit_mode()) { unsigned short *p = (unsigned short *)g_screen_ptr + offs; p[ -1] = 0x0000; p[ 0] = 0x001f; p[ 1] = 0x0000; p[ pp-1] = 0x001f; p[ pp] = 0x001f; p[ pp+1] = 0x001f; p[2*pp-1] = 0x0000; p[2*pp] = 0x001f; p[2*pp+1] = 0x0000; } else { unsigned char *p = (unsigned char *)g_screen_ptr + offs + 8; p[ -1] = 0xe0; p[ 0] = 0xf0; p[ 1] = 0xe0; p[ pp-1] = 0xf0; p[ pp] = 0xf0; p[ pp+1] = 0xf0; p[2*pp-1] = 0xe0; p[2*pp] = 0xf0; p[2*pp+1] = 0xe0; } } static void vidResetMode(void) { set_scaling_params(); Pico.m.dirtyPal = 1; } /* finalize rendering a frame */ void pemu_finalize_frame(const char *fps, const char *notice) { int emu_opt = currentConfig.EmuOpt; if ((PicoIn.AHW & PAHW_PICO) && (currentConfig.EmuOpt & EOPT_PICO_PEN)) if (pico_inp_mode) draw_pico_ptr(); osd_buf_cnt = 0; if (notice) osd_text(4, notice); if (emu_opt & 2) osd_text(OSD_FPS_X, fps); osd_cdleds = 0; if ((emu_opt & 0x400) && (PicoIn.AHW & PAHW_MCD)) cd_leds(); FlushCache(WRITEBACK_DCACHE); } /* display a completed frame buffer and prepare a new render buffer */ void plat_video_flip(void) { gsKit_TexManager_invalidate(gsGlobal, osd); gsKit_TexManager_invalidate(gsGlobal, cdleds); gsKit_TexManager_invalidate(gsGlobal, g_screen); gsKit_finish(); flipScreen(); gsKit_clear(gsGlobal, GS_BLACK); blit_screen(); blit_osd(); blit_cdleds(); gsKit_queue_exec(gsGlobal); g_screen_index ^= 1; g_screen = g_screens[g_screen_index]; g_screen_ptr = g_screen->Mem; plat_video_set_buffer(g_screen_ptr); } /* wait for start of vertical blanking */ void plat_video_wait_vsync(void) { gsKit_sync(gsGlobal); } /* switch from emulation display to menu display */ void plat_video_menu_enter(int is_rom_loaded) { g_screen_ptr = NULL; } /* start rendering a menu screen */ void plat_video_menu_begin(void) { } /* display a completed menu screen */ void plat_video_menu_end(void) { gsKit_TexManager_bind(gsGlobal, g_menuscreen); gskit_prim_list_sprite_texture_uv_3d( gsGlobal, g_menuscreen, 2, g_menuscreen_vertices); gsKit_queue_exec(gsGlobal); gsKit_finish(); gsKit_TexManager_invalidate(gsGlobal, g_menuscreen); flipScreen(); } /* terminate menu display */ void plat_video_menu_leave(void) { g_screen_ptr = g_screen->Mem; plat_video_set_buffer(g_screen_ptr); } /* set default configuration values */ void pemu_prep_defconfig(void) { defaultConfig.s_PsndRate = 22050; defaultConfig.s_PicoCDBuffers = 64; defaultConfig.filter = EOPT_FILTER_BILINEAR; // bilinear filtering defaultConfig.scaling = EOPT_SCALE_43; defaultConfig.vscaling = EOPT_VSCALE_FULL; defaultConfig.renderer = RT_8BIT_ACC; defaultConfig.renderer32x = RT_8BIT_ACC; defaultConfig.EmuOpt |= EOPT_SHOW_RTC; } /* check configuration for inconsistencies */ void pemu_validate_config(void) { } void plat_init(void) { video_init(); init_joystick_driver(false); in_ps2_init(in_ps2_defbinds); in_probe(); init_audio_driver(); sound_init(); plat_get_data_dir(rom_fname_loaded, sizeof(rom_fname_loaded)); } void plat_finish(void) { sound_deinit(); deinit_audio_driver(); deinit_joystick_driver(false); video_deinit(); } /* display emulator status messages before holding emulation */ void plat_status_msg_busy_first(const char *msg) { plat_status_msg_busy_next(msg); } void plat_status_msg_busy_next(const char *msg) { plat_status_msg_clear(); pemu_finalize_frame("", msg); plat_video_flip(); emu_status_msg(""); reset_timing = 1; } /* clear status message area */ void plat_status_msg_clear(void) { // not needed since the screen buf is cleared through the gskit_clear } /* change the audio volume setting */ void plat_update_volume(int has_changed, int is_up) {} /* prepare for MD screen mode change */ void emu_video_mode_change(int start_line, int line_count, int start_col, int col_count) { int h43 = (col_count >= 192 ? 320 : col_count); // ugh, mind GG... int v43 = (line_count >= 192 ? Pico.m.pal ? 240 : 224 : line_count); out_y = start_line; out_x = start_col; out_h = line_count; out_w = col_count; if (col_count == 248) // mind aspect ratio when blanking 1st column col_count = 256; switch (currentConfig.vscaling) { case EOPT_VSCALE_FULL: line_count = v43; vscale = (float)gsGlobal->Height/line_count; break; case EOPT_VSCALE_NOBORDER: vscale = (float)gsGlobal->Height/line_count; break; default: vscale = 1; break; } hscale = vscale * (gsGlobal->Aspect == GS_ASPECT_16_9 ? (4./3)/(16./9) : 1); switch (currentConfig.scaling) { case EOPT_SCALE_43: hscale = (hscale*h43)/col_count; break; case EOPT_SCALE_STRETCH: hscale = (hscale*h43/2 + gsGlobal->Width/2)/col_count; break; case EOPT_SCALE_WIDE: hscale = (float)gsGlobal->Width/col_count; break; default: // hscale = vscale, computed before switch break; } vidResetMode(); } /* render one frame in RGB */ void pemu_forced_frame(int no_scale, int do_emu) { is_bg_frame = 1; Pico.m.dirtyPal = 1; if (!no_scale) no_scale = currentConfig.scaling == EOPT_SCALE_NONE; emu_cmn_forced_frame(no_scale, do_emu, g_screen_ptr); g_menubg_src_ptr = g_screen_ptr; is_bg_frame = 0; } /* change the platform output rendering */ void plat_video_toggle_renderer(int change, int is_menu_call) { change_renderer(change); if (is_menu_call) return; apply_renderer(); vidResetMode(); rendstatus_old = -1; if (PicoIn.AHW & PAHW_32X) emu_status_msg(renderer_names32x[get_renderer()]); else emu_status_msg(renderer_names[get_renderer()]); } /* set the buffer for emulator output rendering */ void plat_video_set_buffer(void *buf) { if (is_16bit_mode()) PicoDrawSetOutBuf(g_screen_ptr, g_screen_ppitch * 2); else PicoDrawSetOutBuf(g_screen_ptr, g_screen_ppitch); } /* prepare for emulator output rendering */ void plat_video_loop_prepare(void) { apply_renderer(); vidResetMode(); } /* prepare for entering the emulator loop */ void pemu_loop_prep(void) { } /* terminate the emulator loop */ void pemu_loop_end(void) { pemu_sound_stop(); pemu_forced_frame(0, 1); }