(Libretro) Fixup logging
[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 #ifndef _WIN32
14 #include <sys/mman.h>
15 #else
16 #include <io.h>
17 #include <windows.h>
18 #include <sys/types.h>
19 #endif
20 #include <errno.h>
21 #ifdef __MACH__
22 #include <libkern/OSCacheControl.h>
23 #endif
24
25 #include <pico/pico_int.h>
26 #include <pico/state.h>
27 #include "common/input_pico.h"
28 #include "common/version.h"
29 #include "libretro.h"
30
31 static retro_log_printf_t log_cb;
32 static retro_video_refresh_t video_cb;
33 static retro_input_poll_t input_poll_cb;
34 static retro_input_state_t input_state_cb;
35 static retro_environment_t environ_cb;
36 static retro_audio_sample_batch_t audio_batch_cb;
37
38 #define VOUT_MAX_WIDTH 320
39 #define VOUT_MAX_HEIGHT 240
40 static void *vout_buf;
41 static int vout_width, vout_height, vout_offset;
42
43 static short __attribute__((aligned(4))) sndBuffer[2*44100/50];
44
45 static void snd_write(int len);
46
47 #ifdef _WIN32
48 #define SLASH '\\'
49 #else
50 #define SLASH '/'
51 #endif
52
53 /* functions called by the core */
54
55 void cache_flush_d_inval_i(void *start, void *end)
56 {
57 #ifdef __arm__
58 #if defined(__BLACKBERRY_QNX__)
59         msync(start, end - start, MS_SYNC | MS_CACHE_ONLY | MS_INVALIDATE_ICACHE);
60 #elif defined(__MACH__)
61         size_t len = (char *)end - (char *)start;
62         sys_dcache_flush(start, len);
63         sys_icache_invalidate(start, len);
64 #else
65         __clear_cache(start, end);
66 #endif
67 #endif
68 }
69
70 #ifdef _WIN32
71 /* mmap() replacement for Windows
72  *
73  * Author: Mike Frysinger <vapier@gentoo.org>
74  * Placed into the public domain
75  */
76
77 /* References:
78  * CreateFileMapping: http://msdn.microsoft.com/en-us/library/aa366537(VS.85).aspx
79  * CloseHandle:       http://msdn.microsoft.com/en-us/library/ms724211(VS.85).aspx
80  * MapViewOfFile:     http://msdn.microsoft.com/en-us/library/aa366761(VS.85).aspx
81  * UnmapViewOfFile:   http://msdn.microsoft.com/en-us/library/aa366882(VS.85).aspx
82  */
83
84 #define PROT_READ     0x1
85 #define PROT_WRITE    0x2
86 /* This flag is only available in WinXP+ */
87 #ifdef FILE_MAP_EXECUTE
88 #define PROT_EXEC     0x4
89 #else
90 #define PROT_EXEC        0x0
91 #define FILE_MAP_EXECUTE 0
92 #endif
93
94 #define MAP_SHARED    0x01
95 #define MAP_PRIVATE   0x02
96 #define MAP_ANONYMOUS 0x20
97 #define MAP_ANON      MAP_ANONYMOUS
98 #define MAP_FAILED    ((void *) -1)
99
100 #ifdef __USE_FILE_OFFSET64
101 # define DWORD_HI(x) (x >> 32)
102 # define DWORD_LO(x) ((x) & 0xffffffff)
103 #else
104 # define DWORD_HI(x) (0)
105 # define DWORD_LO(x) (x)
106 #endif
107
108 static void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset)
109 {
110         if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC))
111                 return MAP_FAILED;
112         if (fd == -1) {
113                 if (!(flags & MAP_ANON) || offset)
114                         return MAP_FAILED;
115         } else if (flags & MAP_ANON)
116                 return MAP_FAILED;
117
118         DWORD flProtect;
119         if (prot & PROT_WRITE) {
120                 if (prot & PROT_EXEC)
121                         flProtect = PAGE_EXECUTE_READWRITE;
122                 else
123                         flProtect = PAGE_READWRITE;
124         } else if (prot & PROT_EXEC) {
125                 if (prot & PROT_READ)
126                         flProtect = PAGE_EXECUTE_READ;
127                 else if (prot & PROT_EXEC)
128                         flProtect = PAGE_EXECUTE;
129         } else
130                 flProtect = PAGE_READONLY;
131
132         off_t end = length + offset;
133         HANDLE mmap_fd, h;
134         if (fd == -1)
135                 mmap_fd = INVALID_HANDLE_VALUE;
136         else
137                 mmap_fd = (HANDLE)_get_osfhandle(fd);
138         h = CreateFileMapping(mmap_fd, NULL, flProtect, DWORD_HI(end), DWORD_LO(end), NULL);
139         if (h == NULL)
140                 return MAP_FAILED;
141
142         DWORD dwDesiredAccess;
143         if (prot & PROT_WRITE)
144                 dwDesiredAccess = FILE_MAP_WRITE;
145         else
146                 dwDesiredAccess = FILE_MAP_READ;
147         if (prot & PROT_EXEC)
148                 dwDesiredAccess |= FILE_MAP_EXECUTE;
149         if (flags & MAP_PRIVATE)
150                 dwDesiredAccess |= FILE_MAP_COPY;
151         void *ret = MapViewOfFile(h, dwDesiredAccess, DWORD_HI(offset), DWORD_LO(offset), length);
152         if (ret == NULL) {
153                 CloseHandle(h);
154                 ret = MAP_FAILED;
155         }
156         return ret;
157 }
158
159 static void munmap(void *addr, size_t length)
160 {
161         UnmapViewOfFile(addr);
162         /* ruh-ro, we leaked handle from CreateFileMapping() ... */
163 }
164 #endif
165
166 #ifndef MAP_ANONYMOUS
167 #define MAP_ANONYMOUS MAP_ANON
168 #endif
169
170 void *plat_mmap(unsigned long addr, size_t size, int need_exec, int is_fixed)
171 {
172    int flags = MAP_PRIVATE | MAP_ANONYMOUS;
173    void *req, *ret;
174
175    req = (void *)addr;
176    ret = mmap(req, size, PROT_READ | PROT_WRITE, flags, -1, 0);
177    if (ret == MAP_FAILED) {
178       if (log_cb)
179          log_cb(RETRO_LOG_ERROR, "mmap(%08lx, %zd) failed: %d\n", addr, size, errno);
180       return NULL;
181    }
182
183    if (addr != 0 && ret != (void *)addr) {
184       if (log_cb)
185          log_cb(RETRO_LOG_WARN, "warning: wanted to map @%08lx, got %p\n",
186                addr, ret);
187
188       if (is_fixed) {
189          munmap(ret, size);
190          return NULL;
191       }
192    }
193
194         return ret;
195 }
196
197 void *plat_mremap(void *ptr, size_t oldsize, size_t newsize)
198 {
199 #ifdef __linux__
200         void *ret = mremap(ptr, oldsize, newsize, 0);
201         if (ret == MAP_FAILED)
202                 return NULL;
203
204         return ret;
205 #else
206         void *tmp, *ret;
207         size_t preserve_size;
208         
209         preserve_size = oldsize;
210         if (preserve_size > newsize)
211                 preserve_size = newsize;
212         tmp = malloc(preserve_size);
213         if (tmp == NULL)
214                 return NULL;
215         memcpy(tmp, ptr, preserve_size);
216
217         munmap(ptr, oldsize);
218         ret = mmap(ptr, newsize, PROT_READ | PROT_WRITE,
219                    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
220         if (ret == MAP_FAILED) {
221                 free(tmp);
222                 return NULL;
223         }
224         memcpy(ret, tmp, preserve_size);
225         free(tmp);
226         return ret;
227 #endif
228 }
229
230 void plat_munmap(void *ptr, size_t size)
231 {
232         if (ptr != NULL)
233                 munmap(ptr, size);
234 }
235
236 int plat_mem_set_exec(void *ptr, size_t size)
237 {
238 #ifdef _WIN32
239    int ret = VirtualProtect(ptr,size,PAGE_EXECUTE_READWRITE,0);
240    if (ret == 0 && log_cb)
241       log_cb(RETRO_LOG_ERROR, "mprotect(%p, %zd) failed: %d\n", ptr, size, 0);
242 #else
243    int ret = mprotect(ptr, size, PROT_READ | PROT_WRITE | PROT_EXEC);
244    if (ret != 0 && log_cb)
245       log_cb(RETRO_LOG_ERROR, "mprotect(%p, %zd) failed: %d\n", ptr, size, errno);
246 #endif
247         return ret;
248 }
249
250 void emu_video_mode_change(int start_line, int line_count, int is_32cols)
251 {
252         memset(vout_buf, 0, 320 * 240 * 2);
253         vout_width = is_32cols ? 256 : 320;
254         PicoDrawSetOutBuf(vout_buf, vout_width * 2);
255
256         vout_height = line_count;
257         vout_offset = vout_width * start_line;
258 }
259
260 void emu_32x_startup(void)
261 {
262 }
263
264 void lprintf(const char *fmt, ...)
265 {
266    char buffer[256];
267    va_list ap;
268    va_start(ap, fmt);
269    vsprintf(buffer, fmt, ap);
270    /* TODO - add 'level' param for warning/error messages? */
271    if (log_cb)
272       log_cb(RETRO_LOG_INFO, "%s\n", fmt);
273 }
274
275 /* libretro */
276 void retro_set_environment(retro_environment_t cb)
277 {
278         static const struct retro_variable vars[] = {
279                 //{ "region", "Region; Auto|NTSC|PAL" },
280                 { "picodrive_input1", "Input device 1; 3 button pad|6 button pad|None" },
281                 { "picodrive_input2", "Input device 2; 3 button pad|6 button pad|None" },
282                 { "picodrive_sprlim", "No sprite limit; disabled|enabled" },
283                 { "picodrive_ramcart", "MegaCD RAM cart; disabled|enabled" },
284 #ifdef DRC_SH2
285                 { "picodrive_drc", "Dynamic recompilers; enabled|disabled" },
286 #endif
287                 { NULL, NULL },
288         };
289
290         environ_cb = cb;
291
292         cb(RETRO_ENVIRONMENT_SET_VARIABLES, (void *)vars);
293 }
294
295 void retro_set_video_refresh(retro_video_refresh_t cb) { video_cb = cb; }
296 void retro_set_audio_sample(retro_audio_sample_t cb) { (void)cb; }
297 void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) { audio_batch_cb = cb; }
298 void retro_set_input_poll(retro_input_poll_t cb) { input_poll_cb = cb; }
299 void retro_set_input_state(retro_input_state_t cb) { input_state_cb = cb; }
300
301 unsigned retro_api_version(void)
302 {
303         return RETRO_API_VERSION;
304 }
305
306 void retro_set_controller_port_device(unsigned port, unsigned device)
307 {
308 }
309
310 void retro_get_system_info(struct retro_system_info *info)
311 {
312         memset(info, 0, sizeof(*info));
313         info->library_name = "PicoDrive";
314         info->library_version = VERSION;
315         info->valid_extensions = "bin|gen|smd|md|32x|cue|iso|sms";
316         info->need_fullpath = true;
317 }
318
319 void retro_get_system_av_info(struct retro_system_av_info *info)
320 {
321         memset(info, 0, sizeof(*info));
322         info->timing.fps            = Pico.m.pal ? 50 : 60;
323         info->timing.sample_rate    = 44100;
324         info->geometry.base_width   = 320;
325         info->geometry.base_height  = vout_height;
326         info->geometry.max_width    = VOUT_MAX_WIDTH;
327         info->geometry.max_height   = VOUT_MAX_HEIGHT;
328         info->geometry.aspect_ratio = 0.0f;
329 }
330
331 /* savestates */
332 struct savestate_state {
333         const char *load_buf;
334         char *save_buf;
335         size_t size;
336         size_t pos;
337 };
338
339 size_t state_read(void *p, size_t size, size_t nmemb, void *file)
340 {
341         struct savestate_state *state = file;
342         size_t bsize = size * nmemb;
343
344         if (state->pos + bsize > state->size) {
345       if (log_cb)
346          log_cb(RETRO_LOG_ERROR, "savestate error: %u/%u\n",
347                state->pos + bsize, state->size);
348                 bsize = state->size - state->pos;
349                 if ((int)bsize <= 0)
350                         return 0;
351         }
352
353         memcpy(p, state->load_buf + state->pos, bsize);
354         state->pos += bsize;
355         return bsize;
356 }
357
358 size_t state_write(void *p, size_t size, size_t nmemb, void *file)
359 {
360         struct savestate_state *state = file;
361         size_t bsize = size * nmemb;
362
363         if (state->pos + bsize > state->size) {
364       if (log_cb)
365          log_cb(RETRO_LOG_ERROR, "savestate error: %u/%u\n",
366                state->pos + bsize, state->size);
367                 bsize = state->size - state->pos;
368                 if ((int)bsize <= 0)
369                         return 0;
370         }
371
372         memcpy(state->save_buf + state->pos, p, bsize);
373         state->pos += bsize;
374         return bsize;
375 }
376
377 size_t state_skip(void *p, size_t size, size_t nmemb, void *file)
378 {
379         struct savestate_state *state = file;
380         size_t bsize = size * nmemb;
381
382         state->pos += bsize;
383         return bsize;
384 }
385
386 size_t state_eof(void *file)
387 {
388         struct savestate_state *state = file;
389
390         return state->pos >= state->size;
391 }
392
393 int state_fseek(void *file, long offset, int whence)
394 {
395         struct savestate_state *state = file;
396
397         switch (whence) {
398         case SEEK_SET:
399                 state->pos = offset;
400                 break;
401         case SEEK_CUR:
402                 state->pos += offset;
403                 break;
404         case SEEK_END:
405                 state->pos = state->size + offset;
406                 break;
407         }
408         return (int)state->pos;
409 }
410
411 /* savestate sizes vary wildly depending if cd/32x or
412  * carthw is active, so run the whole thing to get size */
413 size_t retro_serialize_size(void) 
414
415         struct savestate_state state = { 0, };
416         int ret;
417
418         ret = PicoStateFP(&state, 1, NULL, state_skip, NULL, state_fseek);
419         if (ret != 0)
420                 return 0;
421
422         return state.pos;
423 }
424
425 bool retro_serialize(void *data, size_t size)
426
427         struct savestate_state state = { 0, };
428         int ret;
429
430         state.save_buf = data;
431         state.size = size;
432         state.pos = 0;
433
434         ret = PicoStateFP(&state, 1, NULL, state_write,
435                 NULL, state_fseek);
436         return ret == 0;
437 }
438
439 bool retro_unserialize(const void *data, size_t size)
440 {
441         struct savestate_state state = { 0, };
442         int ret;
443
444         state.load_buf = data;
445         state.size = size;
446         state.pos = 0;
447
448         ret = PicoStateFP(&state, 0, state_read, NULL,
449                 state_eof, state_fseek);
450         return ret == 0;
451 }
452
453 /* cheats - TODO */
454 void retro_cheat_reset(void)
455 {
456 }
457
458 void retro_cheat_set(unsigned index, bool enabled, const char *code)
459 {
460 }
461
462 /* multidisk support */
463 static bool disk_ejected;
464 static unsigned int disk_current_index;
465 static unsigned int disk_count;
466 static struct disks_state {
467         char *fname;
468 } disks[8];
469
470 static bool disk_set_eject_state(bool ejected)
471 {
472         // TODO?
473         disk_ejected = ejected;
474         return true;
475 }
476
477 static bool disk_get_eject_state(void)
478 {
479         return disk_ejected;
480 }
481
482 static unsigned int disk_get_image_index(void)
483 {
484         return disk_current_index;
485 }
486
487 static bool disk_set_image_index(unsigned int index)
488 {
489         enum cd_img_type cd_type;
490         int ret;
491
492         if (index >= sizeof(disks) / sizeof(disks[0]))
493                 return false;
494
495         if (disks[index].fname == NULL) {
496       if (log_cb)
497          log_cb(RETRO_LOG_ERROR, "missing disk #%u\n", index);
498
499                 // RetroArch specifies "no disk" with index == count,
500                 // so don't fail here..
501                 disk_current_index = index;
502                 return true;
503         }
504
505    if (log_cb)
506       log_cb(RETRO_LOG_INFO, "switching to disk %u: \"%s\"\n", index,
507             disks[index].fname);
508
509         ret = -1;
510         cd_type = PicoCdCheck(disks[index].fname, NULL);
511         if (cd_type != CIT_NOT_CD)
512                 ret = cdd_load(disks[index].fname, cd_type);
513         if (ret != 0) {
514       if (log_cb)
515          log_cb(RETRO_LOG_ERROR, "Load failed, invalid CD image?\n");
516                 return 0;
517         }
518
519         disk_current_index = index;
520         return true;
521 }
522
523 static unsigned int disk_get_num_images(void)
524 {
525         return disk_count;
526 }
527
528 static bool disk_replace_image_index(unsigned index,
529         const struct retro_game_info *info)
530 {
531         bool ret = true;
532
533         if (index >= sizeof(disks) / sizeof(disks[0]))
534                 return false;
535
536         if (disks[index].fname != NULL)
537                 free(disks[index].fname);
538         disks[index].fname = NULL;
539
540         if (info != NULL) {
541                 disks[index].fname = strdup(info->path);
542                 if (index == disk_current_index)
543                         ret = disk_set_image_index(index);
544         }
545
546         return ret;
547 }
548
549 static bool disk_add_image_index(void)
550 {
551         if (disk_count >= sizeof(disks) / sizeof(disks[0]))
552                 return false;
553
554         disk_count++;
555         return true;
556 }
557
558 static struct retro_disk_control_callback disk_control = {
559         .set_eject_state = disk_set_eject_state,
560         .get_eject_state = disk_get_eject_state,
561         .get_image_index = disk_get_image_index,
562         .set_image_index = disk_set_image_index,
563         .get_num_images = disk_get_num_images,
564         .replace_image_index = disk_replace_image_index,
565         .add_image_index = disk_add_image_index,
566 };
567
568 static void disk_tray_open(void)
569 {
570    if (log_cb)
571       log_cb(RETRO_LOG_INFO, "cd tray open\n");
572         disk_ejected = 1;
573 }
574
575 static void disk_tray_close(void)
576 {
577    if (log_cb)
578       log_cb(RETRO_LOG_INFO, "cd tray close\n");
579         disk_ejected = 0;
580 }
581
582
583 static const char * const biosfiles_us[] = {
584         "us_scd2_9306", "SegaCDBIOS9303", "us_scd1_9210", "bios_CD_U"
585 };
586 static const char * const biosfiles_eu[] = {
587         "eu_mcd2_9306", "eu_mcd2_9303", "eu_mcd1_9210", "bios_CD_E"
588 };
589 static const char * const biosfiles_jp[] = {
590         "jp_mcd2_921222", "jp_mcd1_9112", "jp_mcd1_9111", "bios_CD_J"
591 };
592
593 static void make_system_path(char *buf, size_t buf_size,
594         const char *name, const char *ext)
595 {
596         const char *dir = NULL;
597
598         if (environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir) && dir) {
599                 snprintf(buf, buf_size, "%s%c%s%s", dir, SLASH, name, ext);
600         }
601         else {
602                 snprintf(buf, buf_size, "%s%s", name, ext);
603         }
604 }
605
606 static const char *find_bios(int *region, const char *cd_fname)
607 {
608         const char * const *files;
609         static char path[256];
610         int i, count;
611         FILE *f = NULL;
612
613         if (*region == 4) { // US
614                 files = biosfiles_us;
615                 count = sizeof(biosfiles_us) / sizeof(char *);
616         } else if (*region == 8) { // EU
617                 files = biosfiles_eu;
618                 count = sizeof(biosfiles_eu) / sizeof(char *);
619         } else if (*region == 1 || *region == 2) {
620                 files = biosfiles_jp;
621                 count = sizeof(biosfiles_jp) / sizeof(char *);
622         } else {
623                 return NULL;
624         }
625
626         for (i = 0; i < count; i++)
627         {
628                 make_system_path(path, sizeof(path), files[i], ".bin");
629                 f = fopen(path, "rb");
630                 if (f != NULL)
631                         break;
632
633                 make_system_path(path, sizeof(path), files[i], ".zip");
634                 f = fopen(path, "rb");
635                 if (f != NULL)
636                         break;
637         }
638
639         if (f != NULL) {
640       if (log_cb)
641          log_cb(RETRO_LOG_INFO, "using bios: %s\n", path);
642                 fclose(f);
643                 return path;
644         }
645
646         return NULL;
647 }
648
649 bool retro_load_game(const struct retro_game_info *info)
650 {
651         enum media_type_e media_type;
652         static char carthw_path[256];
653         size_t i;
654
655         enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_RGB565;
656         if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt)) {
657       if (log_cb)
658          log_cb(RETRO_LOG_ERROR, "RGB565 support required, sorry\n");
659                 return false;
660         }
661
662         if (info == NULL || info->path == NULL) {
663       if (log_cb)
664          log_cb(RETRO_LOG_ERROR, "info->path required\n");
665                 return false;
666         }
667
668         for (i = 0; i < sizeof(disks) / sizeof(disks[0]); i++) {
669                 if (disks[i].fname != NULL) {
670                         free(disks[i].fname);
671                         disks[i].fname = NULL;
672                 }
673         }
674
675         disk_current_index = 0;
676         disk_count = 1;
677         disks[0].fname = strdup(info->path);
678
679         make_system_path(carthw_path, sizeof(carthw_path), "carthw", ".cfg");
680
681         media_type = PicoLoadMedia(info->path, carthw_path,
682                         find_bios, NULL);
683
684         switch (media_type) {
685         case PM_BAD_DETECT:
686       if (log_cb)
687          log_cb(RETRO_LOG_ERROR, "Failed to detect ROM/CD image type.\n");
688                 return false;
689         case PM_BAD_CD:
690       if (log_cb)
691          log_cb(RETRO_LOG_ERROR, "Invalid CD image\n");
692                 return false;
693         case PM_BAD_CD_NO_BIOS:
694       if (log_cb)
695          log_cb(RETRO_LOG_ERROR, "Missing BIOS\n");
696                 return false;
697         case PM_ERROR:
698       if (log_cb)
699          log_cb(RETRO_LOG_ERROR, "Load error\n");
700                 return false;
701         default:
702                 break;
703         }
704
705         PicoLoopPrepare();
706
707         PicoWriteSound = snd_write;
708         memset(sndBuffer, 0, sizeof(sndBuffer));
709         PsndOut = sndBuffer;
710         PsndRerate(0);
711
712         return true;
713 }
714
715 bool retro_load_game_special(unsigned game_type, const struct retro_game_info *info, size_t num_info)
716 {
717         return false;
718 }
719
720 void retro_unload_game(void) 
721 {
722 }
723
724 unsigned retro_get_region(void)
725 {
726         return Pico.m.pal ? RETRO_REGION_PAL : RETRO_REGION_NTSC;
727 }
728
729 void *retro_get_memory_data(unsigned id)
730 {
731         if (id != RETRO_MEMORY_SAVE_RAM)
732                 return NULL;
733
734         if (PicoAHW & PAHW_MCD)
735                 return Pico_mcd->bram;
736         else
737                 return SRam.data;
738 }
739
740 size_t retro_get_memory_size(unsigned id)
741 {
742         unsigned int i;
743         int sum;
744
745         if (id != RETRO_MEMORY_SAVE_RAM)
746                 return 0;
747
748         if (PicoAHW & PAHW_MCD)
749                 // bram
750                 return 0x2000;
751
752         if (Pico.m.frame_count == 0)
753                 return SRam.size;
754
755         // if game doesn't write to sram, don't report it to
756         // libretro so that RA doesn't write out zeroed .srm
757         for (i = 0, sum = 0; i < SRam.size; i++)
758                 sum |= SRam.data[i];
759
760         return (sum != 0) ? SRam.size : 0;
761 }
762
763 void retro_reset(void)
764 {
765         PicoReset();
766 }
767
768 static const unsigned short retro_pico_map[] = {
769         [RETRO_DEVICE_ID_JOYPAD_B]      = 1 << GBTN_B,
770         [RETRO_DEVICE_ID_JOYPAD_Y]      = 1 << GBTN_A,
771         [RETRO_DEVICE_ID_JOYPAD_SELECT] = 1 << GBTN_MODE,
772         [RETRO_DEVICE_ID_JOYPAD_START]  = 1 << GBTN_START,
773         [RETRO_DEVICE_ID_JOYPAD_UP]     = 1 << GBTN_UP,
774         [RETRO_DEVICE_ID_JOYPAD_DOWN]   = 1 << GBTN_DOWN,
775         [RETRO_DEVICE_ID_JOYPAD_LEFT]   = 1 << GBTN_LEFT,
776         [RETRO_DEVICE_ID_JOYPAD_RIGHT]  = 1 << GBTN_RIGHT,
777         [RETRO_DEVICE_ID_JOYPAD_A]      = 1 << GBTN_C,
778         [RETRO_DEVICE_ID_JOYPAD_X]      = 1 << GBTN_Y,
779         [RETRO_DEVICE_ID_JOYPAD_L]      = 1 << GBTN_X,
780         [RETRO_DEVICE_ID_JOYPAD_R]      = 1 << GBTN_Z,
781 };
782 #define RETRO_PICO_MAP_LEN (sizeof(retro_pico_map) / sizeof(retro_pico_map[0]))
783
784 static void snd_write(int len)
785 {
786         audio_batch_cb(PsndOut, len / 4);
787 }
788
789 static enum input_device input_name_to_val(const char *name)
790 {
791         if (strcmp(name, "3 button pad") == 0)
792                 return PICO_INPUT_PAD_3BTN;
793         if (strcmp(name, "6 button pad") == 0)
794                 return PICO_INPUT_PAD_6BTN;
795         if (strcmp(name, "None") == 0)
796                 return PICO_INPUT_NOTHING;
797
798    if (log_cb)
799       log_cb(RETRO_LOG_WARN, "invalid picodrive_input: '%s'\n", name);
800         return PICO_INPUT_PAD_3BTN;
801 }
802
803 static void update_variables(void)
804 {
805         struct retro_variable var;
806
807         var.value = NULL;
808         var.key = "picodrive_input1";
809         if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
810                 PicoSetInputDevice(0, input_name_to_val(var.value));
811
812         var.value = NULL;
813         var.key = "picodrive_input2";
814         if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
815                 PicoSetInputDevice(1, input_name_to_val(var.value));
816
817         var.value = NULL;
818         var.key = "picodrive_sprlim";
819         if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
820                 if (strcmp(var.value, "enabled") == 0)
821                         PicoOpt |= POPT_DIS_SPRITE_LIM;
822                 else
823                         PicoOpt &= ~POPT_DIS_SPRITE_LIM;
824         }
825
826         var.value = NULL;
827         var.key = "picodrive_ramcart";
828         if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
829                 if (strcmp(var.value, "enabled") == 0)
830                         PicoOpt |= POPT_EN_MCD_RAMCART;
831                 else
832                         PicoOpt &= ~POPT_EN_MCD_RAMCART;
833         }
834
835 #ifdef DRC_SH2
836         var.value = NULL;
837         var.key = "picodrive_drc";
838         if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
839                 if (strcmp(var.value, "enabled") == 0)
840                         PicoOpt |= POPT_EN_DRC;
841                 else
842                         PicoOpt &= ~POPT_EN_DRC;
843         }
844 #endif
845 }
846
847 void retro_run(void) 
848 {
849         bool updated = false;
850         int pad, i;
851
852         if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated)
853                 update_variables();
854
855         input_poll_cb();
856
857         PicoPad[0] = PicoPad[1] = 0;
858         for (pad = 0; pad < 2; pad++)
859                 for (i = 0; i < RETRO_PICO_MAP_LEN; i++)
860                         if (input_state_cb(pad, RETRO_DEVICE_JOYPAD, 0, i))
861                                 PicoPad[pad] |= retro_pico_map[i];
862
863         PicoFrame();
864
865         video_cb((short *)vout_buf + vout_offset,
866                 vout_width, vout_height, vout_width * 2);
867 }
868
869 void retro_init(void)
870 {
871    struct retro_log_callback log;
872         int level;
873
874         level = 0;
875         environ_cb(RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL, &level);
876
877    if (environ_cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log))
878       log_cb = log.log;
879    else
880       log_cb = NULL;
881
882         environ_cb(RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE, &disk_control);
883
884         PicoOpt = POPT_EN_STEREO|POPT_EN_FM|POPT_EN_PSG|POPT_EN_Z80
885                 | POPT_EN_MCD_PCM|POPT_EN_MCD_CDDA|POPT_EN_MCD_GFX
886                 | POPT_EN_32X|POPT_EN_PWM
887                 | POPT_ACC_SPRITES|POPT_DIS_32C_BORDER;
888 #ifdef __arm__
889         PicoOpt |= POPT_EN_DRC;
890 #endif
891         PsndRate = 44100;
892         PicoAutoRgnOrder = 0x184; // US, EU, JP
893
894         vout_width = 320;
895         vout_height = 240;
896         vout_buf = malloc(VOUT_MAX_WIDTH * VOUT_MAX_HEIGHT * 2);
897
898         PicoInit();
899         PicoDrawSetOutFormat(PDF_RGB555, 0);
900         PicoDrawSetOutBuf(vout_buf, vout_width * 2);
901
902         //PicoMessage = plat_status_msg_busy_next;
903         PicoMCDopenTray = disk_tray_open;
904         PicoMCDcloseTray = disk_tray_close;
905
906         update_variables();
907 }
908
909 void retro_deinit(void)
910 {
911         PicoExit();
912 }