2 * (c) Copyright 2006-2010 notaz, All rights reserved.
\r
3 * (c) Copyright 2019-2024 irixxxx
\r
5 * For performance reasons 3 renderers are exported for both MD and 32x modes:
\r
6 * - 16bpp line renderer
\r
7 * - 8bpp line renderer (slightly faster)
\r
8 * - 8bpp tile renderer
\r
10 * - 32x layer is overlayed on top of 16bpp one
\r
11 * - line internal one done on .Draw2FB, then mixed with 32x
\r
12 * - tile internal one done on .Draw2FB, then mixed with 32x
\r
19 #include "../libpicofe/gp2x/plat_gp2x.h"
\r
20 #include "../libpicofe/gp2x/soc.h"
\r
21 #include "../libpicofe/input.h"
\r
22 #include "../libpicofe/plat.h"
\r
23 #include "../libpicofe/gp2x/soc_pollux.h"
\r
24 #include "../common/menu_pico.h"
\r
25 #include "../common/arm_utils.h"
\r
26 #include "../common/emu.h"
\r
27 #include "../common/config_file.h"
\r
28 #include "../common/version.h"
\r
31 #include <pico/pico_int.h>
\r
32 #include <pico/patch.h>
\r
33 #include <pico/sound/mix.h>
\r
37 #define OSD_FPS_X 220
\r
39 #define OSD_FPS_X 260
\r
43 //extern int crashed_940;
\r
45 static int osd_fps_x, osd_y, doing_bg_frame;
\r
46 const char *renderer_names[] = { "16bit accurate", " 8bit accurate", " 8bit fast", NULL };
\r
47 const char *renderer_names32x[] = { "accurate", "faster", "fastest", NULL };
\r
48 enum renderer_types { RT_16BIT, RT_8BIT_ACC, RT_8BIT_FAST, RT_COUNT };
\r
50 static int is_1stblanked;
\r
51 static int firstline, linecount;
\r
52 static int firstcol, colcount;
\r
54 static int (*emu_scan_begin)(unsigned int num) = NULL;
\r
55 static int (*emu_scan_end)(unsigned int num) = NULL;
\r
58 void pemu_prep_defconfig(void)
\r
62 defaultConfig.CPUclock = default_cpu_clock;
\r
63 defaultConfig.renderer32x = RT_8BIT_ACC;
\r
64 defaultConfig.analog_deadzone = 50;
\r
67 if (soc == SOCID_MMSP2)
\r
68 defaultConfig.s_PicoOpt |= POPT_EXT_FM;
\r
69 else if (soc == SOCID_POLLUX) {
\r
70 defaultConfig.EmuOpt |= EOPT_WIZ_TEAR_FIX|EOPT_SHOW_RTC;
\r
71 defaultConfig.s_PicoOpt |= POPT_EN_MCD_GFX;
\r
75 void pemu_validate_config(void)
\r
77 if (gp2x_dev_id != GP2X_DEV_GP2X)
\r
78 PicoIn.opt &= ~POPT_EXT_FM;
\r
79 if (gp2x_dev_id != GP2X_DEV_WIZ)
\r
80 currentConfig.EmuOpt &= ~EOPT_WIZ_TEAR_FIX;
\r
82 if (currentConfig.gamma < 10 || currentConfig.gamma > 300)
\r
83 currentConfig.gamma = 100;
\r
85 if (currentConfig.CPUclock < 10 || currentConfig.CPUclock > 1024)
\r
86 currentConfig.CPUclock = default_cpu_clock;
\r
89 static int get_renderer(void)
\r
93 if (PicoIn.AHW & PAHW_32X)
\r
94 return currentConfig.renderer32x;
\r
96 return currentConfig.renderer;
\r
99 static void change_renderer(int diff)
\r
102 if (PicoIn.AHW & PAHW_32X)
\r
103 r = ¤tConfig.renderer32x;
\r
105 r = ¤tConfig.renderer;
\r
108 if (*r >= RT_COUNT)
\r
114 #define is_16bit_mode() \
\r
115 (currentConfig.renderer == RT_16BIT || (PicoIn.AHW & PAHW_32X) || doing_bg_frame)
\r
117 static void (*osd_text)(int x, int y, const char *text);
\r
119 static void osd_text8(int x, int y, const char *text)
\r
121 int len = strlen(text)*8;
\r
122 int *p, i, h, offs;
\r
124 len = (len+3) >> 2;
\r
125 for (h = 0; h < 8; h++) {
\r
126 offs = (x + g_screen_width * (y+h)) & ~3;
\r
127 p = (int *) ((char *)g_screen_ptr + offs);
\r
128 for (i = len; i; i--, p++)
\r
131 emu_text_out8(x, y, text);
\r
134 static void osd_text8_rot(int x, int y, const char *text)
\r
136 int len = strlen(text) * 8;
\r
137 char *p = (char *)g_screen_ptr + 240*(320-x) + y;
\r
140 memset(p, 0xe0, 8);
\r
144 emu_text_out8_rot(x, y, text);
\r
147 static void osd_text16_rot(int x, int y, const char *text)
\r
149 int len = strlen(text) * 8;
\r
150 short *p = (short *)g_screen_ptr + 240*(320-x) + y;
\r
157 emu_text_out16_rot(x, y, text);
\r
160 static void draw_cd_leds(void)
\r
162 int led_reg, pitch, scr_offs, led_offs;
\r
163 led_reg = Pico_mcd->s68k_regs[0];
\r
165 if (currentConfig.EmuOpt & EOPT_WIZ_TEAR_FIX) {
\r
167 led_offs = -pitch * 6;
\r
168 scr_offs = pitch * (320 - 4);
\r
172 scr_offs = pitch * 2 + 4;
\r
175 if (!is_16bit_mode()) {
\r
176 #define p(x) px[(x) >> 2]
\r
178 unsigned int *px = (unsigned int *)((char *)g_screen_ptr + scr_offs);
\r
179 unsigned int col_g = (led_reg & 2) ? 0xc0c0c0c0 : 0xe0e0e0e0;
\r
180 unsigned int col_r = (led_reg & 1) ? 0xd0d0d0d0 : 0xe0e0e0e0;
\r
181 p(pitch*0) = p(pitch*1) = p(pitch*2) = col_g;
\r
182 p(pitch*0 + led_offs) = p(pitch*1 + led_offs) = p(pitch*2 + led_offs) = col_r;
\r
185 #define p(x) px[(x)*2 >> 2] = px[((x)*2 >> 2) + 1]
\r
187 unsigned int *px = (unsigned int *)((short *)g_screen_ptr + scr_offs);
\r
188 unsigned int col_g = (led_reg & 2) ? 0x06000600 : 0;
\r
189 unsigned int col_r = (led_reg & 1) ? 0xc000c000 : 0;
\r
190 p(pitch*0) = p(pitch*1) = p(pitch*2) = col_g;
\r
191 p(pitch*0 + led_offs) = p(pitch*1 + led_offs) = p(pitch*2 + led_offs) = col_r;
\r
196 static void draw_pico_ptr(void)
\r
198 int up = (PicoPicohw.pen_pos[0]|PicoPicohw.pen_pos[1]) & 0x8000;
\r
199 int x, y, pitch = 320, offs;
\r
200 // storyware pages are actually squished, 2:1
\r
201 int h = (pico_inp_mode == 1 ? 160 : linecount);
\r
204 x = ((pico_pen_x * colcount * ((1ULL<<32)/320 + 1)) >> 32) + firstcol;
\r
205 y = ((pico_pen_y * h * ((1ULL<<32)/224 + 1)) >> 32) + firstline;
\r
207 if (currentConfig.EmuOpt & EOPT_WIZ_TEAR_FIX) {
\r
209 offs = (319 - x) * pitch + y;
\r
211 offs = x + y * pitch;
\r
213 if (is_16bit_mode()) {
\r
214 unsigned short *p = (unsigned short *)g_screen_ptr + offs;
\r
215 int o = (up ? 0x0000 : 0xffff), _ = (up ? 0xffff : 0x0000);
\r
217 p[-pitch-1] ^= o; p[-pitch] ^= _; p[-pitch+1] ^= _; p[-pitch+2] ^= o;
\r
218 p[-1] ^= _; p[0] ^= o; p[1] ^= o; p[2] ^= _;
\r
219 p[pitch-1] ^= _; p[pitch] ^= o; p[pitch+1] ^= o; p[pitch+2] ^= _;
\r
220 p[2*pitch-1]^= o; p[2*pitch]^= _; p[2*pitch+1]^= _; p[2*pitch+2]^= o;
\r
222 unsigned char *p = (unsigned char *)g_screen_ptr + offs;
\r
223 int o = (up ? 0xe0 : 0xf0), _ = (up ? 0xf0 : 0xe0);
\r
225 p[-pitch-1] = o; p[-pitch] = _; p[-pitch+1] = _; p[-pitch+2] = o;
\r
226 p[-1] = _; p[0] = o; p[1] = o; p[2] = _;
\r
227 p[pitch-1] = _; p[pitch] = o; p[pitch+1] = o; p[pitch+2] = _;
\r
228 p[2*pitch-1]= o; p[2*pitch]= _; p[2*pitch+1]= _; p[2*pitch+2]= o;
\r
232 static void clear_1st_column(int firstcol, int firstline, int linecount)
\r
234 int size = is_16bit_mode() ? 2 : 1;
\r
235 int black = is_16bit_mode() ? 0 : 0xe0;
\r
238 // SMS 1st column blanked, replace with black
\r
239 if ((currentConfig.EmuOpt & EOPT_WIZ_TEAR_FIX) && !doing_bg_frame) {
\r
240 int pitch = 240*size;
\r
241 char *p = (char *)g_screen_ptr + (319-(firstcol-8))*pitch;
\r
242 for (i = 0; i < 8; i++, p -= pitch)
\r
243 memset(p+(firstline)*size, black, linecount*size);
\r
245 int pitch = 320*size;
\r
246 char *p = (char *)g_screen_ptr + (firstline)*pitch;
\r
247 for (i = 0; i < linecount; i++, p += pitch)
\r
248 memset(p+(firstcol-8)*size, black, 8*size);
\r
252 /* rot thing for Wiz */
\r
253 static unsigned char __attribute__((aligned(4))) rot_buff[320*4*2];
\r
255 static int EmuScanBegin16_rot(unsigned int num)
\r
257 Pico.est.DrawLineDest = rot_buff + (num & 3) * 320 * 2;
\r
261 static int EmuScanEnd16_rot(unsigned int num)
\r
263 if ((num & 3) != 3)
\r
265 rotated_blit16(g_screen_ptr, rot_buff, num + 1,
\r
266 !(Pico.video.reg[12] & 1) && !(PicoIn.opt & POPT_EN_SOFTSCALE));
\r
270 static int EmuScanBegin8_rot(unsigned int num)
\r
272 Pico.est.DrawLineDest = rot_buff + (num & 3) * 320;
\r
276 static int EmuScanEnd8_rot(unsigned int num)
\r
278 if ((num & 3) != 3)
\r
280 rotated_blit8(g_screen_ptr, rot_buff, num + 1,
\r
281 !(Pico.video.reg[12] & 1) && !(PicoIn.opt & POPT_EN_SOFTSCALE));
\r
285 /* line doublers */
\r
286 static unsigned int ld_counter;
\r
287 static int ld_left, ld_lines; // numbers in Q1 format
\r
289 static int EmuScanBegin16_ld(unsigned int num)
\r
291 if ((signed int)(ld_counter - num) > 100) {
\r
292 // vsync, offset so that the upscaled image is centered
\r
293 ld_counter = 120 - (120-num) * (ld_lines+2)/ld_lines;
\r
294 ld_left = ld_lines;
\r
297 if (emu_scan_begin)
\r
298 return emu_scan_begin(ld_counter);
\r
300 Pico.est.DrawLineDest = (char *)g_screen_ptr + 320 * ld_counter * gp2x_current_bpp / 8;
\r
305 static int EmuScanEnd16_ld(unsigned int num)
\r
307 void *oldline = Pico.est.DrawLineDest;
\r
310 emu_scan_end(ld_counter);
\r
314 if (ld_left <= 0) {
\r
315 ld_left += ld_lines;
\r
317 EmuScanBegin16_ld(num);
\r
318 memcpy(Pico.est.DrawLineDest, oldline, 320 * gp2x_current_bpp / 8);
\r
320 emu_scan_end(ld_counter);
\r
328 static int localPal[0x100];
\r
329 static int localPalSize;
\r
331 static void (*vidcpy8bit)(void *dest, void *src, int x_y, int w_h);
\r
332 static int (*make_local_pal)(int fast_mode);
\r
334 static int make_local_pal_md(int fast_mode)
\r
336 int pallen = 0x100;
\r
339 bgr444_to_rgb32(localPal, PicoMem.cram, 64);
\r
341 Pico.m.dirtyPal = 0;
\r
343 else if (Pico.est.rendstatus & PDRAW_SONIC_MODE) { // mid-frame palette changes
\r
344 switch (Pico.est.SonicPalCount) {
\r
345 case 3: bgr444_to_rgb32(localPal+0xc0, Pico.est.SonicPal+0xc0, 64);
\r
346 case 2: bgr444_to_rgb32(localPal+0x80, Pico.est.SonicPal+0x80, 64);
\r
347 case 1: bgr444_to_rgb32(localPal+0x40, Pico.est.SonicPal+0x40, 64);
\r
348 default:bgr444_to_rgb32(localPal, Pico.est.SonicPal, 64);
\r
350 pallen = (Pico.est.SonicPalCount+1)*0x40;
\r
352 else if (Pico.video.reg[0xC] & 8) { // shadow/hilight mode
\r
353 bgr444_to_rgb32(localPal, Pico.est.SonicPal, 64);
\r
354 bgr444_to_rgb32_sh(localPal, Pico.est.SonicPal);
\r
355 memcpy(localPal+0xc0, localPal, 0x40*4); // for spr prio mess
\r
358 bgr444_to_rgb32(localPal, Pico.est.SonicPal, 64);
\r
359 memcpy(localPal+0x40, localPal, 0x40*4); // for spr prio mess
\r
360 memcpy(localPal+0x80, localPal, 0x80*4); // for spr prio mess
\r
362 localPal[0xc0] = 0x0000c000;
\r
363 localPal[0xd0] = 0x00c00000;
\r
364 localPal[0xe0] = 0x00000000; // reserved pixels for OSD
\r
365 localPal[0xf0] = 0x00ffffff;
\r
367 if (Pico.m.dirtyPal == 2)
\r
368 Pico.m.dirtyPal = 0;
\r
372 static int make_local_pal_sms(int fast_mode)
\r
374 static u16 tmspal[32] = {
\r
375 // SMS palette for TMS modes
\r
376 0x0000, 0x0000, 0x00a0, 0x00f0, 0x0500, 0x0f00, 0x0005, 0x0ff0,
\r
377 0x000a, 0x000f, 0x0055, 0x00ff, 0x0050, 0x0f0f, 0x0555, 0x0fff,
\r
379 0x0000, 0x0000, 0x04c2, 0x07d6, 0x0e55, 0x0f77, 0x055c, 0x0ee4,
\r
380 0x055f, 0x077f, 0x05bc, 0x08ce, 0x03a2, 0x0b5c, 0x0ccc, 0x0fff,
\r
384 if (!(Pico.video.reg[0] & 0x4)) {
\r
385 for (i = Pico.est.SonicPalCount; i >= 0; i--) {
\r
386 int sg = !!(PicoIn.AHW & (PAHW_SG|PAHW_SC));
\r
387 bgr444_to_rgb32(localPal+i*0x40, tmspal+sg*0x10, 32);
\r
388 memcpy(localPal+i*0x40+0x20, localPal+i*0x40, 0x20*4);
\r
390 } else if (fast_mode) {
\r
391 for (i = 0;i >= 0; i--) {
\r
392 bgr444_to_rgb32(localPal+i*0x40, PicoMem.cram+i*0x40, 32);
\r
393 memcpy(localPal+i*0x40+0x20, localPal+i*0x40, 0x20*4);
\r
396 for (i = Pico.est.SonicPalCount; i >= 0; i--) {
\r
397 bgr444_to_rgb32(localPal+i*0x40, Pico.est.SonicPal+i*0x40, 32);
\r
398 memcpy(localPal+i*0x40+0x20, localPal+i*0x40, 0x20*4);
\r
401 if (Pico.m.dirtyPal == 2)
\r
402 Pico.m.dirtyPal = 0;
\r
403 return (Pico.est.SonicPalCount+1)*0x40;
\r
406 void pemu_finalize_frame(const char *fps, const char *notice)
\r
408 int emu_opt = currentConfig.EmuOpt;
\r
409 int direct_rendered = 1;
\r
411 if (is_16bit_mode())
\r
412 localPalSize = 0; // nothing to do
\r
413 else if (get_renderer() == RT_8BIT_FAST)
\r
415 // 8bit fast renderer
\r
416 if (Pico.m.dirtyPal)
\r
417 localPalSize = make_local_pal(1);
\r
419 if (PicoIn.AHW & PAHW_SVP)
\r
420 memset32((int *)(Pico.est.Draw2FB+328*8+328*223), 0xe0e0e0e0, 328/4);
\r
422 vidcpy8bit(g_screen_ptr, Pico.est.Draw2FB,
\r
423 (firstcol << 16) | firstline, (colcount << 16) | linecount);
\r
424 direct_rendered = 0;
\r
426 else if (get_renderer() == RT_8BIT_ACC)
\r
428 // 8bit accurate renderer
\r
429 if (Pico.m.dirtyPal)
\r
430 localPalSize = make_local_pal(0);
\r
433 // blank 1st column, only needed in modes directly rendering to screen
\r
434 if (is_1stblanked && direct_rendered)
\r
435 clear_1st_column(firstcol, firstline, linecount);
\r
438 osd_text(4, osd_y, notice);
\r
439 if (emu_opt & EOPT_SHOW_FPS)
\r
440 osd_text(osd_fps_x, osd_y, fps);
\r
441 if ((PicoIn.AHW & PAHW_MCD) && (emu_opt & EOPT_EN_CD_LEDS))
\r
443 if (PicoIn.AHW & PAHW_PICO) {
\r
444 int h = linecount, w = colcount;
\r
445 u16 *pd = g_screen_ptr + firstline*g_screen_ppitch + firstcol;
\r
447 if (pico_inp_mode && is_16bit_mode())
\r
448 emu_pico_overlay(pd, w, h, g_screen_ppitch);
\r
449 if (pico_inp_mode /*== 2 || overlay*/)
\r
454 void plat_video_flip(void)
\r
456 int stride = g_screen_width;
\r
458 // switching the palette takes immediate effect, whilst flipping only
\r
459 // takes effect with the next vsync; unavoidable flicker may occur!
\r
461 gp2x_video_setpalette(localPal, localPalSize);
\r
463 if (is_16bit_mode())
\r
465 // the fast renderer has overlap areas and can't directly render to
\r
466 // screen buffers. Its output is copied to screen in finalize_frame
\r
467 if (get_renderer() != RT_8BIT_FAST || (PicoIn.AHW & PAHW_32X))
\r
468 PicoDrawSetOutBuf(g_screen_ptr, stride);
\r
472 unsigned int plat_get_ticks_ms(void)
\r
474 return gp2x_get_ticks_ms();
\r
477 unsigned int plat_get_ticks_us(void)
\r
479 return gp2x_get_ticks_us();
\r
482 void plat_wait_till_us(unsigned int us_to)
\r
486 spend_cycles(1024);
\r
487 now = plat_get_ticks_us();
\r
489 while ((signed int)(us_to - now) > 512)
\r
491 spend_cycles(1024);
\r
492 now = plat_get_ticks_us();
\r
496 void plat_video_wait_vsync(void)
\r
498 gp2x_video_wait_vsync();
\r
501 void plat_status_msg_clear(void)
\r
503 int i, is_8bit = !is_16bit_mode();
\r
505 for (i = 0; i < 4; i++) {
\r
506 if (currentConfig.EmuOpt & EOPT_WIZ_TEAR_FIX) {
\r
510 p = (int *)gp2x_screens[i] + (240-8) / 4;
\r
511 for (u = 320; u > 0; u--, p += 240/4)
\r
512 p[0] = p[1] = 0xe0e0e0e0;
\r
514 p = (int *)gp2x_screens[i] + (240-8)*2 / 4;
\r
515 for (u = 320; u > 0; u--, p += 240*2/4)
\r
516 p[0] = p[1] = p[2] = p[3] = 0;
\r
520 char *d = (char *)gp2x_screens[i] + 320 * (240-8);
\r
521 memset32((int *)d, 0xe0e0e0e0, 320 * 8 / 4);
\r
523 char *d = (char *)gp2x_screens[i] + 320*2 * (240-8);
\r
524 memset32((int *)d, 0, 2*320 * 8 / 4);
\r
530 void plat_status_msg_busy_next(const char *msg)
\r
532 plat_status_msg_clear();
\r
533 pemu_finalize_frame("", msg);
\r
535 emu_status_msg("");
\r
537 /* assumption: msg_busy_next gets called only when
\r
538 * something slow is about to happen */
\r
542 void plat_status_msg_busy_first(const char *msg)
\r
544 plat_status_msg_busy_next(msg);
\r
547 static void vid_reset_mode(void)
\r
549 int gp2x_mode = 16;
\r
550 int renderer = get_renderer();
\r
552 PicoIn.opt &= ~(POPT_ALT_RENDERER|POPT_DIS_32C_BORDER|POPT_EN_SOFTSCALE);
\r
553 if (currentConfig.scaling == EOPT_SCALE_SW) {
\r
554 PicoIn.opt |= POPT_EN_SOFTSCALE;
\r
555 PicoIn.filter = EOPT_FILTER_BILINEAR2;
\r
556 } else if (currentConfig.scaling == EOPT_SCALE_HW)
\r
557 // hw scaling, render without any padding
\r
558 PicoIn.opt |= POPT_DIS_32C_BORDER;
\r
560 switch (renderer) {
\r
562 PicoDrawSetOutFormat(PDF_RGB555, 0);
\r
563 PicoDrawSetOutBuf(g_screen_ptr, g_screen_width * 2);
\r
566 PicoDrawSetOutFormat(PDF_8BIT, 0);
\r
567 PicoDrawSetOutBuf(g_screen_ptr, g_screen_width);
\r
571 PicoIn.opt |= POPT_ALT_RENDERER;
\r
572 PicoDrawSetOutFormat(PDF_NONE, 0);
\r
573 vidcpy8bit = vidcpy_8bit;
\r
577 printf("bad renderer\n");
\r
581 if (PicoIn.AHW & PAHW_32X) {
\r
582 // Wiz 16bit is an exception, uses line rendering due to rotation mess
\r
583 if (renderer == RT_16BIT && (currentConfig.EmuOpt & EOPT_WIZ_TEAR_FIX)) {
\r
584 PicoDrawSetOutFormat(PDF_RGB555, 1);
\r
586 PicoDrawSetOutBuf(g_screen_ptr, g_screen_width * 2);
\r
590 emu_scan_begin = NULL;
\r
591 emu_scan_end = NULL;
\r
593 if (currentConfig.EmuOpt & EOPT_WIZ_TEAR_FIX) {
\r
594 if ((PicoIn.AHW & PAHW_32X) || renderer == RT_16BIT) {
\r
595 emu_scan_begin = EmuScanBegin16_rot;
\r
596 emu_scan_end = EmuScanEnd16_rot;
\r
597 memset(rot_buff, 0, 320*4*2);
\r
599 else if (renderer == RT_8BIT_ACC) {
\r
600 emu_scan_begin = EmuScanBegin8_rot;
\r
601 emu_scan_end = EmuScanEnd8_rot;
\r
602 memset(rot_buff, 0xe0, 320*4);
\r
604 else if (renderer == RT_8BIT_FAST)
\r
605 vidcpy8bit = vidcpy_8bit_rot;
\r
608 PicoDrawSetCallbacks(emu_scan_begin, emu_scan_end);
\r
610 if (is_16bit_mode())
\r
611 osd_text = (currentConfig.EmuOpt & EOPT_WIZ_TEAR_FIX) ? osd_text16_rot : emu_osd_text16;
\r
613 osd_text = (currentConfig.EmuOpt & EOPT_WIZ_TEAR_FIX) ? osd_text8_rot : osd_text8;
\r
615 gp2x_video_wait_vsync();
\r
616 if (!is_16bit_mode()) {
\r
617 // setup pal for 8-bit modes
\r
618 localPal[0xc0] = 0x0000c000; // MCD LEDs
\r
619 localPal[0xd0] = 0x00c00000;
\r
620 localPal[0xe0] = 0x00000000; // reserved pixels for OSD
\r
621 localPal[0xf0] = 0x00ffffff;
\r
622 gp2x_video_setpalette(localPal, 0x100);
\r
625 if (currentConfig.EmuOpt & EOPT_WIZ_TEAR_FIX)
\r
626 gp2x_mode = -gp2x_mode;
\r
628 gp2x_video_changemode(gp2x_mode, Pico.m.pal);
\r
630 // clear whole screen in all buffers
\r
631 if (!is_16bit_mode())
\r
632 gp2x_memset_all_buffers(0, 0xe0, 320*240);
\r
634 gp2x_memset_all_buffers(0, 0, 320*240*2);
\r
636 Pico.m.dirtyPal = 1;
\r
638 // palette converters for 8bit modes
\r
639 make_local_pal = (PicoIn.AHW & PAHW_SMS) ? make_local_pal_sms : make_local_pal_md;
\r
642 void emu_video_mode_change(int start_line, int line_count, int start_col, int col_count)
\r
644 int scalex = 320, scaley = 240;
\r
647 if (currentConfig.vscaling != EOPT_SCALE_NONE &&
\r
648 (is_16bit_mode() || get_renderer() != RT_8BIT_FAST)) {
\r
649 /* NTSC always has 224 visible lines, anything smaller has bars */
\r
650 if (line_count < 224 && line_count > 144) {
\r
651 start_line -= (224-line_count) /2;
\r
655 /* line doubling for swscaling, also needed for bg frames */
\r
656 if (currentConfig.vscaling == EOPT_SCALE_SW && line_count < 240) {
\r
657 ld_lines = ld_left = 2*line_count / (240 - line_count);
\r
658 PicoDrawSetCallbacks(EmuScanBegin16_ld,EmuScanEnd16_ld);
\r
662 /* blanking for SMS with 1st tile blanked */
\r
663 is_1stblanked = (col_count == 248);
\r
664 firstline = start_line; linecount = line_count;
\r
665 firstcol = start_col; colcount = col_count;
\r
667 if (doing_bg_frame)
\r
670 osd_fps_x = OSD_FPS_X;
\r
673 /* set up hwscaling here */
\r
674 if (col_count < 320 && currentConfig.scaling == EOPT_SCALE_HW) {
\r
675 scalex = col_count;
\r
676 osd_fps_x = col_count - (320-OSD_FPS_X);
\r
679 if (currentConfig.vscaling == EOPT_SCALE_HW) {
\r
680 ln_offs = start_line;
\r
681 scaley = line_count;
\r
682 osd_y = start_line + line_count - 8;
\r
685 gp2x_video_RGB_setscaling(ln_offs, scalex, scaley);
\r
687 // clear whole screen in all buffers
\r
688 if (!is_16bit_mode())
\r
689 gp2x_memset_all_buffers(0, 0xe0, 320*240);
\r
691 gp2x_memset_all_buffers(0, 0, 320*240*2);
\r
694 void plat_video_toggle_renderer(int change, int is_menu_call)
\r
696 change_renderer(change);
\r
702 rendstatus_old = -1;
\r
704 if (PicoIn.AHW & PAHW_32X)
\r
705 emu_status_msg(renderer_names32x[get_renderer()]);
\r
707 emu_status_msg(renderer_names[get_renderer()]);
\r
711 static void RunEventsPico(unsigned int events)
\r
713 int ret, px, py, lim_x;
\r
714 static int pdown_frames = 0;
\r
717 ret = gp2x_touchpad_read(&px, &py);
\r
722 if (pdown_frames++ > 5)
\r
723 PicoIn.pad[0] |= 0x20;
\r
727 if (!(Pico.video.reg[12]&1)) {
\r
729 if (pico_pen_x < 0) pico_pen_x = 0;
\r
730 if (pico_pen_x > 248) pico_pen_x = 248;
\r
732 if (pico_pen_y > 224) pico_pen_y = 224;
\r
738 // PicoPicohw.pen_pos[0] = PicoPicohw.pen_pos[1] = 0x8000;
\r
743 void plat_update_volume(int has_changed, int is_up)
\r
745 static int prev_frame = 0, wait_frames = 0;
\r
746 int need_low_volume = 0;
\r
747 int vol = currentConfig.volume;
\r
750 soc = soc_detect();
\r
751 if ((PicoIn.opt & POPT_EN_STEREO) && soc == SOCID_MMSP2)
\r
752 need_low_volume = 1;
\r
756 if (need_low_volume && vol < 5 && prev_frame == Pico.m.frame_count - 1 && wait_frames < 12)
\r
760 plat_target_step_volume(¤tConfig.volume, is_up ? 1 : -1);
\r
761 vol = currentConfig.volume;
\r
763 emu_status_msg("VOL: %02i", vol);
\r
764 prev_frame = Pico.m.frame_count;
\r
767 if (!need_low_volume)
\r
770 /* set the right mixer func */
\r
772 PsndMix_32_to_16 = mix_32_to_16_stereo;
\r
774 mix_32_to_16_level = 5 - vol;
\r
775 PsndMix_32_to_16 = mix_32_to_16_stereo_lvl;
\r
779 void pemu_sound_start(void)
\r
785 if (currentConfig.EmuOpt & EOPT_EN_SOUND)
\r
787 soc = soc_detect();
\r
788 if (soc == SOCID_POLLUX) {
\r
789 PicoIn.sndRate = pollux_get_real_snd_rate(PicoIn.sndRate);
\r
790 PsndRerate(Pico.m.frame_count ? 1 : 0);
\r
793 plat_target_step_volume(¤tConfig.volume, 0);
\r
797 static const int sound_rates[] = { 52000, 44100, 32000, 22050, 16000, 11025, 8000 };
\r
799 void pemu_sound_stop(void)
\r
803 /* get back from Pollux pain */
\r
804 PicoIn.sndRate += 1000;
\r
805 for (i = 0; i < ARRAY_SIZE(sound_rates); i++) {
\r
806 if (PicoIn.sndRate >= sound_rates[i]) {
\r
807 PicoIn.sndRate = sound_rates[i];
\r
813 void pemu_forced_frame(int no_scale, int do_emu)
\r
815 doing_bg_frame = 1;
\r
816 PicoDrawSetCallbacks(NULL, NULL);
\r
817 Pico.m.dirtyPal = 1;
\r
818 PicoIn.opt &= ~POPT_DIS_32C_BORDER;
\r
819 gp2x_current_bpp = 16;
\r
820 // always render in screen 3 since menu uses 0-2
\r
821 g_screen_ptr = gp2x_screens[3];
\r
824 no_scale = currentConfig.scaling == EOPT_SCALE_NONE;
\r
825 emu_cmn_forced_frame(no_scale, do_emu, g_screen_ptr);
\r
828 clear_1st_column(firstcol, firstline, linecount);
\r
830 g_menubg_src_ptr = g_screen_ptr;
\r
831 doing_bg_frame = 0;
\r
834 void plat_debug_cat(char *str)
\r
838 void plat_video_loop_prepare(void)
\r
840 // make sure we are in correct mode
\r
841 change_renderer(0);
\r
843 rendstatus_old = -1;
\r
846 void pemu_loop_prep(void)
\r
848 if (gp2x_dev_id == GP2X_DEV_CAANOO)
\r
849 in_set_config_int(in_name_to_id("evdev:pollux-analog"),
\r
850 IN_CFG_ABS_DEAD_ZONE,
\r
851 currentConfig.analog_deadzone);
\r
853 // dirty buffers better go now than during gameplay
\r
858 void pemu_loop_end(void)
\r
862 if (g_screen_ptr == gp2x_screens[0]) {
\r
863 /* currently on screen 3, which is needed for forced_frame */
\r
864 int size = gp2x_current_bpp / 8;
\r
865 gp2x_memcpy_all_buffers(g_screen_ptr, 0, 320*240 * size);
\r
868 /* do one more frame for menu bg */
\r
869 pemu_forced_frame(0, 1);
\r