pandora: fix readme and pxml version
[picodrive.git] / platform / common / emu.c
... / ...
CommitLineData
1/*\r
2 * PicoDrive\r
3 * (C) notaz, 2007-2010\r
4 * (C) irixxxx, 2019-2024\r
5 *\r
6 * This work is licensed under the terms of MAME license.\r
7 * See COPYING file in the top-level directory.\r
8 */\r
9\r
10#include <stdio.h>\r
11#include <stdlib.h>\r
12#include <string.h>\r
13#include <stdarg.h>\r
14#ifdef __GP2X__\r
15#include <unistd.h>\r
16#endif\r
17\r
18#include "../libpicofe/posix.h"\r
19#include "../libpicofe/input.h"\r
20#include "../libpicofe/fonts.h"\r
21#include "../libpicofe/sndout.h"\r
22#include "../libpicofe/lprintf.h"\r
23#include "../libpicofe/readpng.h"\r
24#include "../libpicofe/plat.h"\r
25#include "emu.h"\r
26#include "keyboard.h"\r
27#include "input_pico.h"\r
28#include "menu_pico.h"\r
29#include "config_file.h"\r
30\r
31#include <pico/pico_int.h>\r
32#include <pico/patch.h>\r
33\r
34#if defined(__GNUC__) && __GNUC__ >= 7\r
35#pragma GCC diagnostic ignored "-Wformat-truncation"\r
36#endif\r
37\r
38#ifndef _WIN32\r
39#define PATH_SEP "/"\r
40#define PATH_SEP_C '/'\r
41#else\r
42#define PATH_SEP "\\"\r
43#define PATH_SEP_C '\\'\r
44#endif\r
45\r
46#define STATUS_MSG_TIMEOUT 2000\r
47\r
48void *g_screen_ptr;\r
49\r
50int g_screen_width = 320;\r
51int g_screen_height = 240;\r
52int g_screen_ppitch = 320; // pitch in pixels\r
53\r
54const char *PicoConfigFile = "config2.cfg";\r
55currentConfig_t currentConfig, defaultConfig;\r
56int state_slot = 0;\r
57int config_slot = 0, config_slot_current = 0;\r
58int pico_pen_x = 320/2, pico_pen_y = 240/2;\r
59int pico_inp_mode;\r
60int flip_after_sync;\r
61int engineState = PGS_Menu;\r
62\r
63int grab_mode;\r
64int kbd_mode;\r
65struct vkbd *vkbd;\r
66int mouse_x, mouse_y;\r
67\r
68static int pico_page;\r
69static int pico_w, pico_h;\r
70static u16 *pico_overlay;\r
71static int pico_pad;\r
72\r
73static short __attribute__((aligned(4))) sndBuffer[2*54000/50];\r
74\r
75/* tmp buff to reduce stack usage for plats with small stack */\r
76static char static_buff[512];\r
77const char *rom_fname_reload;\r
78char rom_fname_loaded[512];\r
79int reset_timing = 0;\r
80static unsigned int notice_msg_time; /* when started showing */\r
81static char noticeMsg[40];\r
82\r
83unsigned char *movie_data = NULL;\r
84static int movie_size = 0;\r
85\r
86\r
87/* don't use tolower() for easy old glibc binary compatibility */\r
88static void strlwr_(char *string)\r
89{\r
90 char *p;\r
91 for (p = string; *p; p++)\r
92 if ('A' <= *p && *p <= 'Z')\r
93 *p += 'a' - 'A';\r
94}\r
95\r
96static int try_rfn_cut(char *fname)\r
97{\r
98 FILE *tmp;\r
99 char *p;\r
100\r
101 p = fname + strlen(fname) - 1;\r
102 for (; p > fname; p--)\r
103 if (*p == '.') break;\r
104 *p = 0;\r
105\r
106 if((tmp = fopen(fname, "rb"))) {\r
107 fclose(tmp);\r
108 return 1;\r
109 }\r
110 return 0;\r
111}\r
112\r
113static void get_ext(const char *file, char *ext)\r
114{\r
115 const char *p;\r
116\r
117 p = file + strlen(file) - 4;\r
118 if (p < file) p = file;\r
119 strncpy(ext, p, 4);\r
120 ext[4] = 0;\r
121 strlwr_(ext);\r
122}\r
123\r
124static void fname_ext(char *dst, int dstlen, const char *prefix, const char *ext, const char *fname)\r
125{\r
126 int prefix_len = 0;\r
127 const char *p;\r
128\r
129 *dst = 0;\r
130 if (prefix) {\r
131 int len = plat_get_root_dir(dst, dstlen);\r
132 strcpy(dst + len, prefix);\r
133 prefix_len = len + strlen(prefix);\r
134 }\r
135\r
136 p = fname + strlen(fname) - 1;\r
137 for (; p >= fname && *p != PATH_SEP_C; p--)\r
138 ;\r
139 p++;\r
140 strncpy(dst + prefix_len, p, dstlen - prefix_len - 1);\r
141\r
142 dst[dstlen - 8] = 0;\r
143 if ((p = strrchr(dst, '.')) != NULL)\r
144 dst[p-dst] = 0;\r
145 if (ext)\r
146 strcat(dst, ext);\r
147}\r
148\r
149static void romfname_ext(char *dst, int dstlen, const char *prefix, const char *ext)\r
150{\r
151 fname_ext(dst, dstlen, prefix, ext, rom_fname_loaded);\r
152}\r
153\r
154void emu_status_msg(const char *format, ...)\r
155{\r
156 va_list vl;\r
157 int ret;\r
158\r
159 va_start(vl, format);\r
160 ret = vsnprintf(noticeMsg, sizeof(noticeMsg), format, vl);\r
161 va_end(vl);\r
162\r
163 /* be sure old text gets overwritten */\r
164 for (; ret < 28; ret++)\r
165 noticeMsg[ret] = ' ';\r
166 noticeMsg[ret] = 0;\r
167\r
168 notice_msg_time = plat_get_ticks_ms();\r
169}\r
170\r
171static const char * const biosfiles_us[] = {\r
172 "us_scd2_9306", "SegaCDBIOS9303", "us_scd1_9210", "bios_CD_U"\r
173};\r
174static const char * const biosfiles_eu[] = {\r
175 "eu_mcd2_9306", "eu_mcd2_9303", "eu_mcd1_9210", "bios_CD_E"\r
176};\r
177static const char * const biosfiles_jp[] = {\r
178 "jp_mcd2_921222", "jp_mcd1_9112", "jp_mcd1_9111", "bios_CD_J"\r
179};\r
180\r
181static const char *find_bios(int *region, const char *cd_fname)\r
182{\r
183 int i, count;\r
184 const char * const *files;\r
185 FILE *f = NULL;\r
186 int ret;\r
187\r
188 // we need to have config loaded at this point\r
189 ret = emu_read_config(cd_fname, 0);\r
190 if (!ret) emu_read_config(NULL, 0);\r
191\r
192 if (PicoIn.regionOverride) {\r
193 *region = PicoIn.regionOverride;\r
194 lprintf("override region to %s\n", *region != 4 ?\r
195 (*region == 8 ? "EU" : "JAP") : "USA");\r
196 }\r
197\r
198 // locate BIOS file\r
199 if (*region == 4) { // US\r
200 files = biosfiles_us;\r
201 count = sizeof(biosfiles_us) / sizeof(char *);\r
202 } else if (*region == 8) { // EU\r
203 files = biosfiles_eu;\r
204 count = sizeof(biosfiles_eu) / sizeof(char *);\r
205 } else if (*region == 1 || *region == 2) {\r
206 files = biosfiles_jp;\r
207 count = sizeof(biosfiles_jp) / sizeof(char *);\r
208 } else {\r
209 return 0;\r
210 }\r
211\r
212 for (i = 0; i < count; i++)\r
213 {\r
214 emu_make_path(static_buff, files[i], sizeof(static_buff) - 4);\r
215 strcat(static_buff, ".bin");\r
216 f = fopen(static_buff, "rb");\r
217 if (f) break;\r
218\r
219 static_buff[strlen(static_buff) - 4] = 0;\r
220 strcat(static_buff, ".zip");\r
221 f = fopen(static_buff, "rb");\r
222 if (f) break;\r
223\r
224 strcpy(static_buff, files[i]);\r
225 strcat(static_buff, ".bin");\r
226 f = fopen(static_buff, "rb");\r
227 if (f) break;\r
228\r
229 static_buff[strlen(static_buff) - 4] = 0;\r
230 strcat(static_buff, ".zip");\r
231 f = fopen(static_buff, "rb");\r
232 if (f) break;\r
233 }\r
234\r
235 if (f) {\r
236 lprintf("using bios: %s\n", static_buff);\r
237 fclose(f);\r
238 return static_buff;\r
239 } else {\r
240 sprintf(static_buff, "no %s BIOS files found, read docs",\r
241 *region != 4 ? (*region == 8 ? "EU" : "JAP") : "USA");\r
242 menu_update_msg(static_buff);\r
243 return NULL;\r
244 }\r
245}\r
246\r
247static const char *find_msu(const char *cd_fname)\r
248{\r
249 int i;\r
250\r
251 // look for MSU.MD or MD+ rom file. XXX another extension list? ugh...\r
252 static const char *md_exts[] = { "gen", "smd", "md", "32x" };\r
253 char *ext = strrchr(cd_fname, '.');\r
254 int extpos = ext ? ext-cd_fname : strlen(cd_fname);\r
255 strcpy(static_buff, cd_fname);\r
256 static_buff[extpos++] = '.';\r
257 for (i = 0; i < ARRAY_SIZE(md_exts); i++) {\r
258 strcpy(static_buff+extpos, md_exts[i]);\r
259 if (access(static_buff, R_OK) == 0) {\r
260 printf("found MSU rom: %s\n",static_buff);\r
261 return static_buff;\r
262 }\r
263 }\r
264 return NULL;\r
265}\r
266\r
267/* check if the name begins with BIOS name */\r
268/*\r
269static int emu_isBios(const char *name)\r
270{\r
271 int i;\r
272 for (i = 0; i < sizeof(biosfiles_us)/sizeof(biosfiles_us[0]); i++)\r
273 if (strstr(name, biosfiles_us[i]) != NULL) return 1;\r
274 for (i = 0; i < sizeof(biosfiles_eu)/sizeof(biosfiles_eu[0]); i++)\r
275 if (strstr(name, biosfiles_eu[i]) != NULL) return 1;\r
276 for (i = 0; i < sizeof(biosfiles_jp)/sizeof(biosfiles_jp[0]); i++)\r
277 if (strstr(name, biosfiles_jp[i]) != NULL) return 1;\r
278 return 0;\r
279}\r
280*/\r
281\r
282static int extract_text(char *dest, const unsigned char *src, int len, int swab)\r
283{\r
284 char *p = dest;\r
285 int i;\r
286\r
287 if (swab) swab = 1;\r
288\r
289 for (i = len - 1; i >= 0; i--)\r
290 {\r
291 if (src[i^swab] != ' ') break;\r
292 }\r
293 len = i + 1;\r
294\r
295 for (i = 0; i < len; i++)\r
296 {\r
297 unsigned char s = src[i^swab];\r
298 if (s >= 0x20 && s < 0x7f && s != '#' && s != '|' &&\r
299 s != '[' && s != ']' && s != '\\')\r
300 {\r
301 *p++ = s;\r
302 }\r
303 else\r
304 {\r
305 sprintf(p, "\\%02x", s);\r
306 p += 3;\r
307 }\r
308 }\r
309\r
310 return p - dest;\r
311}\r
312\r
313static char *emu_make_rom_id(const char *fname)\r
314{\r
315 static char id_string[3+0xe*3+0x3*3+0x30*3+3];\r
316 int pos, swab = 1;\r
317\r
318 if (PicoIn.AHW & PAHW_MCD) {\r
319 strcpy(id_string, "CD|");\r
320 swab = 0;\r
321 }\r
322 else if (PicoIn.AHW & PAHW_SMS)\r
323 strcpy(id_string, "MS|");\r
324 else strcpy(id_string, "MD|");\r
325 pos = 3;\r
326\r
327 if (!(PicoIn.AHW & PAHW_SMS)) {\r
328 pos += extract_text(id_string + pos, media_id_header + 0x80, 0x0e, swab); // serial\r
329 id_string[pos] = '|'; pos++;\r
330 pos += extract_text(id_string + pos, media_id_header + 0xf0, 0x03, swab); // region\r
331 id_string[pos] = '|'; pos++;\r
332 pos += extract_text(id_string + pos, media_id_header + 0x50, 0x30, swab); // overseas name\r
333 id_string[pos] = 0;\r
334 if (pos > 5)\r
335 return id_string;\r
336 pos = 3;\r
337 }\r
338\r
339 // can't find name in ROM, use filename\r
340 fname_ext(id_string + 3, sizeof(id_string) - 3, NULL, NULL, fname);\r
341\r
342 return id_string;\r
343}\r
344\r
345// buffer must be at least 150 byte long\r
346void emu_get_game_name(char *str150)\r
347{\r
348 int ret, swab = (PicoIn.AHW & PAHW_MCD) ? 0 : 1;\r
349 char *s, *d;\r
350\r
351 ret = extract_text(str150, media_id_header + 0x50, 0x30, swab); // overseas name\r
352\r
353 for (s = d = str150 + 1; s < str150+ret; s++)\r
354 {\r
355 if (*s == 0) break;\r
356 if (*s != ' ' || d[-1] != ' ')\r
357 *d++ = *s;\r
358 }\r
359 *d = 0;\r
360}\r
361\r
362static void system_announce(void)\r
363{\r
364 const char *sys_name, *tv_standard, *extra = "";\r
365 int fps;\r
366\r
367 if (PicoIn.AHW & PAHW_SMS) {\r
368 sys_name = "Master System";\r
369 if (PicoIn.AHW & PAHW_GG)\r
370 sys_name = "Game Gear";\r
371 else if (PicoIn.AHW & PAHW_SG)\r
372 sys_name = "SG-1000";\r
373 else if (PicoIn.AHW & PAHW_SC)\r
374 sys_name = "SC-3000";\r
375 else if (Pico.m.hardware & PMS_HW_JAP)\r
376 sys_name = "Mark III";\r
377#ifdef NO_SMS\r
378 extra = " [no support]";\r
379#endif\r
380 } else if (PicoIn.AHW & PAHW_PICO) {\r
381 sys_name = "Pico";\r
382 } else if ((PicoIn.AHW & (PAHW_32X|PAHW_MCD)) == (PAHW_32X|PAHW_MCD)) {\r
383 sys_name = "32X + Mega CD";\r
384 if ((Pico.m.hardware & 0xc0) == 0x80)\r
385 sys_name = "32X + Sega CD";\r
386 } else if (PicoIn.AHW & PAHW_MCD) {\r
387 sys_name = "Mega CD";\r
388 if ((Pico.m.hardware & 0xc0) == 0x80)\r
389 sys_name = "Sega CD";\r
390 } else if (PicoIn.AHW & PAHW_32X) {\r
391 sys_name = "32X";\r
392 } else {\r
393 sys_name = "Mega Drive";\r
394 if ((Pico.m.hardware & 0xc0) == 0x80)\r
395 sys_name = "Genesis";\r
396 }\r
397 tv_standard = Pico.m.pal ? "PAL" : "NTSC";\r
398 fps = Pico.m.pal ? 50 : 60;\r
399\r
400 emu_status_msg("%s %s / %dFPS%s", tv_standard, sys_name, fps, extra);\r
401}\r
402\r
403static void do_region_override(const char *media_fname)\r
404{\r
405 // we only need to override region if config tells us so\r
406 int ret = emu_read_config(media_fname, 0);\r
407 if (!ret) emu_read_config(NULL, 0);\r
408}\r
409\r
410int emu_reload_rom(const char *rom_fname_in)\r
411{\r
412 // use setting before rom config is loaded\r
413 int autoload = g_autostateld_opt;\r
414 char *rom_fname = NULL;\r
415 char ext[5];\r
416 enum media_type_e media_type;\r
417 int menu_romload_started = 0;\r
418 char carthw_path[512];\r
419 int retval = 0;\r
420\r
421 lprintf("emu_ReloadRom(%s)\n", rom_fname_in);\r
422\r
423 rom_fname = strdup(rom_fname_in);\r
424 if (rom_fname == NULL)\r
425 return 0;\r
426\r
427 get_ext(rom_fname, ext);\r
428\r
429 // early cleanup\r
430 PicoPatchUnload();\r
431 if (movie_data) {\r
432 free(movie_data);\r
433 movie_data = 0;\r
434 }\r
435\r
436 if (!strcasecmp(ext, ".gmv"))\r
437 {\r
438 // check for both gmv and rom\r
439 int dummy;\r
440 FILE *movie_file = fopen(rom_fname, "rb");\r
441 if (!movie_file) {\r
442 menu_update_msg("Failed to open movie.");\r
443 goto out;\r
444 }\r
445 fseek(movie_file, 0, SEEK_END);\r
446 movie_size = ftell(movie_file);\r
447 fseek(movie_file, 0, SEEK_SET);\r
448 if (movie_size < 64+3) {\r
449 menu_update_msg("Invalid GMV file.");\r
450 fclose(movie_file);\r
451 goto out;\r
452 }\r
453 movie_data = malloc(movie_size);\r
454 if (movie_data == NULL) {\r
455 menu_update_msg("low memory.");\r
456 fclose(movie_file);\r
457 goto out;\r
458 }\r
459 dummy = fread(movie_data, 1, movie_size, movie_file);\r
460 fclose(movie_file);\r
461 if (strncmp((char *)movie_data, "Gens Movie TEST", 15) != 0) {\r
462 menu_update_msg("Invalid GMV file.");\r
463 goto out;\r
464 }\r
465 dummy = try_rfn_cut(rom_fname) || try_rfn_cut(rom_fname);\r
466 if (!dummy) {\r
467 menu_update_msg("Could't find a ROM for movie.");\r
468 goto out;\r
469 }\r
470 get_ext(rom_fname, ext);\r
471 lprintf("gmv loaded for %s\n", rom_fname);\r
472 }\r
473 else if (!strcasecmp(ext, ".pat"))\r
474 {\r
475 int dummy;\r
476 PicoPatchLoad(rom_fname);\r
477 dummy = try_rfn_cut(rom_fname) || try_rfn_cut(rom_fname);\r
478 if (!dummy) {\r
479 menu_update_msg("Could't find a ROM to patch.");\r
480 goto out;\r
481 }\r
482 get_ext(rom_fname, ext);\r
483 }\r
484\r
485 menu_romload_prepare(rom_fname); // also CD load\r
486 menu_romload_started = 1;\r
487\r
488 emu_make_path(carthw_path, "carthw.cfg", sizeof(carthw_path));\r
489\r
490 media_type = PicoLoadMedia(rom_fname, NULL, 0, carthw_path,\r
491 find_bios, find_msu, do_region_override);\r
492\r
493 switch (media_type) {\r
494 case PM_BAD_DETECT:\r
495 menu_update_msg("Not a ROM/CD img selected.");\r
496 goto out;\r
497 case PM_BAD_CD:\r
498 menu_update_msg("Invalid CD image");\r
499 goto out;\r
500 case PM_BAD_CD_NO_BIOS:\r
501 // find_bios() prints a message\r
502 goto out;\r
503 case PM_ERROR:\r
504 menu_update_msg("Load error");\r
505 goto out;\r
506 default:\r
507 break;\r
508 }\r
509\r
510 // make quirks visible in UI\r
511 if (PicoIn.quirks & PQUIRK_FORCE_6BTN)\r
512 currentConfig.input_dev0 = PICO_INPUT_PAD_6BTN;\r
513\r
514 menu_romload_end();\r
515 menu_romload_started = 0;\r
516\r
517 if (PicoPatches) {\r
518 PicoPatchPrepare();\r
519 PicoPatchApply();\r
520 }\r
521\r
522 // additional movie stuff\r
523 if (movie_data)\r
524 {\r
525 enum input_device indev = (movie_data[0x14] == '6') ?\r
526 PICO_INPUT_PAD_6BTN : PICO_INPUT_PAD_3BTN;\r
527 PicoSetInputDevice(0, indev);\r
528 PicoSetInputDevice(1, indev);\r
529\r
530 PicoIn.opt |= POPT_DIS_VDP_FIFO; // no VDP fifo timing\r
531 if (movie_data[0xF] >= 'A') {\r
532 if (movie_data[0x16] & 0x80) {\r
533 PicoIn.regionOverride = 8;\r
534 } else {\r
535 PicoIn.regionOverride = 4;\r
536 }\r
537 PicoReset();\r
538 // TODO: bits 6 & 5\r
539 }\r
540 movie_data[0x18+30] = 0;\r
541 emu_status_msg("MOVIE: %s", (char *) &movie_data[0x18]);\r
542 }\r
543 else\r
544 {\r
545 PicoSetInputDevice(0, currentConfig.input_dev0);\r
546 PicoSetInputDevice(1, currentConfig.input_dev1);\r
547\r
548 system_announce();\r
549 PicoIn.opt &= ~POPT_DIS_VDP_FIFO;\r
550 }\r
551\r
552 strncpy(rom_fname_loaded, rom_fname, sizeof(rom_fname_loaded)-1);\r
553 rom_fname_loaded[sizeof(rom_fname_loaded)-1] = 0;\r
554\r
555 // load SRAM for this ROM\r
556 if (currentConfig.EmuOpt & EOPT_EN_SRAM)\r
557 emu_save_load_game(1, 1);\r
558\r
559 // state autoload?\r
560 if (autoload) {\r
561 int time, newest = 0, newest_slot = -1;\r
562 int slot;\r
563\r
564 for (slot = 0; slot < 10; slot++) {\r
565 if (emu_check_save_file(slot, &time)) {\r
566 if (time > newest) {\r
567 newest = time;\r
568 newest_slot = slot;\r
569 }\r
570 }\r
571 }\r
572\r
573 if (newest_slot >= 0) {\r
574 lprintf("autoload slot %d\n", newest_slot);\r
575 state_slot = newest_slot;\r
576 emu_save_load_game(1, 0);\r
577 }\r
578 else {\r
579 lprintf("no save to autoload.\n");\r
580 }\r
581 }\r
582\r
583 retval = 1;\r
584out:\r
585 if (menu_romload_started)\r
586 menu_romload_end();\r
587 free(rom_fname);\r
588 return retval;\r
589}\r
590\r
591int emu_swap_cd(const char *fname)\r
592{\r
593 enum cd_track_type cd_type;\r
594 int ret = -1;\r
595\r
596 cd_type = PicoCdCheck(fname, NULL);\r
597 if (cd_type != CT_UNKNOWN)\r
598 ret = cdd_load(fname, cd_type);\r
599 if (ret != 0) {\r
600 menu_update_msg("Load failed, invalid CD image?");\r
601 return 0;\r
602 }\r
603\r
604 strncpy(rom_fname_loaded, fname, sizeof(rom_fname_loaded)-1);\r
605 rom_fname_loaded[sizeof(rom_fname_loaded) - 1] = 0;\r
606\r
607 return 1;\r
608}\r
609\r
610int emu_play_tape(const char *fname)\r
611{\r
612 int ret;\r
613\r
614 ret = PicoPlayTape(fname);\r
615 if (ret != 0) {\r
616 menu_update_msg("loading tape failed");\r
617 return 0;\r
618 }\r
619 return 1;\r
620}\r
621\r
622int emu_record_tape(const char *ext)\r
623{\r
624 int ret;\r
625\r
626 fname_ext(static_buff, sizeof(static_buff), "tape"PATH_SEP, ext, rom_fname_loaded);\r
627 ret = PicoRecordTape(static_buff);\r
628 if (ret != 0) {\r
629 menu_update_msg("recording tape failed");\r
630 return 0;\r
631 }\r
632 return 1;\r
633}\r
634\r
635// <base dir><end>\r
636void emu_make_path(char *buff, const char *end, int size)\r
637{\r
638 int pos, end_len;\r
639\r
640 end_len = strlen(end);\r
641 pos = plat_get_root_dir(buff, size);\r
642 strncpy(buff + pos, end, size - pos);\r
643 buff[size - 1] = 0;\r
644 if (pos + end_len > size - 1)\r
645 lprintf("Warning: path truncated: %s\n", buff);\r
646}\r
647\r
648static void make_config_cfg(char *cfg_buff_512)\r
649{\r
650 emu_make_path(cfg_buff_512, PicoConfigFile, 512-6);\r
651 if (config_slot != 0)\r
652 {\r
653 char *p = strrchr(cfg_buff_512, '.');\r
654 if (p == NULL)\r
655 p = cfg_buff_512 + strlen(cfg_buff_512);\r
656 sprintf(p, ".%i.cfg", config_slot);\r
657 }\r
658 cfg_buff_512[511] = 0;\r
659}\r
660\r
661void emu_prep_defconfig(void)\r
662{\r
663 memset(&defaultConfig, 0, sizeof(defaultConfig));\r
664 defaultConfig.EmuOpt = EOPT_EN_SRAM | EOPT_EN_SOUND | EOPT_16BPP |\r
665 EOPT_EN_CD_LEDS | EOPT_GZIP_SAVES | EOPT_PICO_PEN;\r
666 defaultConfig.s_PicoOpt = POPT_EN_SNDFILTER|POPT_EN_GG_LCD|POPT_EN_YM2413 |\r
667 POPT_EN_STEREO|POPT_EN_FM|POPT_EN_PSG|POPT_EN_Z80 |\r
668 POPT_EN_MCD_PCM|POPT_EN_MCD_CDDA|POPT_EN_MCD_GFX |\r
669 POPT_EN_DRC|POPT_ACC_SPRITES |\r
670 POPT_EN_32X|POPT_EN_PWM;\r
671 defaultConfig.s_PsndRate = 44100;\r
672 defaultConfig.s_PicoRegion = 0; // auto\r
673 defaultConfig.s_PicoAutoRgnOrder = 0x184; // US, EU, JP\r
674 defaultConfig.s_hwSelect = PHWS_AUTO;\r
675 defaultConfig.s_PicoCDBuffers = 0;\r
676 defaultConfig.s_PicoSndFilterAlpha = 0x10000 * 60 / 100;\r
677 defaultConfig.confirm_save = EOPT_CONFIRM_SAVE;\r
678 defaultConfig.Frameskip = -1; // auto\r
679 defaultConfig.input_dev0 = PICO_INPUT_PAD_3BTN;\r
680 defaultConfig.input_dev1 = PICO_INPUT_PAD_3BTN;\r
681 defaultConfig.volume = 50;\r
682 defaultConfig.gamma = 100;\r
683 defaultConfig.scaling = 0;\r
684 defaultConfig.turbo_rate = 15;\r
685 defaultConfig.msh2_khz = PICO_MSH2_HZ / 1000;\r
686 defaultConfig.ssh2_khz = PICO_SSH2_HZ / 1000;\r
687 defaultConfig.max_skip = 4;\r
688\r
689 // platform specific overrides\r
690 pemu_prep_defconfig();\r
691}\r
692\r
693void emu_set_defconfig(void)\r
694{\r
695 memcpy(&currentConfig, &defaultConfig, sizeof(currentConfig));\r
696 PicoIn.opt = currentConfig.s_PicoOpt;\r
697 PicoIn.sndRate = currentConfig.s_PsndRate;\r
698 PicoIn.regionOverride = currentConfig.s_PicoRegion;\r
699 PicoIn.autoRgnOrder = currentConfig.s_PicoAutoRgnOrder;\r
700 PicoIn.hwSelect = currentConfig.s_hwSelect;\r
701 PicoIn.sndFilterAlpha = currentConfig.s_PicoSndFilterAlpha;\r
702}\r
703\r
704int emu_read_config(const char *rom_fname, int no_defaults)\r
705{\r
706 char cfg[512];\r
707 int ret;\r
708\r
709 if (!no_defaults)\r
710 emu_set_defconfig();\r
711\r
712 if (rom_fname == NULL)\r
713 {\r
714 // global config\r
715 make_config_cfg(cfg);\r
716 ret = config_readsect(cfg, NULL);\r
717 }\r
718 else\r
719 {\r
720 char ext[16];\r
721 int vol;\r
722\r
723 if (config_slot != 0)\r
724 snprintf(ext, sizeof(ext), ".%i.cfg", config_slot);\r
725 else\r
726 strcpy(ext, ".cfg");\r
727\r
728 fname_ext(cfg, sizeof(cfg), "cfg"PATH_SEP, ext, rom_fname);\r
729\r
730 // read user's config\r
731 vol = currentConfig.volume;\r
732 ret = config_readsect(cfg, NULL);\r
733 currentConfig.volume = vol; // make vol global (bah)\r
734\r
735 if (ret != 0)\r
736 {\r
737 // read global config, and apply game_def.cfg on top\r
738 make_config_cfg(cfg);\r
739 config_readsect(cfg, NULL);\r
740\r
741 emu_make_path(cfg, "game_def.cfg", sizeof(cfg));\r
742 ret = config_readsect(cfg, emu_make_rom_id(rom_fname));\r
743 }\r
744 }\r
745\r
746 pemu_validate_config();\r
747 PicoIn.overclockM68k = currentConfig.overclock_68k;\r
748\r
749 // some sanity checks\r
750 if (currentConfig.volume < 0 || currentConfig.volume > 99)\r
751 currentConfig.volume = 50;\r
752\r
753 if (ret == 0)\r
754 config_slot_current = config_slot;\r
755\r
756 return (ret == 0);\r
757}\r
758\r
759\r
760int emu_write_config(int is_game)\r
761{\r
762 char cfg[512];\r
763 int ret, write_lrom = 0;\r
764\r
765 if (!is_game)\r
766 {\r
767 make_config_cfg(cfg);\r
768 write_lrom = 1;\r
769 } else {\r
770 char ext[16];\r
771\r
772 if (config_slot != 0)\r
773 snprintf(ext, sizeof(ext), ".%i.cfg", config_slot);\r
774 else\r
775 strcpy(ext, ".cfg");\r
776\r
777 romfname_ext(cfg, sizeof(cfg), "cfg"PATH_SEP, ext);\r
778 }\r
779\r
780 lprintf("emu_write_config: %s ", cfg);\r
781 ret = config_write(cfg);\r
782 if (write_lrom) config_writelrom(cfg);\r
783#ifdef __GP2X__\r
784 sync();\r
785#endif\r
786 lprintf((ret == 0) ? "(ok)\n" : "(failed)\n");\r
787\r
788 if (ret == 0) config_slot_current = config_slot;\r
789 return ret == 0;\r
790}\r
791\r
792\r
793/* always using built-in font */\r
794\r
795#define mk_text_out(name, type, val, topleft, step_x, step_y) \\r
796void name(int x, int y, const char *text) \\r
797{ \\r
798 int i, l, len = strlen(text); \\r
799 type *screen = (type *)(topleft) + x * step_x + y * step_y; \\r
800 \\r
801 for (i = 0; i < len; i++, screen += 8 * step_x) \\r
802 { \\r
803 for (l = 0; l < 8; l++) \\r
804 { \\r
805 unsigned char fd = fontdata8x8[text[i] * 8 + l];\\r
806 type *s = screen + l * step_y; \\r
807 if (fd&0x80) s[step_x * 0] = val; \\r
808 if (fd&0x40) s[step_x * 1] = val; \\r
809 if (fd&0x20) s[step_x * 2] = val; \\r
810 if (fd&0x10) s[step_x * 3] = val; \\r
811 if (fd&0x08) s[step_x * 4] = val; \\r
812 if (fd&0x04) s[step_x * 5] = val; \\r
813 if (fd&0x02) s[step_x * 6] = val; \\r
814 if (fd&0x01) s[step_x * 7] = val; \\r
815 } \\r
816 } \\r
817}\r
818\r
819mk_text_out(emu_text_out8, unsigned char, 0xf0, g_screen_ptr, 1, g_screen_ppitch)\r
820mk_text_out(emu_text_out16, unsigned short, 0xffff, g_screen_ptr, 1, g_screen_ppitch)\r
821mk_text_out(emu_text_out8_rot, unsigned char, 0xf0,\r
822 (char *)g_screen_ptr + (g_screen_ppitch - 1) * g_screen_height, -g_screen_height, 1)\r
823mk_text_out(emu_text_out16_rot, unsigned short, 0xffff,\r
824 (short *)g_screen_ptr + (g_screen_ppitch - 1) * g_screen_height, -g_screen_height, 1)\r
825\r
826#undef mk_text_out\r
827\r
828void emu_osd_text16(int x, int y, const char *text)\r
829{\r
830 int len = strlen(text) * 8;\r
831 int i, h;\r
832\r
833 len++;\r
834 if (x + len > g_screen_width)\r
835 len = g_screen_width - x;\r
836\r
837 for (h = 0; h < 8; h++) {\r
838 unsigned short *p;\r
839 p = (unsigned short *)g_screen_ptr\r
840 + x + g_screen_ppitch * (y + h);\r
841 for (i = len; i > 0; i--, p++)\r
842 *p = (*p >> 2) & 0x39e7;\r
843 }\r
844 emu_text_out16(x, y, text);\r
845}\r
846\r
847static void update_movie(void)\r
848{\r
849 int offs = Pico.m.frame_count*3 + 0x40;\r
850 if (offs+3 > movie_size) {\r
851 free(movie_data);\r
852 movie_data = 0;\r
853 emu_status_msg("END OF MOVIE.");\r
854 lprintf("END OF MOVIE.\n");\r
855 } else {\r
856 // MXYZ SACB RLDU\r
857 PicoIn.pad[0] = ~movie_data[offs] & 0x8f; // ! SCBA RLDU\r
858 if(!(movie_data[offs] & 0x10)) PicoIn.pad[0] |= 0x40; // C\r
859 if(!(movie_data[offs] & 0x20)) PicoIn.pad[0] |= 0x10; // A\r
860 if(!(movie_data[offs] & 0x40)) PicoIn.pad[0] |= 0x20; // B\r
861 PicoIn.pad[1] = ~movie_data[offs+1] & 0x8f; // ! SCBA RLDU\r
862 if(!(movie_data[offs+1] & 0x10)) PicoIn.pad[1] |= 0x40; // C\r
863 if(!(movie_data[offs+1] & 0x20)) PicoIn.pad[1] |= 0x10; // A\r
864 if(!(movie_data[offs+1] & 0x40)) PicoIn.pad[1] |= 0x20; // B\r
865 PicoIn.pad[0] |= (~movie_data[offs+2] & 0x0A) << 8; // ! MZYX\r
866 if(!(movie_data[offs+2] & 0x01)) PicoIn.pad[0] |= 0x0400; // X\r
867 if(!(movie_data[offs+2] & 0x04)) PicoIn.pad[0] |= 0x0100; // Z\r
868 PicoIn.pad[1] |= (~movie_data[offs+2] & 0xA0) << 4; // ! MZYX\r
869 if(!(movie_data[offs+2] & 0x10)) PicoIn.pad[1] |= 0x0400; // X\r
870 if(!(movie_data[offs+2] & 0x40)) PicoIn.pad[1] |= 0x0100; // Z\r
871 }\r
872}\r
873\r
874static int try_ropen_file(const char *fname, int *time)\r
875{\r
876 struct stat st;\r
877 FILE *f;\r
878\r
879 f = fopen(fname, "rb");\r
880 if (f) {\r
881 if (time != NULL) {\r
882 *time = 0;\r
883 if (fstat(fileno(f), &st) == 0)\r
884 *time = (int)st.st_mtime;\r
885 }\r
886 fclose(f);\r
887 return 1;\r
888 }\r
889 return 0;\r
890}\r
891\r
892char *emu_get_save_fname(int load, int is_sram, int slot, int *time)\r
893{\r
894 char *saveFname = static_buff;\r
895 char ext[16];\r
896\r
897 if (is_sram)\r
898 {\r
899 strcpy(ext, (PicoIn.AHW & PAHW_MCD) && Pico.romsize == 0 ? ".brm" : ".srm");\r
900 romfname_ext(saveFname, sizeof(static_buff),\r
901 (PicoIn.AHW & PAHW_MCD) && Pico.romsize == 0 ? "brm"PATH_SEP : "srm"PATH_SEP, ext);\r
902 if (!load)\r
903 return saveFname;\r
904\r
905 if (try_ropen_file(saveFname, time))\r
906 return saveFname;\r
907\r
908 romfname_ext(saveFname, sizeof(static_buff), NULL, ext);\r
909 if (try_ropen_file(saveFname, time))\r
910 return saveFname;\r
911 }\r
912 else\r
913 {\r
914 const char *ext_main = (currentConfig.EmuOpt & EOPT_GZIP_SAVES) ? ".mds.gz" : ".mds";\r
915 const char *ext_othr = (currentConfig.EmuOpt & EOPT_GZIP_SAVES) ? ".mds" : ".mds.gz";\r
916 ext[0] = 0;\r
917 if (slot > 0 && slot < 10)\r
918 sprintf(ext, ".%i", slot);\r
919 strcat(ext, ext_main);\r
920\r
921 if (!load) {\r
922 romfname_ext(saveFname, sizeof(static_buff), "mds" PATH_SEP, ext);\r
923 return saveFname;\r
924 }\r
925 else {\r
926 romfname_ext(saveFname, sizeof(static_buff), "mds" PATH_SEP, ext);\r
927 if (try_ropen_file(saveFname, time))\r
928 return saveFname;\r
929\r
930 romfname_ext(saveFname, sizeof(static_buff), NULL, ext);\r
931 if (try_ropen_file(saveFname, time))\r
932 return saveFname;\r
933\r
934 // try the other ext\r
935 ext[0] = 0;\r
936 if (slot > 0 && slot < 10)\r
937 sprintf(ext, ".%i", slot);\r
938 strcat(ext, ext_othr);\r
939\r
940 romfname_ext(saveFname, sizeof(static_buff), "mds"PATH_SEP, ext);\r
941 if (try_ropen_file(saveFname, time))\r
942 return saveFname;\r
943 }\r
944 }\r
945\r
946 return NULL;\r
947}\r
948\r
949int emu_check_save_file(int slot, int *time)\r
950{\r
951 return emu_get_save_fname(1, 0, slot, time) ? 1 : 0;\r
952}\r
953\r
954int emu_save_load_game(int load, int sram)\r
955{\r
956 int ret = 0;\r
957 char *saveFname;\r
958\r
959 // make save filename\r
960 saveFname = emu_get_save_fname(load, sram, state_slot, NULL);\r
961 if (saveFname == NULL) {\r
962 if (!sram)\r
963 emu_status_msg(load ? "LOAD FAILED (missing file)" : "SAVE FAILED");\r
964 return -1;\r
965 }\r
966\r
967 lprintf("saveLoad (%i, %i): %s\n", load, sram, saveFname);\r
968\r
969 if (sram)\r
970 {\r
971 FILE *sramFile;\r
972 int sram_size;\r
973 unsigned char *sram_data;\r
974 int truncate = 1;\r
975 if ((PicoIn.AHW & PAHW_MCD) && Pico.romsize == 0)\r
976 {\r
977 if (PicoIn.opt & POPT_EN_MCD_RAMCART) {\r
978 sram_size = 0x12000;\r
979 sram_data = Pico.sv.data;\r
980 if (sram_data)\r
981 memcpy(sram_data, Pico_mcd->bram, 0x2000);\r
982 } else {\r
983 sram_size = 0x2000;\r
984 sram_data = Pico_mcd->bram;\r
985 truncate = 0; // the .brm may contain RAM cart data after normal brm\r
986 }\r
987 } else {\r
988 sram_size = Pico.sv.size;\r
989 sram_data = Pico.sv.data;\r
990 }\r
991 if (sram_data == NULL)\r
992 return 0; // cart saves forcefully disabled for this game\r
993\r
994 if (load)\r
995 {\r
996 sramFile = fopen(saveFname, "rb");\r
997 if (!sramFile)\r
998 return -1;\r
999 ret = fread(sram_data, 1, sram_size, sramFile);\r
1000 ret = ret > 0 ? 0 : -1;\r
1001 fclose(sramFile);\r
1002 if ((PicoIn.AHW & PAHW_MCD) && Pico.romsize == 0 && (PicoIn.opt&POPT_EN_MCD_RAMCART))\r
1003 memcpy(Pico_mcd->bram, sram_data, 0x2000);\r
1004 } else {\r
1005 // sram save needs some special processing\r
1006 // see if we have anything to save\r
1007 for (; sram_size > 0; sram_size--)\r
1008 if (sram_data[sram_size-1]) break;\r
1009\r
1010 if (sram_size) {\r
1011 sramFile = fopen(saveFname, truncate ? "wb" : "r+b");\r
1012 if (!sramFile) sramFile = fopen(saveFname, "wb"); // retry\r
1013 if (!sramFile) return -1;\r
1014 ret = fwrite(sram_data, 1, sram_size, sramFile);\r
1015 ret = (ret != sram_size) ? -1 : 0;\r
1016 fclose(sramFile);\r
1017#ifdef __GP2X__\r
1018 sync();\r
1019#endif\r
1020 }\r
1021 }\r
1022 return ret;\r
1023 }\r
1024 else\r
1025 {\r
1026 ret = PicoState(saveFname, !load);\r
1027 if (!ret) {\r
1028#ifdef __GP2X__\r
1029 if (!load) sync();\r
1030#endif\r
1031 emu_status_msg(load ? "STATE LOADED" : "STATE SAVED");\r
1032 } else {\r
1033 emu_status_msg(load ? "LOAD FAILED" : "SAVE FAILED");\r
1034 ret = -1;\r
1035 }\r
1036\r
1037 return ret;\r
1038 }\r
1039}\r
1040\r
1041void emu_set_fastforward(int set_on)\r
1042{\r
1043 static void *set_PsndOut = NULL;\r
1044 static int set_Frameskip, set_EmuOpt, is_on = 0;\r
1045\r
1046 if (set_on && !is_on) {\r
1047 set_PsndOut = PicoIn.sndOut;\r
1048 set_Frameskip = currentConfig.Frameskip;\r
1049 set_EmuOpt = currentConfig.EmuOpt;\r
1050 PicoIn.sndOut = NULL;\r
1051 currentConfig.Frameskip = 8;\r
1052 currentConfig.EmuOpt &= ~EOPT_EN_SOUND;\r
1053 currentConfig.EmuOpt |= EOPT_NO_FRMLIMIT;\r
1054 is_on = 1;\r
1055 emu_status_msg("FAST FORWARD");\r
1056 }\r
1057 else if (!set_on && is_on) {\r
1058 PicoIn.sndOut = set_PsndOut;\r
1059 currentConfig.Frameskip = set_Frameskip;\r
1060 currentConfig.EmuOpt = set_EmuOpt;\r
1061 PsndRerate(1);\r
1062 is_on = 0;\r
1063 }\r
1064}\r
1065\r
1066static void emu_tray_open(void)\r
1067{\r
1068 engineState = PGS_TrayMenu;\r
1069}\r
1070\r
1071static void emu_tray_close(void)\r
1072{\r
1073 emu_status_msg("CD tray closed.");\r
1074}\r
1075\r
1076void emu_32x_startup(void)\r
1077{\r
1078 plat_video_toggle_renderer(0, 0); // HACK\r
1079 system_announce();\r
1080}\r
1081\r
1082void emu_reset_game(void)\r
1083{\r
1084 PicoReset();\r
1085 reset_timing = 1;\r
1086}\r
1087\r
1088static u16 *load_pico_overlay(int page, int w, int h)\r
1089{\r
1090 static const char *pic_exts[] = { "png", "PNG" };\r
1091 char *ext, *fname = NULL;\r
1092 int extpos, i;\r
1093\r
1094 if (pico_page == page && pico_w == w && pico_h == h)\r
1095 return pico_overlay;\r
1096 pico_page = page;\r
1097 pico_w = w, pico_h = h;\r
1098\r
1099 ext = strrchr(rom_fname_loaded, '.');\r
1100 extpos = ext ? ext-rom_fname_loaded : strlen(rom_fname_loaded);\r
1101 strcpy(static_buff, rom_fname_loaded);\r
1102 static_buff[extpos++] = '_';\r
1103 if (page < 0) {\r
1104 static_buff[extpos++] = 'p';\r
1105 static_buff[extpos++] = 'a';\r
1106 static_buff[extpos++] = 'd';\r
1107 } else\r
1108 static_buff[extpos++] = '0'+PicoPicohw.page;\r
1109 static_buff[extpos++] = '.';\r
1110\r
1111 for (i = 0; i < ARRAY_SIZE(pic_exts); i++) {\r
1112 strcpy(static_buff+extpos, pic_exts[i]);\r
1113 if (access(static_buff, R_OK) == 0) {\r
1114 printf("found Pico file: %s\n", static_buff);\r
1115 fname = static_buff;\r
1116 break;\r
1117 }\r
1118 }\r
1119\r
1120 pico_overlay = realloc(pico_overlay, w*h*2);\r
1121 memset(pico_overlay, 0, w*h*2);\r
1122 if (!fname || !pico_overlay || readpng(pico_overlay, fname, READPNG_SCALE, w, h)) {\r
1123 if (pico_overlay)\r
1124 free(pico_overlay);\r
1125 pico_overlay = NULL;\r
1126 }\r
1127\r
1128 return pico_overlay;\r
1129}\r
1130\r
1131void emu_pico_overlay(u16 *pd, int w, int h, int pitch)\r
1132{\r
1133 u16 *overlay = NULL;\r
1134 int y, oh = h;\r
1135\r
1136 // get overlay\r
1137 if (pico_inp_mode == 1) {\r
1138 oh = (w/2 < h ? w/2 : h); // storyware has squished h\r
1139 overlay = load_pico_overlay(PicoPicohw.page, w, oh);\r
1140 } else if (pico_inp_mode == 2)\r
1141 overlay = load_pico_overlay(-1, w, oh);\r
1142\r
1143 // copy overlay onto buffer\r
1144 if (overlay) {\r
1145 for (y = 0; y < oh; y++)\r
1146 memcpy(pd + y*pitch, overlay + y*w, w*2);\r
1147 if (y < h)\r
1148 memset(pd + y*pitch, 0, w*2);\r
1149 }\r
1150}\r
1151\r
1152void run_events_pico(unsigned int events)\r
1153{\r
1154 // treat pad ports equal to support pad in one and mouse in the other\r
1155 PicoIn.pad[0] |= PicoIn.pad[1];\r
1156\r
1157 if (events & PEV_PICO_PPREV) {\r
1158 PicoPicohw.page--;\r
1159 if (PicoPicohw.page < 0)\r
1160 PicoPicohw.page = 0;\r
1161 emu_status_msg("Page %i", PicoPicohw.page);\r
1162 }\r
1163 if (events & PEV_PICO_PNEXT) {\r
1164 PicoPicohw.page++;\r
1165 if (PicoPicohw.page > 7)\r
1166 PicoPicohw.page = 7;\r
1167 if (PicoPicohw.page == 7) {\r
1168 // Used in games that require the Keyboard Pico peripheral\r
1169 emu_status_msg("Test Page");\r
1170 } else {\r
1171 emu_status_msg("Page %i", PicoPicohw.page);\r
1172 }\r
1173 }\r
1174 if (events & PEV_PICO_STORY) {\r
1175 if (pico_inp_mode == 1) {\r
1176 pico_inp_mode = 0;\r
1177 emu_status_msg("Input: D-Pad");\r
1178 } else {\r
1179 pico_inp_mode = 1;\r
1180 emu_status_msg("Input: Pen on Storyware");\r
1181 }\r
1182 }\r
1183 if (events & PEV_PICO_PAD) {\r
1184 if (pico_inp_mode == 2) {\r
1185 pico_inp_mode = 0;\r
1186 emu_status_msg("Input: D-Pad");\r
1187 } else {\r
1188 pico_inp_mode = 2;\r
1189 emu_status_msg("Input: Pen on Pad");\r
1190 }\r
1191 }\r
1192\r
1193 if ((currentConfig.EmuOpt & EOPT_PICO_PEN) &&\r
1194 (PicoIn.pad[0]&0x20) && pico_inp_mode && pico_overlay) {\r
1195 pico_inp_mode = 0;\r
1196 emu_status_msg("Input: D-Pad");\r
1197 }\r
1198\r
1199 PicoPicohw.kb.active = (PicoIn.opt & POPT_EN_KBD ? kbd_mode : 0);\r
1200\r
1201 if (pico_inp_mode == 0)\r
1202 return;\r
1203\r
1204 /* handle other input modes using the pen */\r
1205 if (PicoIn.opt & POPT_EN_MOUSE) {\r
1206 pico_pen_x = PicoIn.mouse[0];\r
1207 pico_pen_y = PicoIn.mouse[1];\r
1208 } else {\r
1209 if (PicoIn.pad[0] & 1) pico_pen_y--;\r
1210 if (PicoIn.pad[0] & 2) pico_pen_y++;\r
1211 if (PicoIn.pad[0] & 4) pico_pen_x--;\r
1212 if (PicoIn.pad[0] & 8) pico_pen_x++;\r
1213 PicoIn.pad[0] &= ~0x0f; // release UDLR\r
1214 }\r
1215\r
1216 if ((pico_pad ^ PicoIn.pad[0]) & PicoIn.pad[0] & (1<<GBTN_START)) {\r
1217 PicoPicohw.pen_pos[0] ^= 0x8000;\r
1218 PicoPicohw.pen_pos[1] ^= 0x8000;\r
1219 emu_status_msg("Pen %s", PicoPicohw.pen_pos[0] & 0x8000 ? "Up" : "Down");\r
1220 }\r
1221 pico_pad = PicoIn.pad[0];\r
1222\r
1223 /* cursor position, cursor drawing must not cross screen borders */\r
1224 if (pico_pen_y < PICO_PEN_ADJUST_Y)\r
1225 pico_pen_y = PICO_PEN_ADJUST_Y;\r
1226 if (pico_pen_y > 223-1 - PICO_PEN_ADJUST_Y)\r
1227 pico_pen_y = 223-1 - PICO_PEN_ADJUST_Y;\r
1228 if (pico_pen_x < PICO_PEN_ADJUST_X)\r
1229 pico_pen_x = PICO_PEN_ADJUST_X;\r
1230 if (pico_pen_x > 319-1 - PICO_PEN_ADJUST_X)\r
1231 pico_pen_x = 319-1 - PICO_PEN_ADJUST_X;\r
1232\r
1233 PicoPicohw.pen_pos[0] &= 0x8000;\r
1234 PicoPicohw.pen_pos[1] &= 0x8000;\r
1235 PicoPicohw.pen_pos[0] |= 0x03c + pico_pen_x;\r
1236 PicoPicohw.pen_pos[1] |= (pico_inp_mode == 1 ? 0x2f8 : 0x1fc) + pico_pen_y;\r
1237}\r
1238\r
1239static void do_turbo(unsigned short *pad, int acts)\r
1240{\r
1241 static int turbo_pad = 0;\r
1242 static unsigned char turbo_cnt[3] = { 0, 0, 0 };\r
1243 int inc = currentConfig.turbo_rate * 2;\r
1244\r
1245 if (acts & 0x1000) {\r
1246 turbo_cnt[0] += inc;\r
1247 if (turbo_cnt[0] >= 60)\r
1248 turbo_pad ^= 0x10, turbo_cnt[0] = 0;\r
1249 }\r
1250 if (acts & 0x2000) {\r
1251 turbo_cnt[1] += inc;\r
1252 if (turbo_cnt[1] >= 60)\r
1253 turbo_pad ^= 0x20, turbo_cnt[1] = 0;\r
1254 }\r
1255 if (acts & 0x4000) {\r
1256 turbo_cnt[2] += inc;\r
1257 if (turbo_cnt[2] >= 60)\r
1258 turbo_pad ^= 0x40, turbo_cnt[2] = 0;\r
1259 }\r
1260 *pad |= turbo_pad & (acts >> 8);\r
1261}\r
1262\r
1263static void run_events_ui(unsigned int which)\r
1264{\r
1265 if (which & (PEV_STATE_LOAD|PEV_STATE_SAVE))\r
1266 {\r
1267 int do_it = 1;\r
1268 if ( emu_check_save_file(state_slot, NULL) &&\r
1269 (((which & PEV_STATE_LOAD) && (currentConfig.confirm_save & EOPT_CONFIRM_LOAD)) ||\r
1270 ((which & PEV_STATE_SAVE) && (currentConfig.confirm_save & EOPT_CONFIRM_SAVE))) )\r
1271 {\r
1272 const char *nm;\r
1273 char tmp[64];\r
1274 int keys, len;\r
1275\r
1276 strcpy(tmp, (which & PEV_STATE_LOAD) ? "LOAD STATE? " : "OVERWRITE SAVE? ");\r
1277 len = strlen(tmp);\r
1278 nm = in_get_key_name(-1, -PBTN_MOK);\r
1279 snprintf(tmp + len, sizeof(tmp) - len, "(%s=yes, ", nm);\r
1280 len = strlen(tmp);\r
1281 nm = in_get_key_name(-1, -PBTN_MBACK);\r
1282 snprintf(tmp + len, sizeof(tmp) - len, "%s=no)", nm);\r
1283\r
1284 plat_status_msg_busy_first(tmp);\r
1285\r
1286 in_set_config_int(0, IN_CFG_BLOCKING, 1);\r
1287 while (in_menu_wait_any(NULL, 50) & (PBTN_MOK | PBTN_MBACK))\r
1288 ;\r
1289 while ( !((keys = in_menu_wait_any(NULL, 50)) & (PBTN_MOK | PBTN_MBACK)))\r
1290 ;\r
1291 if (keys & PBTN_MBACK)\r
1292 do_it = 0;\r
1293 while (in_menu_wait_any(NULL, 50) & (PBTN_MOK | PBTN_MBACK))\r
1294 ;\r
1295 in_set_config_int(0, IN_CFG_BLOCKING, 0);\r
1296 plat_status_msg_clear();\r
1297 }\r
1298 if (do_it) {\r
1299 plat_status_msg_busy_first((which & PEV_STATE_LOAD) ? "LOADING STATE" : "SAVING STATE");\r
1300 PicoStateProgressCB = plat_status_msg_busy_next;\r
1301 emu_save_load_game((which & PEV_STATE_LOAD) ? 1 : 0, 0);\r
1302 PicoStateProgressCB = NULL;\r
1303 }\r
1304 plat_status_msg_busy_done();\r
1305 }\r
1306 if (which & (PEV_SSLOT_PREV|PEV_SSLOT_NEXT))\r
1307 {\r
1308 if (which & PEV_SSLOT_PREV) {\r
1309 state_slot -= 1;\r
1310 if (state_slot < 0)\r
1311 state_slot = 9;\r
1312 } else {\r
1313 state_slot += 1;\r
1314 if (state_slot > 9)\r
1315 state_slot = 0;\r
1316 }\r
1317\r
1318 emu_status_msg("SAVE SLOT %i [%s]", state_slot,\r
1319 emu_check_save_file(state_slot, NULL) ? "USED" : "FREE");\r
1320 }\r
1321 if (which & PEV_SWITCH_RND)\r
1322 {\r
1323 plat_video_toggle_renderer(1, 0);\r
1324 }\r
1325 if (which & PEV_GRAB_INPUT)\r
1326 {\r
1327 if (PicoIn.opt & POPT_EN_MOUSE) {\r
1328 grab_mode = !grab_mode;\r
1329 in_update_analog(0, 2, &mouse_x);\r
1330 in_update_analog(0, 3, &mouse_y);\r
1331 in_update_analog(0, 0, &mouse_x);\r
1332 in_update_analog(0, 1, &mouse_y);\r
1333 emu_status_msg("Mouse capture %s", grab_mode ? "on" : "off");\r
1334 } else {\r
1335 grab_mode = 0;\r
1336 emu_status_msg("No mouse configured");\r
1337 }\r
1338\r
1339 plat_grab_cursor(grab_mode);\r
1340 }\r
1341 if (which & PEV_SWITCH_KBD)\r
1342 {\r
1343 if (! (PicoIn.opt & POPT_EN_KBD)) {\r
1344 kbd_mode = 0;\r
1345 emu_status_msg("No keyboard configured");\r
1346 } else {\r
1347 kbd_mode = !kbd_mode;\r
1348 emu_status_msg("Keyboard %s", kbd_mode ? "on" : "off");\r
1349 }\r
1350 if (! kbd_mode)\r
1351 plat_video_clear_buffers();\r
1352 }\r
1353 if (which & PEV_RESET)\r
1354 emu_reset_game();\r
1355 if (which & PEV_MENU)\r
1356 engineState = PGS_Menu;\r
1357}\r
1358\r
1359void emu_update_input(void)\r
1360{\r
1361 static int prev_events = 0;\r
1362 int actions[IN_BINDTYPE_COUNT] = { 0, };\r
1363 int actions_kbd[IN_BIND_LAST] = { 0, };\r
1364 int pl_actions[4];\r
1365 int count_kbd = 0, buttons = 0;\r
1366 int events, i = 0;\r
1367\r
1368 in_update(actions);\r
1369\r
1370 pl_actions[0] = actions[IN_BINDTYPE_PLAYER12];\r
1371 pl_actions[1] = actions[IN_BINDTYPE_PLAYER12] >> 16;\r
1372 pl_actions[2] = actions[IN_BINDTYPE_PLAYER34];\r
1373 pl_actions[3] = actions[IN_BINDTYPE_PLAYER34] >> 16;\r
1374\r
1375 events = actions[IN_BINDTYPE_EMU] & PEV_MASK;\r
1376\r
1377 // update mouse coordinates if there is an emulated mouse\r
1378 if (PicoIn.opt & POPT_EN_MOUSE) {\r
1379 if (!grab_mode) {\r
1380 in_update_analog(0, 0, &PicoIn.mouse[0]);\r
1381 in_update_analog(0, 1, &PicoIn.mouse[1]);\r
1382 // scale mouse coordinates from -1024..1024 to 0..screen_w/h\r
1383 PicoIn.mouse[0] = (PicoIn.mouse[0]+1024) * g_screen_width /2048;\r
1384 PicoIn.mouse[1] = (PicoIn.mouse[1]+1024) * g_screen_height/2048;\r
1385 } else {\r
1386 int xrel, yrel;\r
1387 in_update_analog(0, 2, &xrel);\r
1388 in_update_analog(0, 3, &yrel);\r
1389 mouse_x += xrel, mouse_y += yrel;\r
1390 // scale mouse coordinates from -1024..1024 to 0..screen_w/h\r
1391 PicoIn.mouse[0] = (mouse_x+1024) * g_screen_width /2048;\r
1392 PicoIn.mouse[1] = (mouse_y+1024) * g_screen_height/2048;\r
1393 }\r
1394\r
1395 in_update_analog(0, -1, &i); // get mouse buttons, bit 2-0 = RML\r
1396 if (PicoIn.AHW & PAHW_PICO) {\r
1397 // TODO is maintaining 2 different mappings necessary?\r
1398 if (i & 1) buttons |= 1<<GBTN_C; // pen button\r
1399 if (i & 2) buttons |= 1<<GBTN_B; // red button\r
1400 if (i & 4) buttons |= 1<<GBTN_START; // pen up/down\r
1401 } else {\r
1402 if (i & 1) buttons |= 1<<GBTN_B; // as Sega Mouse\r
1403 if (i & 2) buttons |= 1<<GBTN_START;\r
1404 if (i & 4) buttons |= 1<<GBTN_C;\r
1405 }\r
1406\r
1407 if (currentConfig.input_dev0 == PICO_INPUT_MOUSE)\r
1408 pl_actions[0] |= buttons;\r
1409 if (currentConfig.input_dev1 == PICO_INPUT_MOUSE)\r
1410 pl_actions[1] |= buttons;\r
1411 }\r
1412\r
1413 if (kbd_mode) {\r
1414 int mask = (PicoIn.AHW & PAHW_PICO ? 0xf : 0x0);\r
1415 if (currentConfig.keyboard == 2)\r
1416 count_kbd = in_update_kbd(actions_kbd);\r
1417 else if (currentConfig.keyboard == 1)\r
1418 count_kbd = vkbd_update(vkbd, pl_actions[0], actions_kbd);\r
1419\r
1420 // FIXME: Only passthrough joystick input to avoid collisions\r
1421 // with PS/2 bindings. Ideally we should check if the device this\r
1422 // input originated from is the same as the device used for\r
1423 // PS/2 input, and passthrough if they are different devices.\r
1424 PicoIn.pad[0] = pl_actions[0] & mask;\r
1425 PicoIn.pad[1] = pl_actions[1] & mask;\r
1426 PicoIn.pad[2] = pl_actions[2] & mask;\r
1427 PicoIn.pad[3] = pl_actions[3] & mask;\r
1428\r
1429 // Ignore events mapped to bindings that collide with PS/2 peripherals.\r
1430 // Note that calls to emu_set_fastforward() should be avoided as well,\r
1431 // since fast-forward activates even with parameter set_on = 0.\r
1432 events &= PEV_SWITCH_KBD;\r
1433 } else {\r
1434 PicoIn.pad[0] = pl_actions[0] & 0xfff;\r
1435 PicoIn.pad[1] = pl_actions[1] & 0xfff;\r
1436 PicoIn.pad[2] = pl_actions[2] & 0xfff;\r
1437 PicoIn.pad[3] = pl_actions[3] & 0xfff;\r
1438\r
1439 if (pl_actions[0] & 0x7000)\r
1440 do_turbo(&PicoIn.pad[0], pl_actions[0]);\r
1441 if (pl_actions[1] & 0x7000)\r
1442 do_turbo(&PicoIn.pad[1], pl_actions[1]);\r
1443 if (pl_actions[2] & 0x7000)\r
1444 do_turbo(&PicoIn.pad[2], pl_actions[2]);\r
1445 if (pl_actions[3] & 0x7000)\r
1446 do_turbo(&PicoIn.pad[3], pl_actions[3]);\r
1447\r
1448 if ((events ^ prev_events) & PEV_FF) {\r
1449 emu_set_fastforward(events & PEV_FF);\r
1450 plat_update_volume(0, 0);\r
1451 reset_timing = 1;\r
1452 }\r
1453 }\r
1454\r
1455 // volume is treated in special way and triggered every frame\r
1456 if (events & (PEV_VOL_DOWN|PEV_VOL_UP))\r
1457 plat_update_volume(1, events & PEV_VOL_UP);\r
1458\r
1459 events &= ~prev_events;\r
1460\r
1461 // update keyboard input, actions only updated if keyboard mode active\r
1462 PicoIn.kbd = 0;\r
1463 for (i = 0; i < count_kbd; i++) {\r
1464 if (actions_kbd[i]) {\r
1465 unsigned int key = (actions_kbd[i] & 0xff);\r
1466 if (key == PEVB_KBD_LSHIFT || key == PEVB_KBD_RSHIFT ||\r
1467 key == PEVB_KBD_CTRL || key == PEVB_KBD_FUNC) {\r
1468 PicoIn.kbd = (PicoIn.kbd & 0x00ff) | (key << 8);\r
1469 } else {\r
1470 PicoIn.kbd = (PicoIn.kbd & 0xff00) | key;\r
1471 }\r
1472 }\r
1473 }\r
1474\r
1475 if (PicoIn.AHW & PAHW_PICO)\r
1476 run_events_pico(events);\r
1477 if (events)\r
1478 run_events_ui(events);\r
1479 if (movie_data)\r
1480 update_movie();\r
1481\r
1482 prev_events = actions[IN_BINDTYPE_EMU] & PEV_MASK;\r
1483}\r
1484\r
1485static void mkdir_path(char *path_with_reserve, int pos, const char *name)\r
1486{\r
1487 strcpy(path_with_reserve + pos, name);\r
1488 if (plat_is_dir(path_with_reserve))\r
1489 return;\r
1490 if (mkdir(path_with_reserve, 0755) < 0)\r
1491 lprintf("failed to create: %s\n", path_with_reserve);\r
1492}\r
1493\r
1494void emu_cmn_forced_frame(int no_scale, int do_emu, void *buf)\r
1495{\r
1496 int po_old = PicoIn.opt;\r
1497 int y;\r
1498\r
1499 for (y = 0; y < g_screen_height; y++)\r
1500 memset32((short *)g_screen_ptr + g_screen_ppitch * y, 0,\r
1501 g_screen_width * 2 / 4);\r
1502\r
1503 PicoIn.opt &= ~(POPT_ALT_RENDERER|POPT_EN_SOFTSCALE);\r
1504 PicoIn.opt |= POPT_ACC_SPRITES;\r
1505 if (!no_scale && currentConfig.scaling)\r
1506 PicoIn.opt |= POPT_EN_SOFTSCALE;\r
1507\r
1508 PicoDrawSetOutFormat(PDF_RGB555, 1);\r
1509 PicoDrawSetOutBuf(buf, g_screen_ppitch * 2);\r
1510 Pico.m.dirtyPal = 1;\r
1511 if (do_emu)\r
1512 PicoFrame();\r
1513 else\r
1514 PicoFrameDrawOnly();\r
1515\r
1516 PicoIn.opt = po_old;\r
1517}\r
1518\r
1519void emu_init(void)\r
1520{\r
1521 char path[512];\r
1522 int pos;\r
1523\r
1524#if 0\r
1525 // FIXME: handle through menu, etc\r
1526 FILE *f;\r
1527 f = fopen("32X_M_BIOS.BIN", "rb");\r
1528 p32x_bios_m = malloc(2048);\r
1529 fread(p32x_bios_m, 1, 2048, f);\r
1530 fclose(f);\r
1531 f = fopen("32X_S_BIOS.BIN", "rb");\r
1532 p32x_bios_s = malloc(1024);\r
1533 fread(p32x_bios_s, 1, 1024, f);\r
1534 fclose(f);\r
1535#endif\r
1536\r
1537 /* make dirs for saves */\r
1538 pos = plat_get_root_dir(path, sizeof(path) - 4);\r
1539 mkdir_path(path, pos, "mds");\r
1540 mkdir_path(path, pos, "srm");\r
1541 mkdir_path(path, pos, "brm");\r
1542 mkdir_path(path, pos, "tape");\r
1543 mkdir_path(path, pos, "cfg");\r
1544\r
1545 pprof_init();\r
1546\r
1547 make_config_cfg(path);\r
1548 config_readlrom(path);\r
1549\r
1550 PicoInit();\r
1551 PicoIn.osdMessage = plat_status_msg_busy_next;\r
1552 PicoIn.mcdTrayOpen = emu_tray_open;\r
1553 PicoIn.mcdTrayClose = emu_tray_close;\r
1554\r
1555 sndout_init();\r
1556}\r
1557\r
1558void emu_finish(void)\r
1559{\r
1560 // save SRAM\r
1561 if ((currentConfig.EmuOpt & EOPT_EN_SRAM) && Pico.sv.changed) {\r
1562 emu_save_load_game(0, 1);\r
1563 Pico.sv.changed = 0;\r
1564 }\r
1565\r
1566 if (!(currentConfig.EmuOpt & EOPT_NO_AUTOSVCFG)) {\r
1567 char cfg[512];\r
1568 make_config_cfg(cfg);\r
1569 config_writelrom(cfg);\r
1570#ifdef __GP2X__\r
1571 sync();\r
1572#endif\r
1573 }\r
1574\r
1575 pprof_finish();\r
1576\r
1577 PicoExit();\r
1578 sndout_exit();\r
1579}\r
1580\r
1581static void snd_write_nonblocking(int len)\r
1582{\r
1583 sndout_write_nb(PicoIn.sndOut, len);\r
1584}\r
1585\r
1586void emu_sound_start(void)\r
1587{\r
1588 PicoIn.sndOut = NULL;\r
1589\r
1590 // auto-select rate?\r
1591 if (PicoIn.sndRate > 52000 && PicoIn.sndRate < 54000)\r
1592 PicoIn.sndRate = YM2612_NATIVE_RATE();\r
1593 if (currentConfig.EmuOpt & EOPT_EN_SOUND)\r
1594 {\r
1595 int is_stereo = (PicoIn.opt & POPT_EN_STEREO) ? 1 : 0;\r
1596\r
1597 memset(sndBuffer, 0, sizeof(sndBuffer));\r
1598 PicoIn.sndOut = sndBuffer;\r
1599 PsndRerate(Pico.m.frame_count ? 1 : 0);\r
1600\r
1601 printf("starting audio: %i len: %i stereo: %i, pal: %i\n",\r
1602 PicoIn.sndRate, Pico.snd.len, is_stereo, Pico.m.pal);\r
1603\r
1604 sndout_start(PicoIn.sndRate, is_stereo);\r
1605 PicoIn.writeSound = snd_write_nonblocking;\r
1606 plat_update_volume(0, 0);\r
1607 }\r
1608}\r
1609\r
1610void emu_sound_stop(void)\r
1611{\r
1612 sndout_stop();\r
1613}\r
1614\r
1615void emu_sound_wait(void)\r
1616{\r
1617 sndout_wait();\r
1618}\r
1619\r
1620static void emu_loop_prep(void)\r
1621{\r
1622 static int pal_old = -1;\r
1623 static int filter_old = -1;\r
1624\r
1625 if (currentConfig.CPUclock != plat_target_cpu_clock_get())\r
1626 plat_target_cpu_clock_set(currentConfig.CPUclock);\r
1627\r
1628 if (Pico.m.pal != pal_old) {\r
1629 plat_target_lcdrate_set(Pico.m.pal);\r
1630 pal_old = Pico.m.pal;\r
1631 }\r
1632\r
1633 if (currentConfig.filter != filter_old) {\r
1634 plat_target_hwfilter_set(currentConfig.filter);\r
1635 filter_old = currentConfig.filter;\r
1636 }\r
1637\r
1638 plat_target_gamma_set(currentConfig.gamma, 0);\r
1639\r
1640 vkbd = NULL;\r
1641 if (currentConfig.keyboard == 1) {\r
1642 if (PicoIn.AHW & PAHW_SMS) vkbd = vkbd_init(0);\r
1643 else if (PicoIn.AHW & PAHW_PICO) vkbd = vkbd_init(1);\r
1644 }\r
1645 PicoIn.opt &= ~POPT_EN_KBD;\r
1646 if (((PicoIn.AHW & PAHW_PICO) || (PicoIn.AHW & PAHW_SC)) && currentConfig.keyboard)\r
1647 PicoIn.opt |= POPT_EN_KBD;\r
1648\r
1649 PicoIn.opt &= ~POPT_EN_MOUSE;\r
1650 if (!(PicoIn.AHW & PAHW_8BIT) && (currentConfig.input_dev0 == PICO_INPUT_MOUSE ||\r
1651 currentConfig.input_dev1 == PICO_INPUT_MOUSE)) {\r
1652 PicoIn.opt |= POPT_EN_MOUSE;\r
1653 plat_grab_cursor(grab_mode);\r
1654 }\r
1655\r
1656 pemu_loop_prep();\r
1657}\r
1658\r
1659/* our tick here is 1 us right now */\r
1660#define ms_to_ticks(x) (int)(x * 1000)\r
1661#define get_ticks() plat_get_ticks_us()\r
1662#define vsync_delay ms_to_ticks(1)\r
1663\r
1664void emu_loop(void)\r
1665{\r
1666 int frames_done, frames_shown; /* actual frames for fps counter */\r
1667 int target_frametime;\r
1668 unsigned int timestamp = 0;\r
1669 unsigned int timestamp_aim = 0;\r
1670 unsigned int timestamp_fps = 0;\r
1671 char *notice_msg = NULL;\r
1672 char fpsbuff[24];\r
1673 int fskip_cnt = 0;\r
1674\r
1675 fpsbuff[0] = 0;\r
1676\r
1677 PicoLoopPrepare();\r
1678\r
1679 plat_video_loop_prepare();\r
1680 emu_loop_prep();\r
1681 pemu_sound_start();\r
1682\r
1683 /* number of ticks per frame */\r
1684 if (Pico.m.pal)\r
1685 target_frametime = ms_to_ticks(1000) / 50;\r
1686 else\r
1687 target_frametime = ms_to_ticks(1000) / 60;\r
1688\r
1689 reset_timing = 1;\r
1690 frames_done = frames_shown = 0;\r
1691\r
1692 /* loop with resync every 1 sec. */\r
1693 while (engineState == PGS_Running)\r
1694 {\r
1695 int skip = 0;\r
1696 int diff;\r
1697\r
1698 pprof_start(main);\r
1699\r
1700 if (reset_timing) {\r
1701 reset_timing = 0;\r
1702 plat_video_wait_vsync();\r
1703 timestamp_aim = get_ticks();\r
1704 timestamp_fps = timestamp_aim;\r
1705 fskip_cnt = 0;\r
1706 }\r
1707 else if (currentConfig.EmuOpt & EOPT_NO_FRMLIMIT) {\r
1708 timestamp_aim = get_ticks();\r
1709 }\r
1710\r
1711 timestamp = get_ticks();\r
1712\r
1713 // show notice_msg message?\r
1714 if (notice_msg_time != 0)\r
1715 {\r
1716 static int noticeMsgSum;\r
1717 if (timestamp - ms_to_ticks(notice_msg_time)\r
1718 > ms_to_ticks(STATUS_MSG_TIMEOUT))\r
1719 {\r
1720 notice_msg_time = 0;\r
1721 notice_msg = NULL;\r
1722 plat_status_msg_clear();\r
1723 }\r
1724 else {\r
1725 int sum = noticeMsg[0] + noticeMsg[1] + noticeMsg[2];\r
1726 if (sum != noticeMsgSum) {\r
1727 plat_status_msg_clear();\r
1728 noticeMsgSum = sum;\r
1729 }\r
1730 notice_msg = noticeMsg;\r
1731 }\r
1732 }\r
1733\r
1734 // second changed?\r
1735 if (timestamp - timestamp_fps >= ms_to_ticks(1000))\r
1736 {\r
1737#ifdef BENCHMARK\r
1738 static int bench = 0, bench_fps = 0, bench_fps_s = 0, bfp = 0, bf[4];\r
1739 if (++bench == 10) {\r
1740 bench = 0;\r
1741 bench_fps_s = bench_fps;\r
1742 bf[bfp++ & 3] = bench_fps;\r
1743 bench_fps = 0;\r
1744 }\r
1745 bench_fps += frames_shown;\r
1746 sprintf(fpsbuff, "%02i/%02i/%02i", frames_shown, bench_fps_s, (bf[0]+bf[1]+bf[2]+bf[3])>>2);\r
1747 printf("%s\n", fpsbuff);\r
1748#else\r
1749 if (currentConfig.EmuOpt & EOPT_SHOW_FPS)\r
1750 snprintf(fpsbuff, 8, "%02i/%02i ", frames_shown, frames_done);\r
1751#endif\r
1752 frames_shown = frames_done = 0;\r
1753 timestamp_fps += ms_to_ticks(1000);\r
1754 }\r
1755#ifdef PFRAMES\r
1756 sprintf(fpsbuff, "%i", Pico.m.frame_count);\r
1757#endif\r
1758\r
1759 diff = timestamp_aim - timestamp;\r
1760\r
1761 if (currentConfig.Frameskip >= 0) // frameskip enabled (or 0)\r
1762 {\r
1763 if (fskip_cnt < currentConfig.Frameskip) {\r
1764 fskip_cnt++;\r
1765 skip = 1;\r
1766 }\r
1767 else {\r
1768 fskip_cnt = 0;\r
1769 }\r
1770 }\r
1771 else if (diff < -target_frametime)\r
1772 {\r
1773 /* no time left for this frame - skip */\r
1774 /* limit auto frameskip to max_skip */\r
1775 if (fskip_cnt < currentConfig.max_skip) {\r
1776 fskip_cnt++;\r
1777 skip = 1;\r
1778 }\r
1779 else {\r
1780 fskip_cnt = 0;\r
1781 }\r
1782 } else\r
1783 fskip_cnt = 0;\r
1784\r
1785 // don't go in debt too much\r
1786 while (diff < -target_frametime * 3) {\r
1787 timestamp_aim += target_frametime;\r
1788 diff = timestamp_aim - timestamp;\r
1789 }\r
1790\r
1791 emu_update_input();\r
1792\r
1793 // 3D glasses\r
1794 skip |= (PicoIn.AHW & PAHW_SMS) &&\r
1795 (Pico.m.hardware & PMS_HW_3D) &&\r
1796 (PicoMem.zram[0x1ffb] & 1);\r
1797\r
1798 if (skip) {\r
1799 int do_audio = diff > -target_frametime * 2;\r
1800 PicoIn.skipFrame = do_audio ? 1 : 2;\r
1801 PicoFrame();\r
1802 PicoIn.skipFrame = 0;\r
1803 }\r
1804 else {\r
1805 PicoFrame();\r
1806 pemu_finalize_frame(fpsbuff, notice_msg);\r
1807 frames_shown++;\r
1808 }\r
1809 frames_done++;\r
1810 timestamp_aim += target_frametime;\r
1811\r
1812 if (!skip && !flip_after_sync)\r
1813 plat_video_flip();\r
1814\r
1815 /* frame limiter */\r
1816 if (!skip && !reset_timing\r
1817 && !(currentConfig.EmuOpt & (EOPT_NO_FRMLIMIT|EOPT_EXT_FRMLIMIT)))\r
1818 {\r
1819 unsigned int timestamp = get_ticks();\r
1820 diff = timestamp_aim - timestamp;\r
1821\r
1822 // sleep or vsync if we are still too fast\r
1823 if (diff > target_frametime + vsync_delay && (currentConfig.EmuOpt & EOPT_VSYNC)) {\r
1824 // we are too fast\r
1825 plat_video_wait_vsync();\r
1826 timestamp = get_ticks();\r
1827 diff = timestamp_aim - timestamp;\r
1828 }\r
1829 if (diff > target_frametime + vsync_delay) {\r
1830 // still too fast\r
1831 plat_wait_till_us(timestamp + (diff - target_frametime));\r
1832 }\r
1833 }\r
1834\r
1835 if (!skip && flip_after_sync)\r
1836 plat_video_flip();\r
1837\r
1838 pprof_end(main);\r
1839 }\r
1840\r
1841 emu_set_fastforward(0);\r
1842\r
1843 // save SRAM\r
1844 if ((currentConfig.EmuOpt & EOPT_EN_SRAM) && Pico.sv.changed) {\r
1845 plat_status_msg_busy_first("Writing SRAM/BRAM...");\r
1846 emu_save_load_game(0, 1);\r
1847 Pico.sv.changed = 0;\r
1848 }\r
1849\r
1850 pemu_loop_end();\r
1851 emu_sound_stop();\r
1852 plat_grab_cursor(0);\r
1853}\r