rm dupe file
[picodrive.git] / platform / libretro.c
1 /*
2  * libretro core glue for PicoDrive
3  * (C) notaz, 2013
4  *
5  * This work is licensed under the terms of MAME license.
6  * See COPYING file in the top-level directory.
7  */
8
9 #define _GNU_SOURCE 1 // mremap
10 #include <stdio.h>
11 #include <stdarg.h>
12 #include <string.h>
13 #include <sys/mman.h>
14 #include <errno.h>
15 #ifdef __MACH__
16 #include <libkern/OSCacheControl.h>
17 #endif
18
19 #include <pico/pico_int.h>
20 #include "common/input_pico.h"
21 #include "common/version.h"
22 #include "libretro.h"
23
24 #ifndef MAP_ANONYMOUS
25 #define MAP_ANONYMOUS MAP_ANON
26 #endif
27
28 static retro_video_refresh_t video_cb;
29 static retro_input_poll_t input_poll_cb;
30 static retro_input_state_t input_state_cb;
31 static retro_environment_t environ_cb;
32 static retro_audio_sample_batch_t audio_batch_cb;
33
34 static FILE *emu_log;
35
36 #define VOUT_MAX_WIDTH 320
37 #define VOUT_MAX_HEIGHT 240
38 static void *vout_buf;
39 static int vout_width, vout_height;
40
41 static short __attribute__((aligned(4))) sndBuffer[2*44100/50];
42
43 // FIXME: these 2 shouldn't be here
44 static unsigned char PicoDraw2FB_[(8+320) * (8+240+8)];
45 unsigned char *PicoDraw2FB = PicoDraw2FB_;
46
47 static void snd_write(int len);
48
49 #ifdef _WIN32
50 #define SLASH '\\'
51 #else
52 #define SLASH '/'
53 #endif
54
55 /* functions called by the core */
56
57 void cache_flush_d_inval_i(void *start, void *end)
58 {
59 #ifdef __arm__
60 #if defined(__BLACKBERRY_QNX__)
61         msync(start, end - start, MS_SYNC | MS_CACHE_ONLY | MS_INVALIDATE_ICACHE);
62 #elif defined(__MACH__)
63         size_t len = (char *)end - (char *)start;
64         sys_dcache_flush(start, len);
65         sys_icache_invalidate(start, len);
66 #else
67         __clear_cache(start, end);
68 #endif
69 #endif
70 }
71
72 void *plat_mmap(unsigned long addr, size_t size, int need_exec, int is_fixed)
73 {
74         int flags = MAP_PRIVATE | MAP_ANONYMOUS;
75         void *req, *ret;
76
77         req = (void *)addr;
78         ret = mmap(req, size, PROT_READ | PROT_WRITE, flags, -1, 0);
79         if (ret == MAP_FAILED) {
80                 lprintf("mmap(%08lx, %zd) failed: %d\n", addr, size, errno);
81                 return NULL;
82         }
83
84         if (addr != 0 && ret != (void *)addr) {
85                 lprintf("warning: wanted to map @%08lx, got %p\n",
86                         addr, ret);
87
88                 if (is_fixed) {
89                         munmap(ret, size);
90                         return NULL;
91                 }
92         }
93
94         return ret;
95 }
96
97 void *plat_mremap(void *ptr, size_t oldsize, size_t newsize)
98 {
99 #ifdef __linux__
100         void *ret = mremap(ptr, oldsize, newsize, 0);
101         if (ret == MAP_FAILED)
102                 return NULL;
103
104         return ret;
105 #else
106         void *tmp, *ret;
107         size_t preserve_size;
108         
109         preserve_size = oldsize;
110         if (preserve_size > newsize)
111                 preserve_size = newsize;
112         tmp = malloc(preserve_size);
113         if (tmp == NULL)
114                 return NULL;
115         memcpy(tmp, ptr, preserve_size);
116
117         munmap(ptr, oldsize);
118         ret = mmap(ptr, newsize, PROT_READ | PROT_WRITE,
119                    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
120         if (ret == MAP_FAILED) {
121                 free(tmp);
122                 return NULL;
123         }
124         memcpy(ret, tmp, preserve_size);
125         free(tmp);
126         return ret;
127 #endif
128 }
129
130 void plat_munmap(void *ptr, size_t size)
131 {
132         if (ptr != NULL)
133                 munmap(ptr, size);
134 }
135
136 int plat_mem_set_exec(void *ptr, size_t size)
137 {
138         int ret = mprotect(ptr, size, PROT_READ | PROT_WRITE | PROT_EXEC);
139         if (ret != 0)
140                 lprintf("mprotect(%p, %zd) failed: %d\n", ptr, size, errno);
141
142         return ret;
143 }
144
145 void emu_video_mode_change(int start_line, int line_count, int is_32cols)
146 {
147         memset(vout_buf, 0, 320 * 240 * 2);
148         vout_width = is_32cols ? 256 : 320;
149         PicoDrawSetOutBuf(vout_buf, vout_width * 2);
150 }
151
152 void emu_32x_startup(void)
153 {
154         PicoDrawSetOutFormat(PDF_RGB555, 1);
155 }
156
157 #ifndef ANDROID
158
159 void lprintf(const char *fmt, ...)
160 {
161         va_list list;
162
163         va_start(list, fmt);
164         fprintf(emu_log, "PicoDrive: ");
165         vfprintf(emu_log, fmt, list);
166         va_end(list);
167         fflush(emu_log);
168 }
169
170 #else
171
172 #include <android/log.h>
173
174 void lprintf(const char *fmt, ...)
175 {
176         va_list list;
177
178         va_start(list, fmt);
179         __android_log_vprint(ANDROID_LOG_INFO, "PicoDrive", fmt, list);
180         va_end(list);
181 }
182
183 #endif
184
185 /* libretro */
186 void retro_set_environment(retro_environment_t cb)
187 {
188         static const struct retro_variable vars[] = {
189                 //{ "region", "Region; Auto|NTSC|PAL" },
190                 { NULL, NULL },
191         };
192
193         environ_cb = cb;
194
195         cb(RETRO_ENVIRONMENT_SET_VARIABLES, (void *)vars);
196 }
197
198 void retro_set_video_refresh(retro_video_refresh_t cb) { video_cb = cb; }
199 void retro_set_audio_sample(retro_audio_sample_t cb) { (void)cb; }
200 void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) { audio_batch_cb = cb; }
201 void retro_set_input_poll(retro_input_poll_t cb) { input_poll_cb = cb; }
202 void retro_set_input_state(retro_input_state_t cb) { input_state_cb = cb; }
203
204 unsigned retro_api_version(void)
205 {
206         return RETRO_API_VERSION;
207 }
208
209 void retro_set_controller_port_device(unsigned port, unsigned device)
210 {
211 }
212
213 void retro_get_system_info(struct retro_system_info *info)
214 {
215         memset(info, 0, sizeof(*info));
216         info->library_name = "PicoDrive";
217         info->library_version = VERSION;
218         info->valid_extensions = "bin|gen|smd|md|32x|cue|iso|sms";
219         info->need_fullpath = true;
220 }
221
222 void retro_get_system_av_info(struct retro_system_av_info *info)
223 {
224         memset(info, 0, sizeof(*info));
225         info->timing.fps            = Pico.m.pal ? 50 : 60;
226         info->timing.sample_rate    = 44100;
227         info->geometry.base_width   = 320;
228         info->geometry.base_height  = 240;
229         info->geometry.max_width    = VOUT_MAX_WIDTH;
230         info->geometry.max_height   = VOUT_MAX_HEIGHT;
231         info->geometry.aspect_ratio = 4.0 / 3.0;
232 }
233
234 /* savestates - TODO */
235 size_t retro_serialize_size(void) 
236
237        return 0;
238 }
239
240 bool retro_serialize(void *data, size_t size)
241
242        return false;
243 }
244
245 bool retro_unserialize(const void *data, size_t size)
246 {
247        return false;
248 }
249
250 /* cheats - TODO */
251 void retro_cheat_reset(void)
252 {
253 }
254
255 void retro_cheat_set(unsigned index, bool enabled, const char *code)
256 {
257 }
258
259 /* multidisk support */
260 static bool disk_ejected;
261 static unsigned int disk_current_index;
262 static unsigned int disk_count;
263 static struct disks_state {
264         char *fname;
265 } disks[8];
266
267 static bool disk_set_eject_state(bool ejected)
268 {
269         // TODO?
270         disk_ejected = ejected;
271         return true;
272 }
273
274 static bool disk_get_eject_state(void)
275 {
276         return disk_ejected;
277 }
278
279 static unsigned int disk_get_image_index(void)
280 {
281         return disk_current_index;
282 }
283
284 static bool disk_set_image_index(unsigned int index)
285 {
286         cd_img_type cd_type;
287         int ret;
288
289         if (index >= sizeof(disks) / sizeof(disks[0]))
290                 return false;
291
292         if (disks[index].fname == NULL) {
293                 lprintf("missing disk #%u\n", index);
294
295                 // RetroArch specifies "no disk" with index == count,
296                 // so don't fail here..
297                 disk_current_index = index;
298                 return true;
299         }
300
301         lprintf("switching to disk %u: \"%s\"\n", index,
302                 disks[index].fname);
303
304         ret = -1;
305         cd_type = PicoCdCheck(disks[index].fname, NULL);
306         if (cd_type != CIT_NOT_CD)
307                 ret = Insert_CD(disks[index].fname, cd_type);
308         if (ret != 0) {
309                 lprintf("Load failed, invalid CD image?\n");
310                 return 0;
311         }
312
313         disk_current_index = index;
314         return true;
315 }
316
317 static unsigned int disk_get_num_images(void)
318 {
319         return disk_count;
320 }
321
322 static bool disk_replace_image_index(unsigned index,
323         const struct retro_game_info *info)
324 {
325         bool ret = true;
326
327         if (index >= sizeof(disks) / sizeof(disks[0]))
328                 return false;
329
330         if (disks[index].fname != NULL)
331                 free(disks[index].fname);
332         disks[index].fname = NULL;
333
334         if (info != NULL) {
335                 disks[index].fname = strdup(info->path);
336                 if (index == disk_current_index)
337                         ret = disk_set_image_index(index);
338         }
339
340         return ret;
341 }
342
343 static bool disk_add_image_index(void)
344 {
345         if (disk_count >= sizeof(disks) / sizeof(disks[0]))
346                 return false;
347
348         disk_count++;
349         return true;
350 }
351
352 static struct retro_disk_control_callback disk_control = {
353         .set_eject_state = disk_set_eject_state,
354         .get_eject_state = disk_get_eject_state,
355         .get_image_index = disk_get_image_index,
356         .set_image_index = disk_set_image_index,
357         .get_num_images = disk_get_num_images,
358         .replace_image_index = disk_replace_image_index,
359         .add_image_index = disk_add_image_index,
360 };
361
362 static void disk_tray_open(void)
363 {
364         lprintf("cd tray open\n");
365         disk_ejected = 1;
366 }
367
368 static void disk_tray_close(void)
369 {
370         lprintf("cd tray close\n");
371         disk_ejected = 0;
372 }
373
374
375 static const char * const biosfiles_us[] = {
376         "us_scd1_9210", "us_scd2_9306", "SegaCDBIOS9303", "bios_CD_U"
377 };
378 static const char * const biosfiles_eu[] = {
379         "eu_mcd1_9210", "eu_mcd2_9306", "eu_mcd2_9303", "bios_CD_E"
380 };
381 static const char * const biosfiles_jp[] = {
382         "jp_mcd1_9112", "jp_mcd1_9111", "bios_CD_J"
383 };
384
385 static void make_system_path(char *buf, size_t buf_size,
386         const char *name, const char *ext)
387 {
388         const char *dir = NULL;
389
390         if (environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir) && dir) {
391                 snprintf(buf, buf_size, "%s%c%s%s", dir, SLASH, name, ext);
392         }
393         else {
394                 snprintf(buf, buf_size, "%s%s", name, ext);
395         }
396 }
397
398 static const char *find_bios(int *region, const char *cd_fname)
399 {
400         const char * const *files;
401         static char path[256];
402         int i, count;
403         FILE *f = NULL;
404
405         if (*region == 4) { // US
406                 files = biosfiles_us;
407                 count = sizeof(biosfiles_us) / sizeof(char *);
408         } else if (*region == 8) { // EU
409                 files = biosfiles_eu;
410                 count = sizeof(biosfiles_eu) / sizeof(char *);
411         } else if (*region == 1 || *region == 2) {
412                 files = biosfiles_jp;
413                 count = sizeof(biosfiles_jp) / sizeof(char *);
414         } else {
415                 return NULL;
416         }
417
418         for (i = 0; i < count; i++)
419         {
420                 make_system_path(path, sizeof(path), files[i], ".bin");
421                 f = fopen(path, "rb");
422                 if (f != NULL)
423                         break;
424
425                 make_system_path(path, sizeof(path), files[i], ".zip");
426                 f = fopen(path, "rb");
427                 if (f != NULL)
428                         break;
429         }
430
431         if (f != NULL) {
432                 lprintf("using bios: %s\n", path);
433                 fclose(f);
434                 return path;
435         }
436
437         return NULL;
438 }
439
440 bool retro_load_game(const struct retro_game_info *info)
441 {
442         enum media_type_e media_type;
443         static char carthw_path[256];
444         size_t i;
445
446         enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_RGB565;
447         if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt)) {
448                 lprintf("RGB565 suppot required, sorry\n");
449                 return false;
450         }
451
452         if (info == NULL || info->path == NULL) {
453                 lprintf("info->path required\n");
454                 return false;
455         }
456
457         for (i = 0; i < sizeof(disks) / sizeof(disks[0]); i++) {
458                 if (disks[i].fname != NULL) {
459                         free(disks[i].fname);
460                         disks[i].fname = NULL;
461                 }
462         }
463
464         disk_current_index = 0;
465         disk_count = 1;
466         disks[0].fname = strdup(info->path);
467
468         make_system_path(carthw_path, sizeof(carthw_path), "carthw", ".cfg");
469
470         media_type = PicoLoadMedia(info->path, carthw_path,
471                         find_bios, NULL);
472
473         switch (media_type) {
474         case PM_BAD_DETECT:
475                 lprintf("Failed to detect ROM/CD image type.\n");
476                 return false;
477         case PM_BAD_CD:
478                 lprintf("Invalid CD image\n");
479                 return false;
480         case PM_BAD_CD_NO_BIOS:
481                 lprintf("Missing BIOS\n");
482                 return false;
483         case PM_ERROR:
484                 lprintf("Load error\n");
485                 return false;
486         default:
487                 break;
488         }
489
490         PicoLoopPrepare();
491
492         PicoWriteSound = snd_write;
493         memset(sndBuffer, 0, sizeof(sndBuffer));
494         PsndOut = sndBuffer;
495         PsndRerate(1);
496
497         return true;
498 }
499
500 bool retro_load_game_special(unsigned game_type, const struct retro_game_info *info, size_t num_info)
501 {
502         return false;
503 }
504
505 void retro_unload_game(void) 
506 {
507 }
508
509 unsigned retro_get_region(void)
510 {
511         return Pico.m.pal ? RETRO_REGION_PAL : RETRO_REGION_NTSC;
512 }
513
514 void *retro_get_memory_data(unsigned id)
515 {
516         if (id != RETRO_MEMORY_SAVE_RAM)
517                 return NULL;
518
519         if (PicoAHW & PAHW_MCD)
520                 return Pico_mcd->bram;
521         else
522                 return SRam.data;
523 }
524
525 size_t retro_get_memory_size(unsigned id)
526 {
527         if (id != RETRO_MEMORY_SAVE_RAM)
528                 return 0;
529
530         if (PicoAHW & PAHW_MCD)
531                 // bram
532                 return 0x2000;
533         else
534                 return SRam.size;
535 }
536
537 void retro_reset(void)
538 {
539         PicoReset();
540 }
541
542 static const unsigned short retro_pico_map[] = {
543         [RETRO_DEVICE_ID_JOYPAD_B]      = 1 << GBTN_B,
544         [RETRO_DEVICE_ID_JOYPAD_Y]      = 1 << GBTN_A,
545         [RETRO_DEVICE_ID_JOYPAD_SELECT] = 1 << GBTN_MODE,
546         [RETRO_DEVICE_ID_JOYPAD_START]  = 1 << GBTN_START,
547         [RETRO_DEVICE_ID_JOYPAD_UP]     = 1 << GBTN_UP,
548         [RETRO_DEVICE_ID_JOYPAD_DOWN]   = 1 << GBTN_DOWN,
549         [RETRO_DEVICE_ID_JOYPAD_LEFT]   = 1 << GBTN_LEFT,
550         [RETRO_DEVICE_ID_JOYPAD_RIGHT]  = 1 << GBTN_RIGHT,
551         [RETRO_DEVICE_ID_JOYPAD_A]      = 1 << GBTN_C,
552         [RETRO_DEVICE_ID_JOYPAD_X]      = 1 << GBTN_Y,
553         [RETRO_DEVICE_ID_JOYPAD_L]      = 1 << GBTN_X,
554         [RETRO_DEVICE_ID_JOYPAD_R]      = 1 << GBTN_Z,
555 };
556 #define RETRO_PICO_MAP_LEN (sizeof(retro_pico_map) / sizeof(retro_pico_map[0]))
557
558 static void snd_write(int len)
559 {
560         audio_batch_cb(PsndOut, len / 4);
561 }
562
563 void retro_run(void) 
564 {
565         bool updated = false;
566         int pad, i;
567
568         if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated)
569                 ; //update_variables(true);
570
571         input_poll_cb();
572
573         PicoPad[0] = PicoPad[1] = 0;
574         for (pad = 0; pad < 2; pad++)
575                 for (i = 0; i < RETRO_PICO_MAP_LEN; i++)
576                         if (input_state_cb(pad, RETRO_DEVICE_JOYPAD, 0, i))
577                                 PicoPad[pad] |= retro_pico_map[i];
578
579         PicoFrame();
580
581         video_cb(vout_buf, vout_width, vout_height, vout_width * 2);
582 }
583
584 void retro_init(void)
585 {
586         int level;
587
588 #ifdef IOS
589         emu_log = fopen("/User/Documents/PicoDrive.log", "w");
590         if (emu_log == NULL)
591                 emu_log = fopen("PicoDrive.log", "w");
592         if (emu_log == NULL)
593 #endif
594         emu_log = stdout;
595
596         level = 0;
597         environ_cb(RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL, &level);
598
599         environ_cb(RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE, &disk_control);
600
601         PicoOpt = POPT_EN_STEREO|POPT_EN_FM|POPT_EN_PSG|POPT_EN_Z80
602                 | POPT_EN_MCD_PCM|POPT_EN_MCD_CDDA|POPT_EN_MCD_GFX
603                 | POPT_EN_32X|POPT_EN_PWM
604                 | POPT_ACC_SPRITES|POPT_DIS_32C_BORDER;
605 #ifdef __arm__
606         PicoOpt |= POPT_EN_SVP_DRC;
607 #endif
608         PsndRate = 44100;
609         PicoAutoRgnOrder = 0x184; // US, EU, JP
610         PicoCDBuffers = 0;
611
612         vout_width = 320;
613         vout_height = 240;
614         vout_buf = malloc(VOUT_MAX_WIDTH * VOUT_MAX_HEIGHT * 2);
615
616         PicoInit();
617         PicoDrawSetOutFormat(PDF_RGB555, 1);
618         PicoDrawSetOutBuf(vout_buf, vout_width * 2);
619
620         //PicoMessage = plat_status_msg_busy_next;
621         PicoMCDopenTray = disk_tray_open;
622         PicoMCDcloseTray = disk_tray_close;
623 }
624
625 void retro_deinit(void)
626 {
627         PicoExit();
628 }