tweak and refactor frontends, menu and config
[libpicofe.git] / gp2x / emu.c
CommitLineData
662e622b 1/*\r
2 * (c) Copyright 2006-2010 notaz, All rights reserved.\r
3 *\r
4 * For performance reasons 3 renderers are exported for both MD and 32x modes:\r
5 * - 16bpp line renderer\r
6 * - 8bpp line renderer (slightly faster)\r
7 * - 8bpp tile renderer\r
8 * In 32x mode:\r
9 * - 32x layer is overlayed on top of 16bpp one\r
10 * - line internal one done on PicoDraw2FB, then mixed with 32x\r
11 * - tile internal one done on PicoDraw2FB, then mixed with 32x\r
12 */\r
720ee7f6 13\r
14#include <stdio.h>\r
15#include <stdlib.h>\r
c66f49e6 16#include <unistd.h>\r
720ee7f6 17\r
fa5e045b 18#include "plat_gp2x.h"\r
d572cbad 19#include "soc.h"\r
046c4540 20#include "../common/plat.h"\r
82abf46f 21#include "../common/menu.h"\r
c7a4ff64 22#include "../common/arm_utils.h"\r
23#include "../common/fonts.h"\r
f013066e 24#include "../common/emu.h"\r
0ae25549 25#include "../common/config.h"\r
902972d1 26#include "../common/input.h"\r
b3972d82 27#include "../linux/sndout_oss.h"\r
049a6b3e 28#include "version.h"\r
720ee7f6 29\r
f11bad75 30#include <pico/pico_int.h>\r
31#include <pico/patch.h>\r
32#include <pico/sound/mix.h>\r
70d2ecc5 33#include <zlib/zlib.h>\r
720ee7f6 34\r
720ee7f6 35#ifdef BENCHMARK\r
36#define OSD_FPS_X 220\r
37#else\r
38#define OSD_FPS_X 260\r
39#endif\r
40\r
720ee7f6 41\r
720ee7f6 42extern int crashed_940;\r
43\r
f9dd0a63 44static short __attribute__((aligned(4))) sndBuffer[2*(44100+100)/50];\r
93c18cb4 45static unsigned char PicoDraw2FB_[(8+320) * (8+240+8)];\r
46unsigned char *PicoDraw2FB = PicoDraw2FB_;\r
b6072c17 47static int osd_fps_x, osd_y, doing_bg_frame;\r
0c9ae592 48const char *renderer_names[] = { "16bit accurate", " 8bit accurate", " 8bit fast", NULL };\r
49const char *renderer_names32x[] = { "accurate", "faster", "fastest", NULL };\r
662e622b 50enum renderer_types { RT_16BIT, RT_8BIT_ACC, RT_8BIT_FAST, RT_COUNT };\r
720ee7f6 51\r
b6072c17 52static int (*emu_scan_begin)(unsigned int num) = NULL;\r
53static int (*emu_scan_end)(unsigned int num) = NULL;\r
720ee7f6 54\r
b6072c17 55extern void *gp2x_screens[4];\r
79cad122 56\r
57\r
93c18cb4 58void pemu_prep_defconfig(void)\r
0ae25549 59{\r
fa8d1331 60 gp2x_soc_t soc;\r
61\r
fa8d1331 62 defaultConfig.CPUclock = default_cpu_clock;\r
662e622b 63 defaultConfig.renderer32x = RT_8BIT_FAST;\r
902972d1 64 defaultConfig.analog_deadzone = 50;\r
fa8d1331 65\r
66 soc = soc_detect();\r
67 if (soc == SOCID_MMSP2)\r
68 defaultConfig.s_PicoOpt |= POPT_EXT_FM;\r
b7911801 69 else if (soc == SOCID_POLLUX)\r
053bef76 70 defaultConfig.EmuOpt |= EOPT_WIZ_TEAR_FIX|EOPT_SHOW_RTC;\r
0ae25549 71}\r
72\r
d2f29611 73void pemu_validate_config(void)\r
74{\r
b6072c17 75 if (gp2x_dev_id != GP2X_DEV_GP2X)\r
d2f29611 76 PicoOpt &= ~POPT_EXT_FM;\r
b6072c17 77 if (gp2x_dev_id != GP2X_DEV_WIZ)\r
d2f29611 78 currentConfig.EmuOpt &= ~EOPT_WIZ_TEAR_FIX;\r
79\r
80 if (currentConfig.gamma < 10 || currentConfig.gamma > 300)\r
81 currentConfig.gamma = 100;\r
82\r
83 if (currentConfig.CPUclock < 10 || currentConfig.CPUclock > 1024)\r
84 currentConfig.CPUclock = default_cpu_clock;\r
85}\r
86\r
662e622b 87static int get_renderer(void)\r
88{\r
89 if (PicoAHW & PAHW_32X)\r
90 return currentConfig.renderer32x;\r
91 else\r
92 return currentConfig.renderer;\r
93}\r
94\r
95static void change_renderer(int diff)\r
96{\r
97 int *r;\r
98 if (PicoAHW & PAHW_32X)\r
99 r = &currentConfig.renderer32x;\r
100 else\r
101 r = &currentConfig.renderer;\r
102 *r += diff;\r
b6072c17 103\r
104 // 8bpp fast is not there (yet?)\r
105 if ((PicoAHW & PAHW_SMS) && *r == RT_8BIT_FAST)\r
106 (*r)++;\r
107\r
662e622b 108 if (*r >= RT_COUNT)\r
109 *r = 0;\r
110 else if (*r < 0)\r
111 *r = RT_COUNT - 1;\r
112}\r
113\r
114#define is_16bit_mode() \\r
115 (get_renderer() == RT_16BIT || (PicoAHW & PAHW_32X))\r
116\r
b7911801 117static void (*osd_text)(int x, int y, const char *text);\r
118\r
119static void osd_text8(int x, int y, const char *text)\r
720ee7f6 120{\r
121 int len = strlen(text)*8;\r
e31266dd 122 int *p, i, h, offs;\r
720ee7f6 123\r
b7911801 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
129 *p = 0xe0e0e0e0;\r
130 }\r
131 emu_text_out8(x, y, text);\r
132}\r
133\r
134static void osd_text16(int x, int y, const char *text)\r
135{\r
136 int len = strlen(text)*8;\r
137 int *p, i, h, offs;\r
138\r
139 len = (len+1) >> 1;\r
140 for (h = 0; h < 8; h++) {\r
141 offs = (x + g_screen_width * (y+h)) & ~1;\r
142 p = (int *) ((short *)g_screen_ptr + offs);\r
143 for (i = len; i; i--, p++)\r
144 *p = (*p >> 2) & 0x39e7;\r
145 }\r
146 emu_text_out16(x, y, text);\r
147}\r
148\r
149static void osd_text8_rot(int x, int y, const char *text)\r
150{\r
151 int len = strlen(text) * 8;\r
152 char *p = (char *)g_screen_ptr + 240*(320-x) + y;\r
153\r
154 while (len--) {\r
155 memset(p, 0xe0, 8);\r
156 p -= 240;\r
720ee7f6 157 }\r
b7911801 158\r
159 emu_text_out8_rot(x, y, text);\r
160}\r
161\r
162static void osd_text16_rot(int x, int y, const char *text)\r
163{\r
164 int len = strlen(text) * 8;\r
165 short *p = (short *)g_screen_ptr + 240*(320-x) + y;\r
166\r
167 while (len--) {\r
168 memset(p, 0, 8*2);\r
169 p -= 240;\r
170 }\r
171\r
172 emu_text_out16_rot(x, y, text);\r
720ee7f6 173}\r
174\r
2b395ee0 175static void draw_cd_leds(void)\r
daf91588 176{\r
b7911801 177 int led_reg, pitch, scr_offs, led_offs;\r
178 led_reg = Pico_mcd->s68k_regs[0];\r
179\r
180 if (currentConfig.EmuOpt & EOPT_WIZ_TEAR_FIX) {\r
181 pitch = 240;\r
182 led_offs = -pitch * 6;\r
183 scr_offs = pitch * (320 - 4);\r
184 } else {\r
185 pitch = 320;\r
186 led_offs = 4;\r
187 scr_offs = pitch * 2 + 4;\r
188 }\r
daf91588 189\r
662e622b 190 if (!is_16bit_mode()) {\r
b7911801 191 #define p(x) px[(x) >> 2]\r
daf91588 192 // 8-bit modes\r
b7911801 193 unsigned int *px = (unsigned int *)((char *)g_screen_ptr + scr_offs);\r
194 unsigned int col_g = (led_reg & 2) ? 0xc0c0c0c0 : 0xe0e0e0e0;\r
195 unsigned int col_r = (led_reg & 1) ? 0xd0d0d0d0 : 0xe0e0e0e0;\r
196 p(pitch*0) = p(pitch*1) = p(pitch*2) = col_g;\r
197 p(pitch*0 + led_offs) = p(pitch*1 + led_offs) = p(pitch*2 + led_offs) = col_r;\r
198 #undef p\r
daf91588 199 } else {\r
b7911801 200 #define p(x) px[(x)*2 >> 2] = px[((x)*2 >> 2) + 1]\r
daf91588 201 // 16-bit modes\r
b7911801 202 unsigned int *px = (unsigned int *)((short *)g_screen_ptr + scr_offs);\r
203 unsigned int col_g = (led_reg & 2) ? 0x06000600 : 0;\r
204 unsigned int col_r = (led_reg & 1) ? 0xc000c000 : 0;\r
205 p(pitch*0) = p(pitch*1) = p(pitch*2) = col_g;\r
206 p(pitch*0 + led_offs) = p(pitch*1 + led_offs) = p(pitch*2 + led_offs) = col_r;\r
207 #undef p\r
daf91588 208 }\r
209}\r
210\r
2b395ee0 211static void draw_pico_ptr(void)\r
212{\r
e31266dd 213 unsigned short *p = (unsigned short *)g_screen_ptr;\r
b7911801 214 int x, y, pitch = 320;\r
2b395ee0 215\r
216 // only if pen enabled and for 16bit modes\r
662e622b 217 if (pico_inp_mode == 0 || currentConfig.EmuOpt != RT_16BIT)\r
b7911801 218 return;\r
2b395ee0 219\r
b7911801 220 x = pico_pen_x + PICO_PEN_ADJUST_X;\r
221 y = pico_pen_y + PICO_PEN_ADJUST_Y;\r
222 if (!(Pico.video.reg[12]&1) && !(PicoOpt & POPT_DIS_32C_BORDER))\r
223 x += 32;\r
2b395ee0 224\r
f9dd0a63 225 if (currentConfig.EmuOpt & EOPT_WIZ_TEAR_FIX) {\r
b7911801 226 pitch = 240;\r
227 p += (319 - x) * pitch + y;\r
228 } else\r
229 p += x + y * pitch;\r
230\r
231 p[0] ^= 0xffff;\r
232 p[pitch-1] ^= 0xffff;\r
233 p[pitch] ^= 0xffff;\r
234 p[pitch+1] ^= 0xffff;\r
235 p[pitch*2] ^= 0xffff;\r
2b395ee0 236}\r
237\r
b7911801 238/* rot thing for Wiz */\r
239static unsigned char __attribute__((aligned(4))) rot_buff[320*4*2];\r
240\r
241static int EmuScanBegin16_rot(unsigned int num)\r
242{\r
243 DrawLineDest = rot_buff + (num & 3) * 320 * 2;\r
244 return 0;\r
245}\r
246\r
247static int EmuScanEnd16_rot(unsigned int num)\r
248{\r
249 if ((num & 3) != 3)\r
250 return 0;\r
b7911801 251 rotated_blit16(g_screen_ptr, rot_buff, num + 1,\r
252 !(Pico.video.reg[12] & 1) && !(PicoOpt & POPT_EN_SOFTSCALE));\r
253 return 0;\r
254}\r
255\r
256static int EmuScanBegin8_rot(unsigned int num)\r
257{\r
258 DrawLineDest = rot_buff + (num & 3) * 320;\r
259 return 0;\r
260}\r
261\r
262static int EmuScanEnd8_rot(unsigned int num)\r
263{\r
264 if ((num & 3) != 3)\r
265 return 0;\r
b7911801 266 rotated_blit8(g_screen_ptr, rot_buff, num + 1,\r
267 !(Pico.video.reg[12] & 1));\r
268 return 0;\r
269}\r
270\r
b6072c17 271/* line doublers */\r
272static unsigned int ld_counter;\r
273static int ld_left, ld_lines;\r
274\r
275static int EmuScanBegin16_ld(unsigned int num)\r
276{\r
277 if ((signed int)(ld_counter - num) > 100)\r
278 ld_counter = 0;\r
279\r
280 if (emu_scan_begin)\r
281 return emu_scan_begin(ld_counter);\r
282 else\r
283 DrawLineDest = (char *)g_screen_ptr + 320 * ld_counter * gp2x_current_bpp / 8;\r
284\r
285 return 0;\r
286}\r
287\r
288static int EmuScanEnd16_ld(unsigned int num)\r
289{\r
290 void *oldline = DrawLineDest;\r
291\r
292 if (emu_scan_end)\r
293 emu_scan_end(ld_counter);\r
294\r
295 ld_counter++;\r
296 ld_left--;\r
297 if (ld_left <= 0) {\r
298 ld_left = ld_lines;\r
299\r
300 EmuScanBegin16_ld(num);\r
301 memcpy32(DrawLineDest, oldline, 320 * gp2x_current_bpp / 8 / 4);\r
302 if (emu_scan_end)\r
303 emu_scan_end(ld_counter);\r
304\r
305 ld_counter++;\r
306 }\r
307\r
308 return 0;\r
309}\r
310\r
ab8a8d36 311static int localPal[0x100];\r
312static void (*vidcpyM2)(void *dest, void *src, int m32col, int with_32c_border);\r
313static int (*make_local_pal)(int fast_mode);\r
314\r
315static int make_local_pal_md(int fast_mode)\r
316{\r
317 int pallen = 0xc0;\r
318\r
319 bgr444_to_rgb32(localPal, Pico.cram);\r
320 if (fast_mode)\r
321 return 0x40;\r
322\r
323 if (Pico.video.reg[0xC] & 8) { // shadow/hilight mode\r
324 bgr444_to_rgb32_sh(localPal, Pico.cram);\r
325 localPal[0xc0] = 0x0000c000;\r
326 localPal[0xd0] = 0x00c00000;\r
327 localPal[0xe0] = 0x00000000; // reserved pixels for OSD\r
328 localPal[0xf0] = 0x00ffffff;\r
329 pallen = 0x100;\r
330 }\r
331 else if (rendstatus & PDRAW_SONIC_MODE) { // mid-frame palette changes\r
332 bgr444_to_rgb32(localPal+0x40, HighPal);\r
333 bgr444_to_rgb32(localPal+0x80, HighPal+0x40);\r
334 }\r
335 else\r
336 memcpy32(localPal+0x80, localPal, 0x40); // for spr prio mess\r
337\r
338 return pallen;\r
339}\r
340\r
341static int make_local_pal_sms(int fast_mode)\r
342{\r
343 unsigned short *spal = Pico.cram;\r
344 unsigned int *dpal = (void *)localPal;\r
345 unsigned int i, t;\r
346\r
347 for (i = 0x40; i > 0; i--) {\r
348 t = *spal++;\r
349 t = ((t & 0x0003) << 22) | ((t & 0x000c) << 12) | ((t & 0x0030) << 2);\r
350 t |= t >> 2;\r
351 t |= t >> 4;\r
352 *dpal++ = t;\r
353 }\r
354\r
355 return 0x40;\r
356}\r
720ee7f6 357\r
b188c2b6 358void pemu_finalize_frame(const char *fps, const char *notice)\r
720ee7f6 359{\r
daf91588 360 int emu_opt = currentConfig.EmuOpt;\r
ab8a8d36 361 int ret;\r
daf91588 362\r
662e622b 363 if (PicoAHW & PAHW_32X)\r
364 ; // nothing to do\r
365 else if (get_renderer() == RT_8BIT_FAST)\r
b3818d1e 366 {\r
720ee7f6 367 // 8bit fast renderer\r
368 if (Pico.m.dirtyPal) {\r
369 Pico.m.dirtyPal = 0;\r
ab8a8d36 370 ret = make_local_pal(1);\r
720ee7f6 371 // feed new palette to our device\r
ab8a8d36 372 gp2x_video_setpalette(localPal, ret);\r
720ee7f6 373 }\r
b3818d1e 374 // a hack for VR\r
a4bd56c6 375 if (PicoAHW & PAHW_SVP)\r
b3818d1e 376 memset32((int *)(PicoDraw2FB+328*8+328*223), 0xe0e0e0e0, 328);\r
377 // do actual copy\r
b7911801 378 vidcpyM2(g_screen_ptr, PicoDraw2FB+328*8,\r
379 !(Pico.video.reg[12] & 1), !(PicoOpt & POPT_DIS_32C_BORDER));\r
b3818d1e 380 }\r
662e622b 381 else if (get_renderer() == RT_8BIT_ACC)\r
b3818d1e 382 {\r
720ee7f6 383 // 8bit accurate renderer\r
dd5fd477 384 if (Pico.m.dirtyPal)\r
385 {\r
720ee7f6 386 Pico.m.dirtyPal = 0;\r
ab8a8d36 387 ret = make_local_pal(0);\r
388 gp2x_video_setpalette(localPal, ret);\r
720ee7f6 389 }\r
390 }\r
391\r
b6072c17 392 if (notice)\r
393 osd_text(4, osd_y, notice);\r
394 if (emu_opt & EOPT_SHOW_FPS)\r
395 osd_text(osd_fps_x, osd_y, fps);\r
d2f29611 396 if ((PicoAHW & PAHW_MCD) && (emu_opt & EOPT_EN_CD_LEDS))\r
2b395ee0 397 draw_cd_leds();\r
398 if (PicoAHW & PAHW_PICO)\r
399 draw_pico_ptr();\r
b188c2b6 400}\r
720ee7f6 401\r
b188c2b6 402void plat_video_flip(void)\r
403{\r
662e622b 404 int stride = g_screen_width;\r
720ee7f6 405 gp2x_video_flip();\r
662e622b 406\r
407 if (is_16bit_mode())\r
408 stride *= 2;\r
409 PicoDrawSetOutBuf(g_screen_ptr, stride);\r
720ee7f6 410}\r
411\r
b5bfb864 412/* XXX */\r
413#ifdef __GP2X__\r
414unsigned int plat_get_ticks_ms(void)\r
415{\r
416 return gp2x_get_ticks_ms();\r
417}\r
418\r
419unsigned int plat_get_ticks_us(void)\r
420{\r
421 return gp2x_get_ticks_us();\r
422}\r
423#endif\r
424\r
425void plat_wait_till_us(unsigned int us_to)\r
426{\r
427 unsigned int now;\r
428\r
429 spend_cycles(1024);\r
430 now = plat_get_ticks_us();\r
431\r
432 while ((signed int)(us_to - now) > 512)\r
433 {\r
434 spend_cycles(1024);\r
435 now = plat_get_ticks_us();\r
436 }\r
437}\r
438\r
439void plat_video_wait_vsync(void)\r
440{\r
441 gp2x_video_wait_vsync();\r
442}\r
443\r
444void plat_status_msg_clear(void)\r
720ee7f6 445{\r
662e622b 446 int is_8bit = !is_16bit_mode();\r
b5bfb864 447 if (currentConfig.EmuOpt & EOPT_WIZ_TEAR_FIX) {\r
b7911801 448 /* ugh.. */\r
449 int i, u, *p;\r
450 if (is_8bit) {\r
451 for (i = 0; i < 4; i++) {\r
452 p = (int *)gp2x_screens[i] + (240-8) / 4;\r
453 for (u = 320; u > 0; u--, p += 240/4)\r
454 p[0] = p[1] = 0xe0e0e0e0;\r
455 }\r
456 } else {\r
457 for (i = 0; i < 4; i++) {\r
458 p = (int *)gp2x_screens[i] + (240-8)*2 / 4;\r
459 for (u = 320; u > 0; u--, p += 240*2/4)\r
460 p[0] = p[1] = p[2] = p[3] = 0;\r
461 }\r
462 }\r
463 return;\r
464 }\r
465\r
b5bfb864 466 if (is_8bit)\r
467 gp2x_memset_all_buffers(320*232, 0xe0, 320*8);\r
468 else\r
469 gp2x_memset_all_buffers(320*232*2, 0, 320*8*2);\r
720ee7f6 470}\r
471\r
388947f3 472void plat_status_msg_busy_next(const char *msg)\r
473{\r
b5bfb864 474 plat_status_msg_clear();\r
b188c2b6 475 pemu_finalize_frame("", msg);\r
476 plat_video_flip();\r
9615b3df 477 emu_status_msg("");\r
388947f3 478\r
479 /* assumption: msg_busy_next gets called only when\r
480 * something slow is about to happen */\r
481 reset_timing = 1;\r
482}\r
483\r
484void plat_status_msg_busy_first(const char *msg)\r
485{\r
486 gp2x_memcpy_all_buffers(g_screen_ptr, 0, 320*240*2);\r
487 plat_status_msg_busy_next(msg);\r
488}\r
720ee7f6 489\r
b6072c17 490static void vid_reset_mode(void)\r
720ee7f6 491{\r
662e622b 492 int gp2x_mode = 16;\r
493 int renderer = get_renderer();\r
494\r
b6072c17 495 PicoOpt &= ~POPT_ALT_RENDERER;\r
496 emu_scan_begin = NULL;\r
497 emu_scan_end = NULL;\r
b7911801 498\r
662e622b 499 switch (renderer) {\r
500 case RT_16BIT:\r
662e622b 501 PicoDrawSetOutFormat(PDF_RGB555, 0);\r
502 PicoDrawSetOutBuf(g_screen_ptr, g_screen_width * 2);\r
662e622b 503 break;\r
504 case RT_8BIT_ACC:\r
662e622b 505 PicoDrawSetOutFormat(PDF_8BIT, 0);\r
506 PicoDrawSetOutBuf(g_screen_ptr, g_screen_width);\r
662e622b 507 gp2x_mode = 8;\r
508 break;\r
509 case RT_8BIT_FAST:\r
b6072c17 510 PicoOpt |= POPT_ALT_RENDERER;\r
662e622b 511 PicoDrawSetOutFormat(PDF_NONE, 0);\r
b6072c17 512 vidcpyM2 = vidcpy_m2;\r
662e622b 513 gp2x_mode = 8;\r
514 break;\r
b6072c17 515 default:\r
516 printf("bad renderer\n");\r
517 break;\r
daf91588 518 }\r
b7911801 519\r
662e622b 520 if (PicoAHW & PAHW_32X) {\r
662e622b 521 // Wiz 16bit is an exception, uses line rendering due to rotation mess\r
522 if (renderer == RT_16BIT && (currentConfig.EmuOpt & EOPT_WIZ_TEAR_FIX)) {\r
523 PicoDrawSetOutFormat(PDF_RGB555, 1);\r
524 PicoDraw32xSetFrameMode(0, 0);\r
525 }\r
526 else {\r
b6072c17 527 PicoDrawSetOutFormat(PDF_NONE, 0);\r
902972d1 528 PicoDraw32xSetFrameMode(1, 0);\r
662e622b 529 }\r
530 PicoDrawSetOutBuf(g_screen_ptr, g_screen_width * 2);\r
902972d1 531 gp2x_mode = 16;\r
662e622b 532 }\r
533\r
b6072c17 534 if (currentConfig.EmuOpt & EOPT_WIZ_TEAR_FIX) {\r
535 if ((PicoAHW & PAHW_32X) || renderer == RT_16BIT) {\r
536 emu_scan_begin = EmuScanBegin16_rot;\r
537 emu_scan_end = EmuScanEnd16_rot;\r
538 }\r
539 else if (renderer == RT_8BIT_ACC) {\r
540 emu_scan_begin = EmuScanBegin8_rot;\r
541 emu_scan_end = EmuScanEnd8_rot;\r
542 }\r
543 else if (renderer == RT_8BIT_FAST)\r
544 vidcpyM2 = vidcpy_m2_rot;\r
545 }\r
546\r
547 PicoDrawSetCallbacks(emu_scan_begin, emu_scan_end);\r
548\r
549 if (is_16bit_mode())\r
550 osd_text = (currentConfig.EmuOpt & EOPT_WIZ_TEAR_FIX) ? osd_text16_rot : osd_text16;\r
551 else\r
552 osd_text = (currentConfig.EmuOpt & EOPT_WIZ_TEAR_FIX) ? osd_text8_rot : osd_text8;\r
553\r
b6072c17 554 gp2x_video_wait_vsync();\r
662e622b 555 if (!is_16bit_mode()) {\r
daf91588 556 // setup pal for 8-bit modes\r
557 localPal[0xc0] = 0x0000c000; // MCD LEDs\r
558 localPal[0xd0] = 0x00c00000;\r
720ee7f6 559 localPal[0xe0] = 0x00000000; // reserved pixels for OSD\r
560 localPal[0xf0] = 0x00ffffff;\r
720ee7f6 561 gp2x_video_setpalette(localPal, 0x100);\r
562 gp2x_memset_all_buffers(0, 0xe0, 320*240);\r
720ee7f6 563 }\r
b6072c17 564 else\r
565 gp2x_memset_all_buffers(0, 0, 320*240*2);\r
566\r
902972d1 567 if (currentConfig.EmuOpt & EOPT_WIZ_TEAR_FIX)\r
568 gp2x_mode = -gp2x_mode;\r
569\r
570 gp2x_video_changemode(gp2x_mode);\r
571\r
720ee7f6 572 Pico.m.dirtyPal = 1;\r
b7911801 573\r
b6072c17 574 PicoOpt &= ~POPT_EN_SOFTSCALE;\r
575 if (currentConfig.scaling == EOPT_SCALE_SW)\r
576 PicoOpt |= POPT_EN_SOFTSCALE;\r
ab8a8d36 577\r
578 // palette converters for 8bit modes\r
579 make_local_pal = (PicoAHW & PAHW_SMS) ? make_local_pal_sms : make_local_pal_md;\r
720ee7f6 580}\r
581\r
b6072c17 582void emu_video_mode_change(int start_line, int line_count, int is_32cols)\r
583{\r
584 int scalex = 320, scaley = 240;\r
585 int ln_offs = 0;\r
586\r
587 if (doing_bg_frame)\r
588 return;\r
589\r
590 osd_fps_x = OSD_FPS_X;\r
591 osd_y = 232;\r
592\r
593 /* set up hwscaling here */\r
594 PicoOpt &= ~POPT_DIS_32C_BORDER;\r
595 if (is_32cols && currentConfig.scaling == EOPT_SCALE_HW) {\r
596 scalex = 256;\r
597 PicoOpt |= POPT_DIS_32C_BORDER;\r
598 osd_fps_x = OSD_FPS_X - 64;\r
599 }\r
600\r
601 if (currentConfig.vscaling == EOPT_SCALE_HW) {\r
602 ln_offs = start_line;\r
603 scaley = line_count;\r
604 osd_y = start_line + line_count - 8;\r
605 }\r
606\r
607 gp2x_video_RGB_setscaling(ln_offs, scalex, scaley);\r
608\r
609 /* line doubling */\r
610 if (currentConfig.vscaling == EOPT_SCALE_SW && line_count < 240) {\r
611 ld_lines = ld_left = line_count / (240 - line_count);\r
612 PicoDrawSetCallbacks(EmuScanBegin16_ld, EmuScanEnd16_ld);\r
613 }\r
614\r
615 // clear whole screen in all buffers\r
616 if (!is_16bit_mode())\r
617 gp2x_memset_all_buffers(0, 0xe0, 320*240);\r
618 else\r
619 gp2x_memset_all_buffers(0, 0, 320*240*2);\r
620}\r
621\r
662e622b 622void plat_video_toggle_renderer(int change, int is_menu_call)\r
dfa4c846 623{\r
662e622b 624 change_renderer(change);\r
9ecdd73a 625\r
662e622b 626 if (is_menu_call)\r
9ecdd73a 627 return;\r
e5589947 628\r
b6072c17 629 vid_reset_mode();\r
ab8a8d36 630 rendstatus_old = -1;\r
dfa4c846 631\r
662e622b 632 if (PicoAHW & PAHW_32X)\r
633 emu_status_msg(renderer_names32x[get_renderer()]);\r
634 else\r
635 emu_status_msg(renderer_names[get_renderer()]);\r
1078e544 636}\r
637\r
93c18cb4 638#if 0 // TODO\r
82abf46f 639static void RunEventsPico(unsigned int events)\r
e1cd546f 640{\r
6589c840 641 int ret, px, py, lim_x;\r
3f2aaff2 642 static int pdown_frames = 0;\r
643\r
3f2aaff2 644 // for F200\r
645 ret = gp2x_touchpad_read(&px, &py);\r
6589c840 646 if (ret >= 0)\r
647 {\r
648 if (ret > 35000)\r
649 {\r
3f2aaff2 650 if (pdown_frames++ > 5)\r
651 PicoPad[0] |= 0x20;\r
652\r
653 pico_pen_x = px;\r
654 pico_pen_y = py;\r
655 if (!(Pico.video.reg[12]&1)) {\r
656 pico_pen_x -= 32;\r
657 if (pico_pen_x < 0) pico_pen_x = 0;\r
658 if (pico_pen_x > 248) pico_pen_x = 248;\r
659 }\r
660 if (pico_pen_y > 224) pico_pen_y = 224;\r
661 }\r
662 else\r
6589c840 663 pdown_frames = 0;\r
3f2aaff2 664\r
665 //if (ret == 0)\r
666 // PicoPicohw.pen_pos[0] = PicoPicohw.pen_pos[1] = 0x8000;\r
667 }\r
e1cd546f 668}\r
93c18cb4 669#endif\r
e1cd546f 670\r
388947f3 671void plat_update_volume(int has_changed, int is_up)\r
0480e6c9 672{\r
673 static int prev_frame = 0, wait_frames = 0;\r
674 int vol = currentConfig.volume;\r
388947f3 675 int need_low_volume = 0;\r
676 gp2x_soc_t soc;\r
677\r
678 soc = soc_detect();\r
679 if ((PicoOpt & POPT_EN_STEREO) && soc == SOCID_MMSP2)\r
680 need_low_volume = 1;\r
0480e6c9 681\r
682 if (has_changed)\r
683 {\r
388947f3 684 if (need_low_volume && vol < 5 && prev_frame == Pico.m.frame_count - 1 && wait_frames < 12)\r
0480e6c9 685 wait_frames++;\r
686 else {\r
687 if (is_up) {\r
688 if (vol < 99) vol++;\r
689 } else {\r
690 if (vol > 0) vol--;\r
691 }\r
692 wait_frames = 0;\r
b3972d82 693 sndout_oss_setvol(vol, vol);\r
0480e6c9 694 currentConfig.volume = vol;\r
695 }\r
b5bfb864 696 emu_status_msg("VOL: %02i", vol);\r
0480e6c9 697 prev_frame = Pico.m.frame_count;\r
698 }\r
699\r
22b2271a 700 if (!need_low_volume)\r
388947f3 701 return;\r
702\r
703 /* set the right mixer func */\r
0480e6c9 704 if (vol >= 5)\r
705 PsndMix_32_to_16l = mix_32_to_16l_stereo;\r
706 else {\r
707 mix_32_to_16l_level = 5 - vol;\r
708 PsndMix_32_to_16l = mix_32_to_16l_stereo_lvl;\r
709 }\r
710}\r
711\r
3ef975c9 712static void updateSound(int len)\r
720ee7f6 713{\r
dd59a74b 714 len <<= 1;\r
b5bfb864 715 if (PicoOpt & POPT_EN_STEREO)\r
716 len <<= 1;\r
720ee7f6 717\r
dd59a74b 718 if ((currentConfig.EmuOpt & EOPT_NO_FRMLIMIT) && !sndout_oss_can_write(len))\r
719 return;\r
720\r
d9a18943 721 /* avoid writing audio when lagging behind to prevent audio lag */\r
722 if (PicoSkipFrame != 2)\r
dd59a74b 723 sndout_oss_write(PsndOut, len);\r
720ee7f6 724}\r
725\r
93c18cb4 726void pemu_sound_start(void)\r
725d7f6c 727{\r
728 static int PsndRate_old = 0, PicoOpt_old = 0, pal_old = 0;\r
725d7f6c 729\r
730 PsndOut = NULL;\r
731\r
732 // prepare sound stuff\r
9615b3df 733 if (currentConfig.EmuOpt & EOPT_EN_SOUND)\r
725d7f6c 734 {\r
9615b3df 735 int is_stereo = (PicoOpt & POPT_EN_STEREO) ? 1 : 0;\r
736 int target_fps = Pico.m.pal ? 50 : 60;\r
dd59a74b 737 int frame_samples, snd_excess_add;\r
f9dd0a63 738 int snd_rate_oss = PsndRate;\r
9615b3df 739 gp2x_soc_t soc;\r
740\r
dd59a74b 741 soc = soc_detect();\r
f9dd0a63 742 if (soc == SOCID_POLLUX) {\r
743 /* POLLUX pain: DPLL1 / mclk_div / bitclk_div / 4 */\r
744 switch (PsndRate) {\r
745 case 44100: PsndRate = 44171; break; // 44170.673077\r
746 case 22050: PsndRate = 22086; break; // 22085.336538\r
747 case 11025: PsndRate = 11043; break; // 11042.668269\r
748 default: break;\r
749 }\r
750 }\r
dd59a74b 751\r
9615b3df 752 #define SOUND_RERATE_FLAGS (POPT_EN_FM|POPT_EN_PSG|POPT_EN_STEREO|POPT_EXT_FM|POPT_EN_MCD_CDDA)\r
753 if (PsndRate != PsndRate_old || Pico.m.pal != pal_old || ((PicoOpt & POPT_EXT_FM) && crashed_940) ||\r
754 ((PicoOpt ^ PicoOpt_old) & SOUND_RERATE_FLAGS)) {\r
725d7f6c 755 PsndRerate(Pico.m.frame_count ? 1 : 0);\r
756 }\r
dd59a74b 757\r
725d7f6c 758 memset(sndBuffer, 0, sizeof(sndBuffer));\r
759 PsndOut = sndBuffer;\r
dd59a74b 760 PicoWriteSound = updateSound;\r
725d7f6c 761 PsndRate_old = PsndRate;\r
762 PicoOpt_old = PicoOpt;\r
763 pal_old = Pico.m.pal;\r
dd59a74b 764 plat_update_volume(0, 0);\r
765\r
766 frame_samples = PsndLen;\r
767 snd_excess_add = ((PsndRate - PsndLen * target_fps)<<16) / target_fps;\r
768 if (snd_excess_add != 0)\r
769 frame_samples++;\r
770 if (soc == SOCID_POLLUX)\r
771 frame_samples *= 2; /* force larger buffer */\r
772\r
773 printf("starting audio: %i len: %i (ex: %04x) stereo: %i, pal: %i\n",\r
774 PsndRate, PsndLen, snd_excess_add, is_stereo, Pico.m.pal);\r
775 sndout_oss_setvol(currentConfig.volume, currentConfig.volume);\r
f9dd0a63 776 sndout_oss_start(snd_rate_oss, frame_samples, is_stereo);\r
9615b3df 777\r
778 /* Wiz's sound hardware needs more prebuffer */\r
9615b3df 779 if (soc == SOCID_POLLUX)\r
dd59a74b 780 updateSound(frame_samples);\r
725d7f6c 781 }\r
782}\r
783\r
93c18cb4 784void pemu_sound_stop(void)\r
725d7f6c 785{\r
f9dd0a63 786 /* get back from Wiz pain */\r
787 switch (PsndRate) {\r
788 case 44171: PsndRate = 44100; break;\r
789 case 22086: PsndRate = 22050; break;\r
790 case 11043: PsndRate = 11025; break;\r
791 default: break;\r
792 }\r
725d7f6c 793}\r
794\r
93c18cb4 795void pemu_sound_wait(void)\r
725d7f6c 796{\r
797 // don't need to do anything, writes will block by themselves\r
798}\r
799\r
902972d1 800void pemu_forced_frame(int no_scale, int do_emu)\r
4ffd2858 801{\r
802 int po_old = PicoOpt;\r
13059a60 803\r
b6072c17 804 doing_bg_frame = 1;\r
93c18cb4 805 PicoOpt &= ~POPT_ALT_RENDERER;\r
902972d1 806 PicoOpt |= POPT_ACC_SPRITES;\r
807 if (!no_scale)\r
808 PicoOpt |= POPT_EN_SOFTSCALE;\r
4ffd2858 809\r
b6072c17 810 memset32(g_screen_ptr, 0, g_screen_width * g_screen_height * 2 / 4);\r
811\r
662e622b 812 PicoDrawSetOutFormat(PDF_RGB555, 1);\r
813 PicoDrawSetOutBuf(g_screen_ptr, g_screen_width * 2);\r
814 PicoDraw32xSetFrameMode(0, 0);\r
b6072c17 815 PicoDrawSetCallbacks(NULL, NULL);\r
13059a60 816 Pico.m.dirtyPal = 1;\r
4ffd2858 817\r
902972d1 818 if (do_emu)\r
b6072c17 819 PicoFrame();\r
820 else\r
821 PicoFrameDrawOnly();\r
822\r
902972d1 823 g_menubg_src_ptr = g_screen_ptr;\r
b6072c17 824 doing_bg_frame = 0;\r
e1aa0fa7 825 PicoOpt = po_old;\r
4ffd2858 826}\r
827\r
93c18cb4 828void plat_debug_cat(char *str)\r
7443ecd9 829{\r
7443ecd9 830}\r
831\r
450dab6f 832#if 0\r
833static void tga_dump(void)\r
834{\r
835#define BYTE unsigned char\r
836#define WORD unsigned short\r
837 struct\r
838 {\r
839 BYTE IDLength; /* 00h Size of Image ID field */\r
840 BYTE ColorMapType; /* 01h Color map type */\r
841 BYTE ImageType; /* 02h Image type code */\r
842 WORD CMapStart; /* 03h Color map origin */\r
843 WORD CMapLength; /* 05h Color map length */\r
844 BYTE CMapDepth; /* 07h Depth of color map entries */\r
845 WORD XOffset; /* 08h X origin of image */\r
846 WORD YOffset; /* 0Ah Y origin of image */\r
847 WORD Width; /* 0Ch Width of image */\r
848 WORD Height; /* 0Eh Height of image */\r
849 BYTE PixelDepth; /* 10h Image pixel size */\r
850 BYTE ImageDescriptor; /* 11h Image descriptor byte */\r
851 } __attribute__((packed)) TGAHEAD;\r
852 static unsigned short oldscr[320*240];\r
853 FILE *f; char name[128]; int i;\r
854\r
855 memset(&TGAHEAD, 0, sizeof(TGAHEAD));\r
856 TGAHEAD.ImageType = 2;\r
857 TGAHEAD.Width = 320;\r
858 TGAHEAD.Height = 240;\r
859 TGAHEAD.PixelDepth = 16;\r
860 TGAHEAD.ImageDescriptor = 2<<4; // image starts at top-left\r
861\r
862#define CONV(X) (((X>>1)&0x7fe0)|(X&0x1f)) // 555?\r
863\r
864 for (i = 0; i < 320*240; i++)\r
e31266dd 865 if(oldscr[i] != CONV(((unsigned short *)g_screen_ptr)[i])) break;\r
450dab6f 866 if (i < 320*240)\r
867 {\r
868 for (i = 0; i < 320*240; i++)\r
e31266dd 869 oldscr[i] = CONV(((unsigned short *)g_screen_ptr)[i]);\r
450dab6f 870 sprintf(name, "%05i.tga", Pico.m.frame_count);\r
871 f = fopen(name, "wb");\r
872 if (!f) { printf("!f\n"); exit(1); }\r
873 fwrite(&TGAHEAD, 1, sizeof(TGAHEAD), f);\r
874 fwrite(oldscr, 1, 320*240*2, f);\r
875 fclose(f);\r
876 }\r
877}\r
878#endif\r
879\r
b5bfb864 880void pemu_loop_prep(void)\r
720ee7f6 881{\r
b7911801 882 static int gp2x_old_clock = -1, EmuOpt_old = 0, pal_old = 0;\r
883 static int gp2x_old_gamma = 100;\r
f71361b5 884 gp2x_soc_t soc;\r
885\r
886 soc = soc_detect();\r
720ee7f6 887\r
fa5e045b 888 if ((EmuOpt_old ^ currentConfig.EmuOpt) & EOPT_RAM_TIMINGS) {\r
889 if (currentConfig.EmuOpt & EOPT_RAM_TIMINGS)\r
fa8d1331 890 set_ram_timings();\r
fa5e045b 891 else\r
892 unset_ram_timings();\r
893 }\r
894\r
895 if (gp2x_old_clock < 0)\r
896 gp2x_old_clock = default_cpu_clock;\r
b6072c17 897 if (gp2x_old_clock != currentConfig.CPUclock && gp2x_set_cpuclk != NULL) {\r
720ee7f6 898 printf("changing clock to %i...", currentConfig.CPUclock); fflush(stdout);\r
fa5e045b 899 gp2x_set_cpuclk(currentConfig.CPUclock);\r
720ee7f6 900 gp2x_old_clock = currentConfig.CPUclock;\r
901 printf(" done\n");\r
902 }\r
903\r
f71361b5 904 if (gp2x_old_gamma != currentConfig.gamma || ((EmuOpt_old ^ currentConfig.EmuOpt) & EOPT_A_SN_GAMMA)) {\r
905 set_lcd_gamma(currentConfig.gamma, !!(currentConfig.EmuOpt & EOPT_A_SN_GAMMA));\r
720ee7f6 906 gp2x_old_gamma = currentConfig.gamma;\r
55a951dd 907 printf("updated gamma to %i, A_SN's curve: %i\n", currentConfig.gamma, !!(currentConfig.EmuOpt&0x1000));\r
720ee7f6 908 }\r
909\r
f71361b5 910 if (((EmuOpt_old ^ currentConfig.EmuOpt) & EOPT_VSYNC) || Pico.m.pal != pal_old) {\r
911 if ((currentConfig.EmuOpt & EOPT_VSYNC) || soc == SOCID_POLLUX)\r
fa5e045b 912 set_lcd_custom_rate(Pico.m.pal);\r
f71361b5 913 else if (EmuOpt_old & EOPT_VSYNC)\r
fa5e045b 914 unset_lcd_custom_rate();\r
59d0f042 915 }\r
916\r
902972d1 917 if (gp2x_dev_id == GP2X_DEV_CAANOO)\r
918 in_set_config_int(in_name_to_id("evdev:pollux-analog"), IN_CFG_ABS_DEAD_ZONE,\r
919 currentConfig.analog_deadzone);\r
920\r
fa5e045b 921 if ((EmuOpt_old ^ currentConfig.EmuOpt) & EOPT_MMUHACK)\r
922 gp2x_make_fb_bufferable(currentConfig.EmuOpt & EOPT_MMUHACK);\r
923\r
59d0f042 924 EmuOpt_old = currentConfig.EmuOpt;\r
b7911801 925 pal_old = Pico.m.pal;\r
720ee7f6 926\r
927 // make sure we are in correct mode\r
b6072c17 928 change_renderer(0);\r
929 vid_reset_mode();\r
720ee7f6 930\r
c66f49e6 931 // dirty buffers better go now than during gameplay\r
932 sync();\r
933 sleep(0);\r
934\r
93c18cb4 935 pemu_sound_start();\r
b5bfb864 936}\r
720ee7f6 937\r
b5bfb864 938void pemu_loop_end(void)\r
939{\r
f9dd0a63 940 pemu_sound_stop();\r
941\r
7bc9a680 942 /* do one more frame for menu bg */\r
902972d1 943 pemu_forced_frame(0, 1);\r
720ee7f6 944}\r
945\r
049a6b3e 946const char *plat_get_credits(void)\r
947{\r
0c9ae592 948 return "PicoDrive v" VERSION " (c) notaz, 2006-2010\n\n\n"\r
049a6b3e 949 "Credits:\n"\r
950 "fDave: Cyclone 68000 core,\n"\r
951 " base code of PicoDrive\n"\r
952 "Reesy & FluBBa: DrZ80 core\n"\r
953 "MAME devs: YM2612 and SN76496 cores\n"\r
954 "rlyeh and others: minimal SDK\n"\r
d572cbad 955 "Squidge: mmuhack\n"\r
049a6b3e 956 "Dzz: ARM940 sample\n"\r
957 "GnoStiC / Puck2099: USB joy code\n"\r
958 "craigix: GP2X hardware\n"\r
959 "ketchupgun: skin design\n"\r
960 "\n"\r
961 "special thanks (for docs, ideas):\n"\r
962 " Charles MacDonald, Haze,\n"\r
963 " Stephane Dallongeville,\n"\r
964 " Lordus, Exophase, Rokas,\n"\r
965 " Nemesis, Tasco Deluxe";\r
966}\r