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