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