2 * libretro core glue for PicoDrive
5 * (C) Daniel De Matteis, 2013
6 * (C) irixxxx, 2020-2024
8 * This work is licensed under the terms of MAME license.
9 * See COPYING file in the top-level directory.
12 #define _GNU_SOURCE 1 // mremap
19 #include <libkern/OSCacheControl.h>
22 #include "libretro-common/include/formats/image.h" // really, for IMAGE_PROCESS_NEXT?!?
23 #include "libretro-common/include/formats/rpng.h"
24 #include "libretro-common/include/file/file_path.h"
26 #include "libretro-common/include/memmap.h"
27 /* Ouf, libretro-common defines replacement functions, but not the flags :-| */
30 #define PROT_WRITE 0x2
31 #define PROT_READWRITE 0x3
33 #define MAP_FAILED ((void *) -1)
34 #define MAP_ANONYMOUS 0x1
35 #define MAP_PRIVATE 0x2
38 #if defined(RENDER_GSKIT_PS2)
41 #include "libretro-common/include/libretro_gskit_ps2.h"
44 #include <platform/common/upscale.h>
46 #include <platform/common/emu.h>
47 #include <platform/libpicofe/plat.h> // need this for PXMAKE in readpng :-/
50 #include "3ds/3ds_utils.h"
55 int svcDuplicateHandle(unsigned int* out, unsigned int original);
56 int svcCloseHandle(unsigned int handle);
57 int svcControlProcessMemory(unsigned int process, void* addr0, void* addr1,
58 unsigned int size, unsigned int type, unsigned int perm);
59 void* linearMemAlign(size_t size, size_t alignment);
60 void linearFree(void* mem);
62 static int ctr_svchack_successful = 0;
65 #define TARGET_SIZE_2 24 // 2^24 = 16 megabytes
67 #include <psp2/kernel/sysmem.h>
70 int _newlib_vm_size_user = 1 << TARGET_SIZE_2;
72 #elif defined(__PS3__)
73 #include <sys/process.h>
74 #include <ps3mapi_ps3_lib.h>
76 static uint64_t page_table[2] = {0, 0};
79 #include "libretro_core_options.h"
81 #include <pico/pico_int.h>
82 #include <pico/state.h>
83 #include <pico/patch.h>
84 #include <pico/sound/mix.h>
85 #include "../common/input_pico.h"
86 #include "../common/version.h"
88 #include <compat/strcasestr.h>
90 static retro_log_printf_t log_cb;
91 static retro_video_refresh_t video_cb;
92 static retro_input_poll_t input_poll_cb;
93 static retro_input_state_t input_state_cb;
94 static retro_environment_t environ_cb;
95 static retro_audio_sample_batch_t audio_batch_cb;
97 #define VOUT_MAX_WIDTH 320
98 #define VOUT_MAX_HEIGHT 240
100 #define SND_RATE_DEFAULT 44100
101 #define SND_RATE_MAX 53267
104 #define PATH_MAX 4096
107 static const float VOUT_PAR = 0.0;
108 static const float VOUT_4_3 = (4.0f / 3.0f);
109 static const float VOUT_CRT = (1.29911f);
111 /* Required to allow on the fly changes to 'renderer' */
112 static int vm_current_start_line = -1;
113 static int vm_current_line_count = -1;
114 static int vm_current_start_col = -1;
115 static int vm_current_col_count = -1;
117 static int vout_16bit = 1;
118 static int vout_format = PDF_RGB555;
119 static void *vout_buf, *vout_ghosting_buf;
120 static int vout_width, vout_height, vout_offset;
121 static float vout_aspect = 0.0;
122 static int vout_ghosting = 0;
124 static bool libretro_update_av_info = false;
125 static bool libretro_update_geometry = false;
127 #if defined(RENDER_GSKIT_PS2)
128 #define VOUT_8BIT_WIDTH 328
129 #define VOUT_8BIT_HEIGHT 256
130 RETRO_HW_RENDER_INTEFACE_GSKIT_PS2 *ps2 = NULL;
131 static void *retro_palette;
132 static struct retro_hw_ps2_insets padding;
135 static short ALIGNED(4) sndBuffer[2*SND_RATE_MAX/50];
137 static void snd_write(int len);
147 /* Frameskipping Support */
149 static unsigned frameskip_type = 0;
150 static unsigned frameskip_threshold = 0;
151 static uint16_t frameskip_counter = 0;
153 static bool retro_audio_buff_active = false;
154 static unsigned retro_audio_buff_occupancy = 0;
155 static bool retro_audio_buff_underrun = false;
156 /* Maximum number of consecutive frames that
158 #define FRAMESKIP_MAX 60
160 static unsigned audio_latency = 0;
161 static bool update_audio_latency = false;
162 static uint16_t pico_events;
165 int pico_pen_x = 320/2, pico_pen_y = 240/2;
166 static int pico_page;
167 static int pico_w, pico_h;
168 static char pico_overlay_path[PATH_MAX];
169 static unsigned short *pico_overlay;
172 static void retro_audio_buff_status_cb(
173 bool active, unsigned occupancy, bool underrun_likely)
175 retro_audio_buff_active = active;
176 retro_audio_buff_occupancy = occupancy;
177 retro_audio_buff_underrun = underrun_likely;
180 static void init_frameskip(void)
182 if (frameskip_type > 0)
184 struct retro_audio_buffer_status_callback buf_status_cb;
186 buf_status_cb.callback = retro_audio_buff_status_cb;
187 if (!environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK,
191 log_cb(RETRO_LOG_WARN, "Frameskip disabled - frontend does not support audio buffer status monitoring.\n");
193 retro_audio_buff_active = false;
194 retro_audio_buff_occupancy = 0;
195 retro_audio_buff_underrun = false;
200 /* Frameskip is enabled - increase frontend
201 * audio latency to minimise potential
202 * buffer underruns */
203 float frame_time_msec = 1000.0f / (Pico.m.pal ? 50.0f : 60.0f);
205 /* Set latency to 6x current frame time... */
206 audio_latency = (unsigned)((6.0f * frame_time_msec) + 0.5f);
208 /* ...then round up to nearest multiple of 32 */
209 audio_latency = (audio_latency + 0x1F) & ~0x1F;
214 environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK, NULL);
218 update_audio_latency = true;
221 /* functions called by the core */
223 void cache_flush_d_inval_i(void *start, void *end)
226 size_t len = (char *)end - (char *)start;
227 #if defined(__BLACKBERRY_QNX__)
228 msync(start, len, MS_SYNC | MS_CACHE_ONLY | MS_INVALIDATE_ICACHE);
229 #elif defined(__MACH__)
230 sys_dcache_flush(start, len);
231 sys_icache_invalidate(start, len);
234 ctr_flush_invalidate_cache();
236 sceKernelSyncVMDomain(sceBlock, start, len);
239 __clear_cache(start, end);
244 #ifdef RENDER_GSKIT_PS2
245 /* In PS2 toolchain these aren't yet defined */
246 void _flush_cache(void *b, void *e)
248 #if 0 /* which of these is overall faster for lots of small cache updates? */
251 FlushCache(0); /* WRITEBACK_DCACHE */
253 FlushCache(2); /* INVALIDATE_ICACHE */
256 int __builtin_parity(unsigned v)
258 /* credits to bit twiddling hacks, https://graphics.stanford.edu/~seander/bithacks.html */
262 return (0x6996 >> (v&0xf)) & 1;
265 int _flush_cache(char *addr, const int size, const int op)
267 //sceKernelDcacheWritebackAll();
268 sceKernelDcacheWritebackRange(addr, size);
269 sceKernelIcacheInvalidateRange(addr, size);
275 /* calls to this may be generated by the compiler, but it's missing in libc? */
276 void __clear_cache(void *start, void *end)
278 size_t len = (char *)end - (char *)start;
279 sys_dcache_flush(start, len);
280 sys_icache_invalidate(start, len);
284 #ifndef MAP_ANONYMOUS
285 #define MAP_ANONYMOUS MAP_ANON
291 unsigned int requested_map;
295 pico_mmap_t pico_mmaps[] = {
301 void *plat_mmap(unsigned long addr, size_t size, int need_exec, int is_fixed)
305 if (ctr_svchack_successful)
307 pico_mmap_t* pico_mmap;
309 for (pico_mmap = pico_mmaps; pico_mmap->requested_map; pico_mmap++)
311 if ((pico_mmap->requested_map == addr))
313 unsigned int ptr_aligned, tmp;
314 unsigned int currentHandle;
315 unsigned int perm = 0b011;
320 size = (size + 0xFFF) & ~0xFFF;
321 pico_mmap->buffer = malloc(size + 0x1000);
322 ptr_aligned = (((unsigned int)pico_mmap->buffer) + 0xFFF) & ~0xFFF;
324 svcDuplicateHandle(¤tHandle, 0xFFFF8001);
326 if(svcControlProcessMemory(currentHandle, pico_mmap->requested_map, ptr_aligned, size, MEMOP_MAP, perm) < 0)
329 log_cb(RETRO_LOG_ERROR, "could not map memory @0x%08X\n", pico_mmap->requested_map);
333 svcCloseHandle(currentHandle);
334 return (void*)pico_mmap->requested_map;
342 void *plat_mremap(void *ptr, size_t oldsize, size_t newsize)
344 if (ctr_svchack_successful)
346 pico_mmap_t* pico_mmap;
348 for (pico_mmap = pico_mmaps; pico_mmap->requested_map; pico_mmap++)
350 if ((pico_mmap->requested_map == (unsigned int)ptr))
352 unsigned int ptr_aligned;
353 unsigned int currentHandle;
356 oldsize = (oldsize + 0xFFF) & ~0xFFF;
357 newsize = (newsize + 0xFFF) & ~0xFFF;
358 ptr_aligned = (((unsigned int)pico_mmap->buffer) + 0xFFF) & ~0xFFF;
360 svcDuplicateHandle(¤tHandle, 0xFFFF8001);
362 svcControlProcessMemory(currentHandle, pico_mmap->requested_map, ptr_aligned, oldsize, MEMOP_UNMAP, 0b011);
364 tmp = realloc(pico_mmap->buffer, newsize + 0x1000);
368 pico_mmap->buffer = tmp;
369 ptr_aligned = (((unsigned int)pico_mmap->buffer) + 0xFFF) & ~0xFFF;
371 svcControlProcessMemory(currentHandle, pico_mmap->requested_map, ptr_aligned, newsize, MEMOP_MAP, 0x3);
373 svcCloseHandle(currentHandle);
380 return realloc(ptr, newsize);
383 void plat_munmap(void *ptr, size_t size)
385 if (ctr_svchack_successful)
387 pico_mmap_t* pico_mmap;
389 for (pico_mmap = pico_mmaps; pico_mmap->requested_map; pico_mmap++)
391 if ((pico_mmap->requested_map == (unsigned int)ptr))
393 unsigned int ptr_aligned;
394 unsigned int currentHandle;
396 size = (size + 0xFFF) & ~0xFFF;
397 ptr_aligned = (((unsigned int)pico_mmap->buffer) + 0xFFF) & ~0xFFF;
399 svcDuplicateHandle(¤tHandle, 0xFFFF8001);
401 svcControlProcessMemory(currentHandle, (void*)pico_mmap->requested_map, (void*)ptr_aligned, size, MEMOP_UNMAP, 0b011);
403 svcCloseHandle(currentHandle);
405 free(pico_mmap->buffer);
406 pico_mmap->buffer = NULL;
416 void *plat_mmap(unsigned long addr, size_t size, int need_exec, int is_fixed)
418 int flags = MAP_PRIVATE | MAP_ANONYMOUS;
421 req = (void *)(uintptr_t)addr;
422 ret = mmap(req, size, PROT_READ | PROT_WRITE, flags, -1, 0);
423 if (ret == MAP_FAILED) {
425 log_cb(RETRO_LOG_ERROR, "mmap(%08lx, %zd) failed: %d\n", addr, size, errno);
429 if (addr != 0 && ret != (void *)(uintptr_t)addr) {
431 log_cb(RETRO_LOG_WARN, "warning: wanted to map @%08lx, got %p\n",
443 void *plat_mremap(void *ptr, size_t oldsize, size_t newsize)
445 #if defined(__linux__) && !defined(__SWITCH__)
446 void *ret = mremap(ptr, oldsize, newsize, 0);
447 if (ret == MAP_FAILED)
453 size_t preserve_size;
455 preserve_size = oldsize;
456 if (preserve_size > newsize)
457 preserve_size = newsize;
458 tmp = malloc(preserve_size);
461 memcpy(tmp, ptr, preserve_size);
463 munmap(ptr, oldsize);
464 ret = mmap(ptr, newsize, PROT_READ | PROT_WRITE,
465 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
466 if (ret == MAP_FAILED) {
470 memcpy(ret, tmp, preserve_size);
476 void plat_munmap(void *ptr, size_t size)
483 // if NULL is returned, static buffer is used
484 void *plat_mem_get_for_drc(size_t size)
488 sceKernelGetMemBlockBase(sceBlock, &mem);
490 // For WiiU, a slice of RWX memory left from the exploit is used, see:
491 // https://github.com/embercold/pcsx_rearmed/commit/af0453223
492 mem = (void *)(0x01000000 - size);
493 #elif defined __PS3__
494 ps3mapi_process_page_allocate(sysProcessGetPid(), size, PAGE_SIZE_AUTO, 0x2F, 1, page_table);
495 mem = (void *)page_table[0];
500 int plat_mem_set_exec(void *ptr, size_t size)
504 DWORD oldProtect = 0;
505 ret = VirtualProtect(ptr, size, PAGE_EXECUTE_READWRITE, &oldProtect);
506 if (ret == 0 && log_cb)
507 log_cb(RETRO_LOG_ERROR, "VirtualProtect(%p, %d) failed: %d\n", ptr, (int)size,
510 if (ctr_svchack_successful)
512 unsigned int currentHandle;
513 svcDuplicateHandle(¤tHandle, 0xFFFF8001);
514 ret = svcControlProcessMemory(currentHandle, ptr, 0x0,
515 size, MEMOP_PROT, 0b111);
516 svcCloseHandle(currentHandle);
517 ctr_flush_invalidate_cache();
523 log_cb(RETRO_LOG_ERROR, "plat_mem_set_exec called with no svcControlProcessMemory access\n");
528 ret = sceKernelOpenVMDomain();
530 ret = mprotect(ptr, size, PROT_READ | PROT_WRITE | PROT_EXEC);
531 if (ret != 0 && log_cb)
532 log_cb(RETRO_LOG_ERROR, "mprotect(%p, %zd) failed: %d\n", ptr, size, errno);
537 static void apply_renderer()
539 PicoIn.opt &= ~(POPT_ALT_RENDERER|POPT_EN_SOFTSCALE);
540 PicoIn.opt |= POPT_DIS_32C_BORDER;
541 if (vout_format == PDF_NONE)
542 PicoIn.opt |= POPT_ALT_RENDERER;
543 PicoDrawSetOutFormat(vout_format, 0);
544 if (!vout_16bit && vout_format == PDF_8BIT)
545 PicoDrawSetOutBuf(Pico.est.Draw2FB, 328);
548 void emu_video_mode_change(int start_line, int line_count, int start_col, int col_count)
550 vm_current_start_line = start_line;
551 vm_current_line_count = line_count;
552 vm_current_start_col = start_col;
553 vm_current_col_count = col_count;
555 // 8bit renderers create a 328x256 CLUT image, 16bit creates 320x240 RGB
556 #if defined(RENDER_GSKIT_PS2)
557 // calculate the borders of the real image inside the picodrive image
558 vout_width = (vout_16bit ? VOUT_MAX_WIDTH : VOUT_8BIT_WIDTH);
559 vout_height = (vout_16bit ? VOUT_MAX_HEIGHT : VOUT_8BIT_HEIGHT);
560 vout_offset = (vout_16bit ? 0 : col_count == 248 ? 16 : 8); // 8bit has overlap area on the left
561 padding = (struct retro_hw_ps2_insets){start_line, vout_offset, vout_height - line_count - start_line, vout_width - col_count - vout_offset};
563 int pxsz = (vout_16bit ? 2 : 1); // pixel size: RGB = 16 bits, CLUT = 8 bits
564 memset(vout_buf, 0, pxsz * vout_width * vout_height);
565 memset(retro_palette, 0, gsKit_texture_size_ee(16, 16, GS_PSM_CT16));
566 PicoDrawSetOutBuf(vout_buf, pxsz * vout_width);
568 // prepare image as texture for rendering
569 ps2->coreTexture->Width = vout_width;
570 ps2->coreTexture->Height = vout_height;
571 ps2->coreTexture->PSM = (vout_16bit ? GS_PSM_CT16 : GS_PSM_T8);
572 ps2->padding = padding;
575 vout_width = col_count;
576 memset(vout_buf, 0, VOUT_MAX_WIDTH * VOUT_MAX_HEIGHT * 2);
578 PicoDrawSetOutBuf(vout_buf, vout_width * 2);
580 vout_height = line_count;
581 /* Note: We multiply by 2 here to account for pitch */
582 vout_offset = vout_width * start_line * 2;
584 /* Redundant sanity check... */
585 vout_height = (vout_height > VOUT_MAX_HEIGHT) ?
586 VOUT_MAX_HEIGHT : vout_height;
587 vout_offset = (vout_offset > vout_width * (VOUT_MAX_HEIGHT - 1) * 2) ?
588 vout_width * (VOUT_MAX_HEIGHT - 1) * 2 : vout_offset;
591 if (vout_ghosting && vout_height == 144) {
592 vout_ghosting_buf = realloc(vout_ghosting_buf, VOUT_MAX_HEIGHT*vout_width*2);
593 memset(vout_ghosting_buf, 0, vout_width*vout_height*2);
598 /* Notify frontend of geometry update */
599 libretro_update_geometry = true;
602 void emu_32x_startup(void)
604 PicoIn.filter = EOPT_FILTER_SMOOTHER; // for H32 upscaling
605 PicoDrawSetOutFormat(vout_format, 0);
609 (vm_current_start_line != -1) && (vm_current_line_count != -1) &&
610 (vm_current_start_col != -1) && (vm_current_col_count != -1))
611 emu_video_mode_change(
612 vm_current_start_line, vm_current_line_count,
613 vm_current_start_col, vm_current_col_count);
616 void lprintf(const char *fmt, ...)
621 vsprintf(buffer, fmt, ap);
622 /* TODO - add 'level' param for warning/error messages? */
624 log_cb(RETRO_LOG_INFO, "%s", buffer);
629 bool libretro_supports_bitmasks = false;
631 void retro_set_environment(retro_environment_t cb)
633 bool option_categories_supported;
634 #ifdef USE_LIBRETRO_VFS
635 struct retro_vfs_interface_info vfs_iface_info;
638 static const struct retro_system_content_info_override content_overrides[] = {
640 "bin|gen|smd|md|32x|sms|gg|sg|sc|68k|sgd|pco", /* extensions */
641 #if defined(LOW_MEMORY)
642 true, /* need_fullpath */
644 false, /* need_fullpath */
646 false /* persistent_data */
648 { NULL, false, false }
653 libretro_set_core_options(environ_cb,
654 &option_categories_supported);
655 environ_cb(RETRO_ENVIRONMENT_SET_CONTENT_INFO_OVERRIDE,
656 (void*)content_overrides);
658 #ifdef USE_LIBRETRO_VFS
659 vfs_iface_info.required_interface_version = 1;
660 vfs_iface_info.iface = NULL;
661 if (environ_cb(RETRO_ENVIRONMENT_GET_VFS_INTERFACE, &vfs_iface_info))
662 filestream_vfs_init(&vfs_iface_info);
666 void retro_set_video_refresh(retro_video_refresh_t cb) { video_cb = cb; }
667 void retro_set_audio_sample(retro_audio_sample_t cb) { (void)cb; }
668 void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) { audio_batch_cb = cb; }
669 void retro_set_input_poll(retro_input_poll_t cb) { input_poll_cb = cb; }
670 void retro_set_input_state(retro_input_state_t cb) { input_state_cb = cb; }
672 unsigned retro_api_version(void)
674 return RETRO_API_VERSION;
677 void retro_set_controller_port_device(unsigned port, unsigned device)
681 void retro_get_system_info(struct retro_system_info *info)
683 memset(info, 0, sizeof(*info));
684 info->library_name = "PicoDrive";
685 info->library_version = VERSION;
686 info->valid_extensions = "bin|gen|smd|md|32x|cue|iso|chd|sms|gg|sg|sc|m3u|68k|sgd|pco";
687 info->need_fullpath = true;
690 void retro_get_system_av_info(struct retro_system_av_info *info)
692 float tv_height = (vout_height > 144 ? Pico.m.pal ? 240 : 224 : 144);
695 memset(info, 0, sizeof(*info));
696 info->timing.fps = Pico.m.pal ? 50 : 60;
697 info->timing.sample_rate = PicoIn.sndRate;
698 info->geometry.base_width = vout_width;
699 info->geometry.base_height = vout_height;
700 info->geometry.max_width = vout_width;
701 info->geometry.max_height = vout_height;
703 common_width = vout_width;
704 if (vout_aspect != 0)
705 common_width = vout_aspect * tv_height;
707 info->geometry.aspect_ratio = common_width / vout_height;
711 struct savestate_state {
712 const char *load_buf;
718 size_t state_read(void *p, size_t size, size_t nmemb, void *file)
720 struct savestate_state *state = file;
721 size_t bsize = size * nmemb;
723 if (state->pos + bsize > state->size) {
725 log_cb(RETRO_LOG_ERROR, "savestate error: %u/%u\n",
726 state->pos + bsize, state->size);
727 bsize = state->size - state->pos;
732 memcpy(p, state->load_buf + state->pos, bsize);
737 size_t state_write(void *p, size_t size, size_t nmemb, void *file)
739 struct savestate_state *state = file;
740 size_t bsize = size * nmemb;
742 if (state->pos + bsize > state->size) {
744 log_cb(RETRO_LOG_ERROR, "savestate error: %u/%u\n",
745 state->pos + bsize, state->size);
746 bsize = state->size - state->pos;
751 memcpy(state->save_buf + state->pos, p, bsize);
756 size_t state_skip(void *p, size_t size, size_t nmemb, void *file)
758 struct savestate_state *state = file;
759 size_t bsize = size * nmemb;
765 size_t state_eof(void *file)
767 struct savestate_state *state = file;
769 return state->pos >= state->size;
772 int state_fseek(void *file, long offset, int whence)
774 struct savestate_state *state = file;
781 state->pos += offset;
784 state->pos = state->size + offset;
787 return (int)state->pos;
790 /* savestate sizes vary wildly depending if cd/32x or
791 * carthw is active, so run the whole thing to get size */
792 size_t retro_serialize_size(void)
794 struct savestate_state state = { 0, };
795 unsigned AHW = PicoIn.AHW;
798 /* we need the max possible size here, so include 32X for MD and MCD */
799 if (!(AHW & (PAHW_SMS|PAHW_PICO|PAHW_SVP)))
800 PicoIn.AHW |= PAHW_32X;
801 ret = PicoStateFP(&state, 1, NULL, state_skip, NULL, state_fseek);
809 bool retro_serialize(void *data, size_t size)
811 struct savestate_state state = { 0, };
814 state.save_buf = data;
818 ret = PicoStateFP(&state, 1, NULL, state_write,
823 bool retro_unserialize(const void *data, size_t size)
825 struct savestate_state state = { 0, };
828 state.load_buf = data;
832 ret = PicoStateFP(&state, 0, state_read, NULL,
833 state_eof, state_fseek);
844 extern void decode(char *buff, patch *dest);
845 extern uint32_t m68k_read16(uint32_t a);
846 extern void m68k_write16(uint32_t a, uint16_t d);
848 void retro_cheat_reset(void)
853 for (i = 0; i < PicoPatchCount; i++)
855 addr = PicoPatches[i].addr;
856 if (addr < Pico.romsize) {
857 if (PicoPatches[i].active)
858 *(unsigned short *)(Pico.rom + addr) = PicoPatches[i].data_old;
860 if (PicoPatches[i].active)
861 m68k_write16(PicoPatches[i].addr,PicoPatches[i].data_old);
868 void retro_cheat_set(unsigned index, bool enabled, const char *code)
871 int array_len = PicoPatchCount;
877 strcpy(codeCopy, code);
878 buff = strtok(codeCopy,"+");
883 if (pt.addr == (uint32_t) -1 || pt.data == (uint16_t) -1)
885 log_cb(RETRO_LOG_ERROR,"CHEATS: Invalid code: %s\n",buff);
889 /* code was good, add it */
890 if (array_len < PicoPatchCount + 1)
895 ptr = realloc(PicoPatches, array_len * sizeof(PicoPatches[0]));
897 log_cb(RETRO_LOG_ERROR,"CHEATS: Failed to allocate memory for: %s\n",buff);
902 strcpy(PicoPatches[PicoPatchCount].code, buff);
904 PicoPatches[PicoPatchCount].active = enabled;
905 PicoPatches[PicoPatchCount].addr = pt.addr;
906 PicoPatches[PicoPatchCount].data = pt.data;
907 PicoPatches[PicoPatchCount].comp = pt.comp;
908 if (PicoPatches[PicoPatchCount].addr < Pico.romsize)
909 PicoPatches[PicoPatchCount].data_old = *(uint16_t *)(Pico.rom + PicoPatches[PicoPatchCount].addr);
911 PicoPatches[PicoPatchCount].data_old = (uint16_t) m68k_read16(PicoPatches[PicoPatchCount].addr);
914 buff = strtok(NULL,"+");
918 /* multidisk support */
919 static unsigned int disk_initial_index;
920 static bool disk_ejected;
921 static unsigned int disk_current_index;
922 static unsigned int disk_count;
923 static char disk_initial_path[PATH_MAX];
924 static struct disks_state {
929 static void get_disk_label(char *disk_label, const char *disk_path, size_t len)
931 const char *base = NULL;
933 if (!disk_path || (*disk_path == '\0'))
936 base = strrchr(disk_path, SLASH);
943 strncpy(disk_label, base, len - 1);
944 disk_label[len - 1] = '\0';
946 char *ext = strrchr(disk_label, '.');
951 static void disk_init(void)
955 disk_ejected = false;
956 disk_current_index = 0;
959 for (i = 0; i < sizeof(disks) / sizeof(disks[0]); i++)
961 if (disks[i].fname != NULL)
963 free(disks[i].fname);
964 disks[i].fname = NULL;
966 if (disks[i].flabel != NULL)
968 free(disks[i].flabel);
969 disks[i].flabel = NULL;
974 static bool disk_set_eject_state(bool ejected)
977 disk_ejected = ejected;
981 static bool disk_get_eject_state(void)
986 static unsigned int disk_get_image_index(void)
988 return disk_current_index;
991 static bool disk_set_image_index(unsigned int index)
993 enum cd_track_type cd_type;
996 if (index >= sizeof(disks) / sizeof(disks[0]))
999 if (disks[index].fname == NULL) {
1001 log_cb(RETRO_LOG_ERROR, "missing disk #%u\n", index);
1003 // RetroArch specifies "no disk" with index == count,
1004 // so don't fail here..
1005 disk_current_index = index;
1010 log_cb(RETRO_LOG_INFO, "switching to disk %u: \"%s\"\n", index,
1011 disks[index].fname);
1014 if (PicoIn.AHW & PAHW_MCD) {
1015 cd_type = PicoCdCheck(disks[index].fname, NULL);
1016 if (cd_type >= 0 && cd_type != CT_UNKNOWN)
1017 ret = cdd_load(disks[index].fname, cd_type);
1021 log_cb(RETRO_LOG_ERROR, "Load failed, invalid CD image?\n");
1025 disk_current_index = index;
1029 static unsigned int disk_get_num_images(void)
1034 static bool disk_replace_image_index(unsigned index,
1035 const struct retro_game_info *info)
1037 char *old_fname = NULL;
1038 char *old_flabel = NULL;
1041 if (index >= sizeof(disks) / sizeof(disks[0]))
1044 old_fname = disks[index].fname;
1045 old_flabel = disks[index].flabel;
1047 if (disks[index].fname != NULL)
1048 free(disks[index].fname);
1049 disks[index].fname = NULL;
1051 if (disks[index].flabel != NULL)
1052 free(disks[index].flabel);
1053 disks[index].flabel = NULL;
1056 char disk_label[PATH_MAX];
1057 disk_label[0] = '\0';
1059 disks[index].fname = strdup(info->path);
1061 get_disk_label(disk_label, info->path, PATH_MAX);
1062 disks[index].flabel = strdup(disk_label);
1064 if (index == disk_current_index)
1065 ret = disk_set_image_index(index);
1068 if (old_fname != NULL)
1071 if (old_flabel != NULL)
1077 static bool disk_add_image_index(void)
1079 if (disk_count >= sizeof(disks) / sizeof(disks[0]))
1086 static bool disk_set_initial_image(unsigned index, const char *path)
1088 if (index >= sizeof(disks) / sizeof(disks[0]))
1091 if (!path || (*path == '\0'))
1094 disk_initial_index = index;
1096 strncpy(disk_initial_path, path, sizeof(disk_initial_path) - 1);
1097 disk_initial_path[sizeof(disk_initial_path) - 1] = '\0';
1102 static bool disk_get_image_path(unsigned index, char *path, size_t len)
1104 const char *fname = NULL;
1109 if (index >= sizeof(disks) / sizeof(disks[0]))
1112 fname = disks[index].fname;
1114 if (!fname || (*fname == '\0'))
1117 strncpy(path, fname, len - 1);
1118 path[len - 1] = '\0';
1123 static bool disk_get_image_label(unsigned index, char *label, size_t len)
1125 const char *flabel = NULL;
1130 if (index >= sizeof(disks) / sizeof(disks[0]))
1133 flabel = disks[index].flabel;
1135 if (!flabel || (*flabel == '\0'))
1138 strncpy(label, flabel, len - 1);
1139 label[len - 1] = '\0';
1144 static struct retro_disk_control_callback disk_control = {
1145 disk_set_eject_state,
1146 disk_get_eject_state,
1147 disk_get_image_index,
1148 disk_set_image_index,
1149 disk_get_num_images,
1150 disk_replace_image_index,
1151 disk_add_image_index,
1154 static struct retro_disk_control_ext_callback disk_control_ext = {
1155 .set_eject_state = disk_set_eject_state,
1156 .get_eject_state = disk_get_eject_state,
1157 .get_image_index = disk_get_image_index,
1158 .set_image_index = disk_set_image_index,
1159 .get_num_images = disk_get_num_images,
1160 .replace_image_index = disk_replace_image_index,
1161 .add_image_index = disk_add_image_index,
1162 .set_initial_image = disk_set_initial_image,
1163 .get_image_path = disk_get_image_path,
1164 .get_image_label = disk_get_image_label,
1167 static void disk_tray_open(void)
1170 log_cb(RETRO_LOG_INFO, "cd tray open\n");
1174 static void disk_tray_close(void)
1177 log_cb(RETRO_LOG_INFO, "cd tray close\n");
1181 static char base_dir[1024];
1183 static void extract_directory(char *buf, const char *path, size_t size)
1186 strncpy(buf, path, size - 1);
1187 buf[size - 1] = '\0';
1189 base = strrchr(buf, '/');
1191 base = strrchr(buf, '\\');
1202 static void extract_basename(char *buf, const char *path, size_t size)
1204 const char *base = strrchr(path, '/');
1206 base = strrchr(path, '\\');
1210 if (*base == '\\' || *base == '/')
1213 strncpy(buf, base, size - 1);
1214 buf[size - 1] = '\0';
1216 char *ext = strrchr(buf, '.');
1221 static bool read_m3u(const char *file)
1224 char name[PATH_MAX];
1225 FILE *f = fopen(file, "r");
1229 while (fgets(line, sizeof(line), f) && disk_count < sizeof(disks) / sizeof(disks[0]))
1233 char *carrige_return = strchr(line, '\r');
1235 *carrige_return = '\0';
1236 char *newline = strchr(line, '\n');
1240 if (line[0] != '\0')
1242 char disk_label[PATH_MAX];
1243 disk_label[0] = '\0';
1245 snprintf(name, sizeof(name), "%s%c%s", base_dir, SLASH, line);
1246 disks[disk_count].fname = strdup(name);
1248 get_disk_label(disk_label, name, PATH_MAX);
1249 disks[disk_count].flabel = strdup(disk_label);
1256 return (disk_count != 0);
1259 /* end of multi disk support */
1261 static const char * const biosfiles_us[] = {
1262 "us_scd2_9306", "SegaCDBIOS9303", "us_scd1_9210", "bios_CD_U"
1264 static const char * const biosfiles_eu[] = {
1265 "eu_mcd2_9306", "eu_mcd2_9303", "eu_mcd1_9210", "bios_CD_E"
1267 static const char * const biosfiles_jp[] = {
1268 "jp_mcd2_921222", "jp_mcd1_9112", "jp_mcd1_9111", "bios_CD_J"
1271 static void make_system_path(char *buf, size_t buf_size,
1272 const char *name, const char *ext)
1274 const char *dir = NULL;
1276 if (environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir) && dir) {
1277 snprintf(buf, buf_size, "%s%c%s%s", dir, SLASH, name, ext);
1280 snprintf(buf, buf_size, "%s%s", name, ext);
1284 static const char *find_bios(int *region, const char *cd_fname)
1286 const char * const *files;
1287 static char path[256];
1291 if (*region == 4) { // US
1292 files = biosfiles_us;
1293 count = sizeof(biosfiles_us) / sizeof(char *);
1294 } else if (*region == 8) { // EU
1295 files = biosfiles_eu;
1296 count = sizeof(biosfiles_eu) / sizeof(char *);
1297 } else if (*region == 1 || *region == 2) {
1298 files = biosfiles_jp;
1299 count = sizeof(biosfiles_jp) / sizeof(char *);
1304 for (i = 0; i < count; i++)
1306 make_system_path(path, sizeof(path), files[i], ".bin");
1307 f = fopen(path, "rb");
1311 make_system_path(path, sizeof(path), files[i], ".zip");
1312 f = fopen(path, "rb");
1319 log_cb(RETRO_LOG_INFO, "using bios: %s\n", path);
1327 static const char *find_msu(const char *cd_fname)
1329 static char path[256];
1333 // look for MSU.MD rom file. XXX another extension list? ugh...
1334 static const char *md_exts[] = { "gen", "smd", "md", "32x" };
1335 char *ext = strrchr(cd_fname, '.');
1336 int extpos = ext ? ext-cd_fname : strlen(cd_fname);
1337 strcpy(path, cd_fname);
1338 path[extpos++] = '.';
1339 for (i = 0; i < ARRAY_SIZE(md_exts); i++) {
1340 strcpy(path+extpos, md_exts[i]);
1341 f = fopen(path, "rb");
1343 log_cb(RETRO_LOG_INFO, "found MSU rom: %s\n", path);
1352 static void set_memory_maps(void)
1354 if (PicoIn.AHW & PAHW_MCD)
1356 const size_t SCD_BIT = 1ULL << 31ULL;
1357 const uint64_t mem = RETRO_MEMDESC_SYSTEM_RAM;
1358 struct retro_memory_map mmaps;
1359 struct retro_memory_descriptor descs[] = {
1360 { mem, PicoMem.ram, 0, 0xFF0000, 0, 0, 0x10000, "68KRAM" },
1361 /* virtual address using SCD_BIT so all 512M of prg_ram can be accessed */
1362 /* at address $80020000 */
1363 { mem, Pico_mcd->prg_ram, 0, SCD_BIT | 0x020000, 0, 0, 0x80000, "PRGRAM" },
1366 mmaps.descriptors = descs;
1367 mmaps.num_descriptors = sizeof(descs) / sizeof(descs[0]);
1368 environ_cb(RETRO_ENVIRONMENT_SET_MEMORY_MAPS, &mmaps);
1372 bool retro_load_game(const struct retro_game_info *info)
1374 const struct retro_game_info_ext *info_ext = NULL;
1375 const unsigned char *content_data = NULL;
1376 size_t content_size = 0;
1377 char content_path[PATH_MAX];
1378 char content_ext[8];
1379 char carthw_path[PATH_MAX];
1380 enum media_type_e media_type;
1389 content_path[0] = '\0';
1390 content_ext[0] = '\0';
1391 carthw_path[0] = '\0';
1393 unsigned int cd_index = 0;
1394 bool is_m3u = false;
1396 struct retro_input_descriptor desc[] = {
1397 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" },
1398 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" },
1399 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" },
1400 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
1401 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" },
1402 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "C" },
1403 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Y" },
1404 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "A" },
1405 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "X" },
1406 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "Z" },
1407 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,"Mode" },
1408 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
1410 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" },
1411 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" },
1412 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" },
1413 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
1414 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" },
1415 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "C" },
1416 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Y" },
1417 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "A" },
1418 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "X" },
1419 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "Z" },
1420 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,"Mode" },
1421 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
1424 { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" },
1425 { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" },
1426 { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" },
1427 { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
1428 { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" },
1429 { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "C" },
1430 { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Y" },
1431 { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "A" },
1432 { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "X" },
1433 { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "Z" },
1434 { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,"Mode" },
1435 { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
1438 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" },
1439 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" },
1440 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" },
1441 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
1442 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" },
1443 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "C" },
1444 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Y" },
1445 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "A" },
1446 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "X" },
1447 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "Z" },
1448 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,"Mode" },
1449 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
1454 struct retro_input_descriptor desc_sms[] = {
1455 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" },
1456 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" },
1457 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" },
1458 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
1459 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "Button 1 Start" },
1460 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "Button 2" },
1461 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Button Pause" },
1463 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" },
1464 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" },
1465 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" },
1466 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
1467 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "Button 1 Start" },
1468 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "Button 2" },
1469 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Button Pause" },
1474 struct retro_input_descriptor desc_pico[] = {
1475 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left (violet)" },
1476 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up (white)" },
1477 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down (orange)" },
1478 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right (green)" },
1479 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "Red Button" },
1480 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "Pen Button" },
1481 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Pen State" },
1482 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Pen on Storyware" },
1483 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Pen on Pad" },
1484 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "Previous Page" },
1485 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "Next Page" },
1490 /* Attempt to fetch extended game info */
1491 if (environ_cb(RETRO_ENVIRONMENT_GET_GAME_INFO_EXT, &info_ext))
1493 #if !defined(LOW_MEMORY)
1494 content_data = (const unsigned char *)info_ext->data;
1495 content_size = info_ext->size;
1497 strncpy(base_dir, info_ext->dir, sizeof(base_dir));
1498 base_dir[sizeof(base_dir) - 1] = '\0';
1500 strncpy(content_ext, info_ext->ext, sizeof(content_ext));
1501 content_ext[sizeof(content_ext) - 1] = '\0';
1503 if (info_ext->file_in_archive)
1505 /* We don't have a 'physical' file in this
1506 * case, but the core still needs a filename
1507 * in order to detect media type. We therefore
1508 * fake it, using the content directory,
1509 * canonical content name, and content file
1511 snprintf(content_path, sizeof(content_path), "%s%c%s.%s",
1512 base_dir, slash, info_ext->name, content_ext);
1516 strncpy(content_path, info_ext->full_path, sizeof(content_path));
1517 content_path[sizeof(content_path) - 1] = '\0';
1522 const char *ext = NULL;
1524 if (!info || !info->path)
1527 log_cb(RETRO_LOG_ERROR, "info->path required\n");
1531 extract_directory(base_dir, info->path, sizeof(base_dir));
1533 strncpy(content_path, info->path, sizeof(content_path));
1534 content_path[sizeof(content_path) - 1] = '\0';
1536 if ((ext = strrchr(info->path, '.')))
1538 strncpy(content_ext, ext + 1, sizeof(content_ext));
1539 content_ext[sizeof(content_ext) - 1] = '\0';
1543 enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_RGB565;
1544 if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt)) {
1546 log_cb(RETRO_LOG_ERROR, "RGB565 support required, sorry\n");
1552 is_m3u = (strcasestr(content_ext, "m3u") != NULL);
1555 if (!read_m3u(content_path))
1557 log_cb(RETRO_LOG_INFO, "failed to read m3u file\n");
1561 strncpy(content_path, disks[0].fname, sizeof(content_path));
1562 content_path[sizeof(content_path) - 1] = '\0';
1566 char disk_label[PATH_MAX];
1567 disk_label[0] = '\0';
1569 disk_current_index = 0;
1571 disks[0].fname = strdup(content_path);
1573 get_disk_label(disk_label, content_path, PATH_MAX);
1574 disks[0].flabel = strdup(disk_label);
1577 /* If this is an M3U file, attempt to set the
1578 * initial disk image */
1579 if (is_m3u && (disk_initial_index > 0) && (disk_initial_index < disk_count))
1581 const char *fname = disks[disk_initial_index].fname;
1583 if (fname && (*fname != '\0'))
1584 if (strcmp(disk_initial_path, fname) == 0)
1585 cd_index = disk_initial_index;
1587 /* If we are not loading the first disk in the
1588 * M3U list, update the content_path string
1589 * that will be passed to PicoLoadMedia() */
1592 strncpy(content_path, disks[cd_index].fname, sizeof(content_path));
1593 content_path[sizeof(content_path) - 1] = '\0';
1597 make_system_path(carthw_path, sizeof(carthw_path), "carthw", ".cfg");
1599 media_type = PicoLoadMedia(content_path, content_data, content_size,
1600 carthw_path, find_bios, find_msu, NULL);
1602 disk_current_index = cd_index;
1604 switch (media_type) {
1607 log_cb(RETRO_LOG_ERROR, "Failed to detect ROM/CD image type.\n");
1611 log_cb(RETRO_LOG_ERROR, "Invalid CD image\n");
1613 case PM_BAD_CD_NO_BIOS:
1615 log_cb(RETRO_LOG_ERROR, "Missing BIOS\n");
1619 log_cb(RETRO_LOG_ERROR, "Load error\n");
1625 strncpy(pico_overlay_path, content_path, sizeof(pico_overlay_path)-4);
1626 if (PicoIn.AHW & PAHW_PICO)
1627 environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc_pico);
1628 else if (PicoIn.AHW & PAHW_SMS)
1629 environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc_sms);
1631 environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc);
1635 PicoIn.writeSound = snd_write;
1636 memset(sndBuffer, 0, sizeof(sndBuffer));
1637 PicoIn.sndOut = sndBuffer;
1638 if (PicoIn.sndRate > 52000 && PicoIn.sndRate < 54000)
1639 PicoIn.sndRate = YM2612_NATIVE_RATE();
1644 /* Setup retro memory maps */
1649 /* Initialisation routines may have 'triggered'
1650 * a libretro AV info or geometry update; this
1651 * happens automatically after retro_load_game(),
1652 * so disable the relevant flags here to avoid
1653 * redundant updates on the first call of
1655 libretro_update_av_info = false;
1656 libretro_update_geometry = false;
1661 bool retro_load_game_special(unsigned game_type, const struct retro_game_info *info, size_t num_info)
1666 void retro_unload_game(void)
1670 unsigned retro_get_region(void)
1672 return Pico.m.pal ? RETRO_REGION_PAL : RETRO_REGION_NTSC;
1675 void *retro_get_memory_data(unsigned type)
1681 case RETRO_MEMORY_SAVE_RAM:
1682 /* Note: MCD RAM cart uses Pico.sv.data */
1683 if ((PicoIn.AHW & PAHW_MCD) && Pico.romsize == 0 &&
1684 !(PicoIn.opt & POPT_EN_MCD_RAMCART))
1685 data = Pico_mcd->bram;
1686 else // TODO: handle copying to/from bram somewhere
1687 data = Pico.sv.data;
1689 case RETRO_MEMORY_SYSTEM_RAM:
1690 if (PicoIn.AHW & PAHW_SMS)
1691 data = PicoMem.zram;
1695 case RETRO_MEMORY_VIDEO_RAM:
1696 data = PicoMem.vram;
1699 data = PicoMem.cram;
1709 size_t retro_get_memory_size(unsigned type)
1716 case RETRO_MEMORY_SAVE_RAM:
1717 if ((PicoIn.AHW & PAHW_MCD) && Pico.romsize == 0)
1719 if (PicoIn.opt & POPT_EN_MCD_RAMCART)
1725 if (Pico.m.frame_count == 0)
1726 return Pico.sv.size;
1728 // if game doesn't write to sram, don't report it to
1729 // libretro so that RA doesn't write out zeroed .srm
1730 for (i = 0, sum = 0; i < Pico.sv.size; i++)
1731 sum |= Pico.sv.data[i];
1733 return (sum != 0) ? Pico.sv.size : 0;
1735 case RETRO_MEMORY_SYSTEM_RAM:
1736 if (PicoIn.AHW & PAHW_SMS)
1739 return sizeof(PicoMem.ram);
1747 void retro_reset(void)
1752 static const unsigned short retro_pico_map[] = {
1753 [RETRO_DEVICE_ID_JOYPAD_B] = 1 << GBTN_B,
1754 [RETRO_DEVICE_ID_JOYPAD_Y] = 1 << GBTN_A,
1755 [RETRO_DEVICE_ID_JOYPAD_SELECT] = 1 << GBTN_MODE,
1756 [RETRO_DEVICE_ID_JOYPAD_START] = 1 << GBTN_START,
1757 [RETRO_DEVICE_ID_JOYPAD_UP] = 1 << GBTN_UP,
1758 [RETRO_DEVICE_ID_JOYPAD_DOWN] = 1 << GBTN_DOWN,
1759 [RETRO_DEVICE_ID_JOYPAD_LEFT] = 1 << GBTN_LEFT,
1760 [RETRO_DEVICE_ID_JOYPAD_RIGHT] = 1 << GBTN_RIGHT,
1761 [RETRO_DEVICE_ID_JOYPAD_A] = 1 << GBTN_C,
1762 [RETRO_DEVICE_ID_JOYPAD_X] = 1 << GBTN_Y,
1763 [RETRO_DEVICE_ID_JOYPAD_L] = 1 << GBTN_X,
1764 [RETRO_DEVICE_ID_JOYPAD_R] = 1 << GBTN_Z,
1766 #define RETRO_PICO_MAP_LEN (sizeof(retro_pico_map) / sizeof(retro_pico_map[0]))
1768 static int has_4_pads;
1770 static void snd_write(int len)
1772 audio_batch_cb(PicoIn.sndOut, len / 4);
1775 static enum input_device input_name_to_val(const char *name)
1777 if (strcmp(name, "3 button pad") == 0)
1778 return PICO_INPUT_PAD_3BTN;
1779 if (strcmp(name, "6 button pad") == 0)
1780 return PICO_INPUT_PAD_6BTN;
1781 if (strcmp(name, "team player") == 0)
1782 return PICO_INPUT_PAD_TEAM;
1783 if (strcmp(name, "4way play") == 0)
1784 return PICO_INPUT_PAD_4WAY;
1785 if (strcmp(name, "None") == 0)
1786 return PICO_INPUT_NOTHING;
1789 log_cb(RETRO_LOG_WARN, "invalid picodrive_input: '%s'\n", name);
1790 return PICO_INPUT_PAD_3BTN;
1793 static void update_variables(bool first_run)
1795 struct retro_variable var;
1796 int OldPicoRegionOverride;
1797 float old_vout_aspect;
1798 unsigned old_frameskip_type;
1799 int old_vout_format;
1800 double new_sound_rate;
1801 unsigned short old_snd_filter;
1802 int32_t old_snd_filter_range;
1805 var.key = "picodrive_input1";
1806 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1807 int input = input_name_to_val(var.value);
1808 PicoSetInputDevice(0, input);
1809 has_4_pads = input == PICO_INPUT_PAD_TEAM || input == PICO_INPUT_PAD_4WAY;
1813 var.key = "picodrive_input2";
1814 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
1815 PicoSetInputDevice(1, input_name_to_val(var.value));
1818 var.key = "picodrive_ramcart";
1819 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1820 if (strcmp(var.value, "enabled") == 0)
1821 PicoIn.opt |= POPT_EN_MCD_RAMCART;
1823 PicoIn.opt &= ~POPT_EN_MCD_RAMCART;
1827 var.key = "picodrive_smstype";
1828 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1829 if (strcmp(var.value, "Auto") == 0)
1830 PicoIn.hwSelect = PHWS_AUTO;
1831 else if (strcmp(var.value, "Game Gear") == 0)
1832 PicoIn.hwSelect = PHWS_GG;
1833 else if (strcmp(var.value, "SG-1000") == 0)
1834 PicoIn.hwSelect = PHWS_SG;
1835 else if (strcmp(var.value, "SC-3000") == 0)
1836 PicoIn.hwSelect = PHWS_SC;
1838 PicoIn.hwSelect = PHWS_SMS;
1842 var.key = "picodrive_smsfm";
1843 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1844 if (strcmp(var.value, "on") == 0)
1845 PicoIn.opt |= POPT_EN_YM2413;
1847 PicoIn.opt &= ~POPT_EN_YM2413;
1851 var.key = "picodrive_smstms";
1852 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1853 if (strcmp(var.value, "SG-1000") == 0)
1854 PicoIn.tmsPalette = 1;
1856 PicoIn.tmsPalette = 0;
1860 var.key = "picodrive_smsmapper";
1861 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1862 if (strcmp(var.value, "Auto") == 0)
1863 PicoIn.mapper = PMS_MAP_AUTO;
1864 else if (strcmp(var.value, "Codemasters") == 0)
1865 PicoIn.mapper = PMS_MAP_CODEM;
1866 else if (strcmp(var.value, "Korea") == 0)
1867 PicoIn.mapper = PMS_MAP_KOREA;
1868 else if (strcmp(var.value, "Korea MSX") == 0)
1869 PicoIn.mapper = PMS_MAP_MSX;
1870 else if (strcmp(var.value, "Korea X-in-1") == 0)
1871 PicoIn.mapper = PMS_MAP_N32K;
1872 else if (strcmp(var.value, "Korea 4-Pak") == 0)
1873 PicoIn.mapper = PMS_MAP_N16K;
1874 else if (strcmp(var.value, "Korea Janggun") == 0)
1875 PicoIn.mapper = PMS_MAP_JANGGUN;
1876 else if (strcmp(var.value, "Korea Nemesis") == 0)
1877 PicoIn.mapper = PMS_MAP_NEMESIS;
1878 else if (strcmp(var.value, "Taiwan 8K RAM") == 0)
1879 PicoIn.mapper = PMS_MAP_8KBRAM;
1881 PicoIn.mapper = PMS_MAP_SEGA;
1885 var.key = "picodrive_ggghost";
1886 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1887 if (strcmp(var.value, "normal") == 0)
1889 else if (strcmp(var.value, "weak") == 0)
1895 OldPicoRegionOverride = PicoIn.regionOverride;
1897 var.key = "picodrive_region";
1898 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1899 if (strcmp(var.value, "Auto") == 0)
1900 PicoIn.regionOverride = 0;
1901 else if (strcmp(var.value, "Japan NTSC") == 0)
1902 PicoIn.regionOverride = 1;
1903 else if (strcmp(var.value, "Japan PAL") == 0)
1904 PicoIn.regionOverride = 2;
1905 else if (strcmp(var.value, "US") == 0)
1906 PicoIn.regionOverride = 4;
1907 else if (strcmp(var.value, "Europe") == 0)
1908 PicoIn.regionOverride = 8;
1911 // Update region, fps and sound flags if needed
1912 if (Pico.rom && PicoIn.regionOverride != OldPicoRegionOverride)
1916 if (PicoIn.sndRate > 52000 && PicoIn.sndRate < 54000)
1917 PicoIn.sndRate = YM2612_NATIVE_RATE();
1918 PsndRerate(!first_run);
1921 old_vout_aspect = vout_aspect;
1923 var.key = "picodrive_aspect";
1924 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1925 if (strcmp(var.value, "4/3") == 0)
1926 vout_aspect = VOUT_4_3;
1927 else if (strcmp(var.value, "CRT") == 0)
1928 vout_aspect = VOUT_CRT;
1930 vout_aspect = VOUT_PAR;
1933 /* Notify frontend of geometry update */
1934 if (vout_aspect != old_vout_aspect)
1935 libretro_update_geometry = true;
1938 var.key = "picodrive_sprlim";
1939 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1940 if (strcmp(var.value, "enabled") == 0)
1941 PicoIn.opt |= POPT_DIS_SPRITE_LIM;
1943 PicoIn.opt &= ~POPT_DIS_SPRITE_LIM;
1947 var.key = "picodrive_overclk68k";
1948 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1949 PicoIn.overclockM68k = 0;
1950 if (var.value[0] == '+')
1951 PicoIn.overclockM68k = atoi(var.value + 1);
1956 var.key = "picodrive_drc";
1957 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1958 if (strcmp(var.value, "enabled") == 0)
1959 PicoIn.opt |= POPT_EN_DRC;
1961 PicoIn.opt &= ~POPT_EN_DRC;
1965 if(!ctr_svchack_successful)
1966 PicoIn.opt &= ~POPT_EN_DRC;
1970 var.key = "picodrive_dacnoise";
1971 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1972 if (strcmp(var.value, "enabled") == 0)
1973 PicoIn.opt |= POPT_EN_FM_DAC;
1975 PicoIn.opt &= ~POPT_EN_FM_DAC;
1979 var.key = "picodrive_fm_filter";
1980 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1981 if (strcmp(var.value, "on") == 0)
1982 PicoIn.opt |= POPT_EN_FM_FILTER;
1984 PicoIn.opt &= ~POPT_EN_FM_FILTER;
1987 old_snd_filter = PicoIn.opt & POPT_EN_SNDFILTER;
1989 var.key = "picodrive_audio_filter";
1990 PicoIn.opt &= ~POPT_EN_SNDFILTER;
1991 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1992 if (strcmp(var.value, "low-pass") == 0)
1993 PicoIn.opt |= POPT_EN_SNDFILTER;
1996 old_snd_filter_range = PicoIn.sndFilterAlpha;
1998 var.key = "picodrive_lowpass_range";
1999 PicoIn.sndFilterAlpha = (60 * 0x10000 / 100);
2000 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
2001 PicoIn.sndFilterAlpha = (atoi(var.value) * 0x10000 / 100);
2004 if (((old_snd_filter ^ PicoIn.opt) & POPT_EN_SNDFILTER) ||
2005 old_snd_filter_range != PicoIn.sndFilterAlpha) {
2006 mix_reset (PicoIn.opt & POPT_EN_SNDFILTER ? PicoIn.sndFilterAlpha : 0);
2009 old_frameskip_type = frameskip_type;
2011 var.key = "picodrive_frameskip";
2012 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
2013 if (strcmp(var.value, "auto") == 0)
2015 else if (strcmp(var.value, "manual") == 0)
2019 frameskip_threshold = 33;
2020 var.key = "picodrive_frameskip_threshold";
2022 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2023 frameskip_threshold = strtol(var.value, NULL, 10);
2025 old_vout_format = vout_format;
2027 var.key = "picodrive_renderer";
2028 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
2029 if (strcmp(var.value, "fast") == 0)
2030 vout_format = PDF_NONE;
2031 else if (strcmp(var.value, "good") == 0)
2032 vout_format = PDF_8BIT;
2033 else if (strcmp(var.value, "accurate") == 0)
2034 vout_format = PDF_RGB555;
2035 vout_16bit = vout_format == PDF_RGB555 || (PicoIn.AHW & PAHW_32X);
2041 var.key = "picodrive_sound_rate";
2042 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
2043 if (!strcmp(var.value, "native"))
2044 new_sound_rate = YM2612_NATIVE_RATE();
2046 new_sound_rate = atoi(var.value);
2047 if (new_sound_rate != PicoIn.sndRate) {
2048 /* Update the sound rate */
2049 PicoIn.sndRate = new_sound_rate;
2050 PsndRerate(!first_run);
2051 libretro_update_av_info = true;
2055 /* setup video if required */
2056 if (vout_format != old_vout_format)
2059 (vm_current_start_line != -1) && (vm_current_line_count != -1) &&
2060 (vm_current_start_col != -1) && (vm_current_col_count != -1))
2061 emu_video_mode_change(
2062 vm_current_start_line, vm_current_line_count,
2063 vm_current_start_col, vm_current_col_count);
2066 /* Reinitialise frameskipping, if required */
2067 if (((frameskip_type != old_frameskip_type) ||
2068 (Pico.rom && PicoIn.regionOverride != OldPicoRegionOverride)) &&
2073 void emu_status_msg(const char *format, ...)
2077 static char msg[512];
2079 memset (msg, 0, sizeof(msg));
2081 va_start(vl, format);
2082 ret = vsnprintf(msg, sizeof(msg), format, vl);
2085 static struct retro_message rmsg;
2088 environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE, &rmsg);
2091 static void draw_pico_ptr(void)
2093 int up = (PicoPicohw.pen_pos[0]|PicoPicohw.pen_pos[1]) & 0x8000;
2094 int x, y, pitch = vout_width;
2095 unsigned short *p = (unsigned short *)((char *)vout_buf + vout_offset);
2096 int o = (up ? 0x0000 : 0xffff), _ = (up ? 0xffff : 0x0000);
2097 // storyware pages are actually squished, 2:1
2098 int h = (pico_inp_mode == 1 ? 160 : vout_height);
2101 x = ((pico_pen_x * vout_width * ((1ULL<<32) / 320 + 1)) >> 32);
2102 y = ((pico_pen_y * h * ((1ULL<<32) / 224 + 1)) >> 32);
2105 p[-pitch-1] ^= o; p[-pitch] ^= _; p[-pitch+1] ^= _; p[-pitch+2] ^= o;
2106 p[-1] ^= _; p[0] ^= o; p[1] ^= o; p[2] ^= _;
2107 p[pitch-1] ^= _; p[pitch] ^= o; p[pitch+1] ^= o; p[pitch+2] ^= _;
2108 p[2*pitch-1]^= o; p[2*pitch]^= _; p[2*pitch+1]^= _; p[2*pitch+2]^= o;
2111 static int readpng(unsigned short *dest, const char *fname, int req_w, int req_h)
2113 rpng_t *rpng = rpng_alloc();
2114 FILE *pf = fopen(fname, "rb");
2115 void *png = NULL, *img = NULL;
2117 unsigned int x, y, w = req_w, h = req_h;
2121 lprintf("can't read png file %s", fname);
2125 // who designed this, reading the whole file for inflating, really?
2126 fseek(pf, 0, SEEK_END);
2128 fseek(pf, 0, SEEK_SET);
2129 if (!(png = malloc(len))) {
2130 lprintf("oom while reading png file %s", fname);
2133 fread(png, 1, len, pf);
2135 // again, who designed this? why all this superfluous iterating here?
2136 rpng_set_buf_ptr(rpng, png, len);
2138 while (rpng_iterate_image(rpng));
2140 ret = rpng_process_image(rpng, &img, len, &w, &h);
2141 } while (ret == IMAGE_PROCESS_NEXT);
2143 // there's already a scaled pngread in libpicofe, but who cares :-/
2144 if (img && rpng_is_valid(rpng)) {
2145 int x_scale = w*65536 / req_w;
2146 int y_scale = h*65536 / req_h;
2147 int x_ofs, y_ofs, x_pos, y_pos;
2149 if (x_scale < y_scale)
2151 else y_scale = x_scale;
2152 x_ofs = req_w - w*65536 / x_scale;
2153 y_ofs = req_h - h*65536 / y_scale;
2155 dest += y_ofs/2*req_w + x_ofs/2;
2156 for (y_pos = 0; y_pos < h*65536; y_pos += y_scale+1)
2158 unsigned char *src = (unsigned char *)img + 4*w*(y_pos >> 16);
2159 for (x_pos = 0, len = 0; x_pos < w*65536; x_pos += x_scale+1, len++)
2161 // to add insult to injury, rpng writes the image endian dependant!
2162 unsigned int d = *(unsigned int *)&src[4*(x_pos >> 16)];
2163 int r = d >> 16, g = d >> 8, b = d;
2164 *dest++ = PXMAKE(r & 0xff, g & 0xff, b & 0xff);
2166 dest += req_w - len;
2179 static unsigned short *load_pico_overlay(int page, int w, int h)
2181 static const char *pic_exts[] = { "png", "PNG" };
2182 char buffer[PATH_MAX];
2183 char *ext, *fname = NULL;
2186 if (pico_page == page && pico_w == w && pico_h == h)
2187 return pico_overlay;
2189 pico_w = w, pico_h = h;
2191 ext = strrchr(pico_overlay_path, '.');
2192 extpos = ext ? ext-pico_overlay_path : strlen(pico_overlay_path);
2193 strcpy(buffer, pico_overlay_path);
2194 buffer[extpos++] = '_';
2196 buffer[extpos++] = 'p';
2197 buffer[extpos++] = 'a';
2198 buffer[extpos++] = 'd';
2200 buffer[extpos++] = '0'+PicoPicohw.page;
2201 buffer[extpos++] = '.';
2203 for (i = 0; i < ARRAY_SIZE(pic_exts); i++) {
2204 strcpy(buffer+extpos, pic_exts[i]);
2205 if (path_is_valid(buffer) == RETRO_VFS_STAT_IS_VALID) {
2206 printf("found Pico file: %s\n", buffer);
2212 pico_overlay = realloc(pico_overlay, w*h*2);
2213 memset(pico_overlay, 0, w*h*2);
2214 if (!fname || !pico_overlay || readpng(pico_overlay, fname, w, h)) {
2217 pico_overlay = NULL;
2220 return pico_overlay;
2223 void emu_pico_overlay(u16 *pd, int w, int h, int pitch)
2225 u16 *overlay = NULL;
2229 if (pico_inp_mode == 1) {
2230 oh = (w/2 < h ? w/2 : h); // storyware has squished h
2231 overlay = load_pico_overlay(PicoPicohw.page, w, oh);
2232 } else if (pico_inp_mode == 2)
2233 overlay = load_pico_overlay(-1, w, oh);
2235 // copy overlay onto buffer
2237 for (y = 0; y < oh; y++)
2238 memcpy(pd + y*pitch, overlay + y*w, w*2);
2240 memset(pd + y*pitch, 0, w*2);
2244 void run_events_pico(unsigned int events)
2248 if (events & (1 << RETRO_DEVICE_ID_JOYPAD_L)) {
2250 if (PicoPicohw.page < 0)
2251 PicoPicohw.page = 0;
2252 emu_status_msg("Page %i", PicoPicohw.page);
2254 if (events & (1 << RETRO_DEVICE_ID_JOYPAD_R)) {
2256 if (PicoPicohw.page > 7)
2257 PicoPicohw.page = 7;
2258 if (PicoPicohw.page == 7) {
2259 // Used in games that require the Keyboard Pico peripheral
2260 emu_status_msg("Test Page");
2263 emu_status_msg("Page %i", PicoPicohw.page);
2266 if (events & (1 << RETRO_DEVICE_ID_JOYPAD_X)) {
2267 if (pico_inp_mode == 2) {
2269 emu_status_msg("Input: D-Pad");
2272 emu_status_msg("Input: Pen on Pad");
2275 if (events & (1 << RETRO_DEVICE_ID_JOYPAD_Y)) {
2276 if (pico_inp_mode == 1) {
2278 emu_status_msg("Input: D-Pad");
2281 emu_status_msg("Input: Pen on Storyware");
2284 if (events & (1 << RETRO_DEVICE_ID_JOYPAD_START)) {
2285 PicoPicohw.pen_pos[0] ^= 0x8000;
2286 PicoPicohw.pen_pos[1] ^= 0x8000;
2287 emu_status_msg("Pen %s", PicoPicohw.pen_pos[0] & 0x8000 ? "Up" : "Down");
2290 if ((PicoIn.pad[0] & 0x20) && pico_inp_mode && pico_overlay) {
2292 emu_status_msg("Input: D-Pad");
2294 if (pico_inp_mode == 0)
2297 /* handle other input modes */
2298 if (PicoIn.pad[0] & 1) pico_pen_y--;
2299 if (PicoIn.pad[0] & 2) pico_pen_y++;
2300 if (PicoIn.pad[0] & 4) pico_pen_x--;
2301 if (PicoIn.pad[0] & 8) pico_pen_x++;
2302 PicoIn.pad[0] &= ~0x0f; // release UDLR
2304 if (pico_pen_y < PICO_PEN_ADJUST_Y)
2305 pico_pen_y = PICO_PEN_ADJUST_Y;
2306 if (pico_pen_y > 223-1 - PICO_PEN_ADJUST_Y)
2307 pico_pen_y = 223-1 - PICO_PEN_ADJUST_Y;
2308 if (pico_pen_x < PICO_PEN_ADJUST_X)
2309 pico_pen_x = PICO_PEN_ADJUST_X;
2310 if (pico_pen_x > 319-1 - PICO_PEN_ADJUST_X)
2311 pico_pen_x = 319-1 - PICO_PEN_ADJUST_X;
2313 PicoPicohw.pen_pos[0] &= 0x8000;
2314 PicoPicohw.pen_pos[1] &= 0x8000;
2315 PicoPicohw.pen_pos[0] |= 0x3c + pico_pen_x;
2316 PicoPicohw.pen_pos[1] |= (pico_inp_mode == 1 ? 0x2f8 : 0x1fc) + pico_pen_y;
2319 void retro_run(void)
2321 bool updated = false;
2322 int pad, i, padcount;
2325 PicoIn.skipFrame = 0;
2327 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated)
2328 update_variables(false);
2332 PicoIn.pad[0] = PicoIn.pad[1] = PicoIn.pad[2] = PicoIn.pad[3] = 0;
2333 if (PicoIn.AHW & PAHW_PICO)
2335 else if (PicoIn.AHW & PAHW_SMS)
2338 padcount = has_4_pads ? 4 : 2;
2340 int16_t input[4] = {0, 0};
2342 if (libretro_supports_bitmasks)
2344 for (pad = 0; pad < padcount; pad++)
2346 input[pad] = input_state_cb(
2347 pad, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK);
2352 for (pad = 0; pad < padcount; pad++)
2354 for (i = 0; i < RETRO_PICO_MAP_LEN; i++)
2355 if (input_state_cb(pad, RETRO_DEVICE_JOYPAD, 0, i))
2356 input[pad] |= 1 << i;
2360 for (pad = 0; pad < padcount; pad++)
2361 for (i = 0; i < RETRO_PICO_MAP_LEN; i++)
2362 if (input[pad] & (1 << i))
2363 PicoIn.pad[pad] |= retro_pico_map[i];
2365 if (PicoIn.AHW == PAHW_PICO) {
2366 uint16_t ev = input[0] &
2367 ((1 << RETRO_DEVICE_ID_JOYPAD_L) | (1 << RETRO_DEVICE_ID_JOYPAD_R) |
2368 (1 << RETRO_DEVICE_ID_JOYPAD_X) | (1 << RETRO_DEVICE_ID_JOYPAD_Y) |
2369 (1 << RETRO_DEVICE_ID_JOYPAD_START));
2370 uint16_t new_ev = ev & ~pico_events;
2372 run_events_pico(new_ev);
2378 /* Check whether current frame should
2380 if ((frameskip_type > 0) && retro_audio_buff_active) {
2381 switch (frameskip_type)
2384 PicoIn.skipFrame = retro_audio_buff_underrun ? 1 : 0;
2386 case 2: /* manual */
2387 PicoIn.skipFrame = (retro_audio_buff_occupancy < frameskip_threshold) ? 1 : 0;
2390 PicoIn.skipFrame = 0;
2394 if (!PicoIn.skipFrame || (frameskip_counter >= FRAMESKIP_MAX)) {
2395 PicoIn.skipFrame = 0;
2396 frameskip_counter = 0;
2398 frameskip_counter++;
2401 /* If frameskip settings have changed, update
2402 * frontend audio latency */
2403 if (update_audio_latency) {
2404 environ_cb(RETRO_ENVIRONMENT_SET_MINIMUM_AUDIO_LATENCY,
2406 update_audio_latency = false;
2411 /* Check whether frontend needs to be notified
2412 * of timing/geometry changes */
2413 if (libretro_update_av_info || libretro_update_geometry) {
2414 struct retro_system_av_info av_info;
2415 retro_get_system_av_info(&av_info);
2416 environ_cb(libretro_update_av_info ?
2417 RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO :
2418 RETRO_ENVIRONMENT_SET_GEOMETRY,
2420 libretro_update_av_info = false;
2421 libretro_update_geometry = false;
2424 /* If frame was skipped, call video_cb() with
2425 * a NULL buffer and return immediately */
2426 if (PicoIn.skipFrame) {
2427 video_cb(NULL, vout_width, vout_height, vout_width * 2);
2431 #if defined(RENDER_GSKIT_PS2)
2432 buff = (uint32_t *)RETRO_HW_FRAME_BUFFER_VALID;
2435 // get access to the graphics hardware
2436 if (!environ_cb(RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE, (void **)&ps2) || !ps2) {
2437 printf("Failed to get HW rendering interface!\n");
2441 if (ps2->interface_version != RETRO_HW_RENDER_INTERFACE_GSKIT_PS2_VERSION) {
2442 printf("HW render interface mismatch, expected %u, got %u!\n",
2443 RETRO_HW_RENDER_INTERFACE_GSKIT_PS2_VERSION, ps2->interface_version);
2447 ps2->coreTexture->ClutPSM = GS_PSM_CT16;
2448 ps2->coreTexture->Filter = GS_FILTER_LINEAR;
2449 ps2->coreTexture->Clut = retro_palette;
2451 ps2->coreTexture->Mem = vout_buf;
2452 ps2->coreTexture->Width = vout_width;
2453 ps2->coreTexture->Height = vout_height;
2454 ps2->coreTexture->PSM = (vout_16bit ? GS_PSM_CT16 : GS_PSM_T8);
2455 ps2->padding = padding;
2458 // CLUT update needed?
2459 if (!vout_16bit && Pico.m.dirtyPal) {
2460 PicoDrawUpdateHighPal();
2462 // Rotate CLUT. PS2 is special since entries in CLUT are not in sequence.
2463 unsigned short int *pal=(void *)retro_palette;
2464 for (i = 0; i < 256; i+=8) {
2465 if ((i&0x18) == 0x08)
2466 memcpy(pal+i,Pico.est.HighPal+i+8,16);
2467 else if ((i&0x18) == 0x10)
2468 memcpy(pal+i,Pico.est.HighPal+i-8,16);
2470 memcpy(pal+i,Pico.est.HighPal+i,16);
2475 /* The 8 bit renderers write a CLUT image in Pico.est.Draw2FB, while libretro wants RGB in vout_buf.
2476 * We need to manually copy that to vout_buf, applying the CLUT on the way. Especially
2477 * with the fast renderer this is improving performance, at the expense of accuracy.
2479 /* This section is mostly copied from pemu_finalize_frame in platform/linux/emu.c */
2480 unsigned short *pd = (unsigned short *)((char *)vout_buf + vout_offset);
2481 /* Skip the leftmost 8 columns (it is used as an overlap area for rendering) */
2482 unsigned char *ps = Pico.est.Draw2FB + vm_current_start_line * 328 + 8;
2483 unsigned short *pal = Pico.est.HighPal;
2485 if (Pico.m.dirtyPal)
2486 PicoDrawUpdateHighPal();
2487 /* 8 bit renderers have an extra offset for SMS wíth 1st tile blanked */
2488 if (vout_width == 248)
2490 /* Copy, and skip the leftmost 8 columns again */
2491 for (i = 0; i < vout_height; i++, ps += 8) {
2492 for (x = 0; x < vout_width; x+=4) {
2498 ps += 320-vout_width; /* Advance to next line in case of 32col mode */
2502 if (vout_ghosting && vout_height == 144) {
2503 unsigned short *pd = (unsigned short *)vout_buf;
2504 unsigned short *ps = (unsigned short *)vout_ghosting_buf;
2506 for (y = 0; y < vout_height; y++) {
2507 if (vout_ghosting == 1)
2508 v_blend(pd, ps, vout_width, p_075_round);
2510 v_blend(pd, ps, vout_width, p_05_round);
2516 if (PicoIn.AHW & PAHW_PICO) {
2517 int h = vout_height, w = vout_width;
2518 unsigned short *pd = (unsigned short *)((char *)vout_buf + vout_offset);
2521 emu_pico_overlay(pd, w, h, vout_width);
2522 if (pico_inp_mode /*== 2 || overlay*/)
2526 buff = (char*)vout_buf + vout_offset;
2529 video_cb((short *)buff, vout_width, vout_height, vout_width * 2);
2532 void retro_init(void)
2534 unsigned dci_version = 0;
2535 struct retro_log_callback log;
2539 environ_cb(RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL, &level);
2541 if (environ_cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log))
2546 environ_cb(RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE, &disk_control);
2548 if (environ_cb(RETRO_ENVIRONMENT_GET_INPUT_BITMASKS, NULL))
2549 libretro_supports_bitmasks = true;
2551 disk_initial_index = 0;
2552 disk_initial_path[0] = '\0';
2553 if (environ_cb(RETRO_ENVIRONMENT_GET_DISK_CONTROL_INTERFACE_VERSION, &dci_version) && (dci_version >= 1))
2554 environ_cb(RETRO_ENVIRONMENT_SET_DISK_CONTROL_EXT_INTERFACE, &disk_control_ext);
2556 environ_cb(RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE, &disk_control);
2559 ctr_svchack_successful = ctr_svchack_init();
2562 sceBlock = getVMBlock();
2565 PicoIn.opt = POPT_EN_STEREO|POPT_EN_FM
2566 | POPT_EN_PSG|POPT_EN_Z80|POPT_EN_GG_LCD
2567 | POPT_EN_MCD_PCM|POPT_EN_MCD_CDDA|POPT_EN_MCD_GFX
2568 | POPT_EN_32X|POPT_EN_PWM
2569 | POPT_ACC_SPRITES|POPT_DIS_32C_BORDER;
2572 if (ctr_svchack_successful)
2574 PicoIn.opt |= POPT_EN_DRC;
2577 struct retro_variable var = { .key = "picodrive_sound_rate" };
2578 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2579 PicoIn.sndRate = atoi(var.value);
2581 PicoIn.sndRate = SND_RATE_DEFAULT;
2583 PicoIn.autoRgnOrder = 0x184; // US, EU, JP
2585 vout_width = VOUT_MAX_WIDTH;
2586 vout_height = VOUT_MAX_HEIGHT;
2588 vout_buf = linearMemAlign(VOUT_MAX_WIDTH * VOUT_MAX_HEIGHT * 2, 0x80);
2589 #elif defined(RENDER_GSKIT_PS2)
2590 vout_buf = memalign(4096, VOUT_MAX_WIDTH * VOUT_MAX_HEIGHT * 2);
2591 retro_palette = memalign(128, gsKit_texture_size_ee(16, 16, GS_PSM_CT16));
2593 vout_buf = malloc(VOUT_MAX_WIDTH * VOUT_MAX_HEIGHT * 2);
2598 //PicoIn.osdMessage = plat_status_msg_busy_next;
2599 PicoIn.mcdTrayOpen = disk_tray_open;
2600 PicoIn.mcdTrayClose = disk_tray_close;
2603 frameskip_threshold = 0;
2604 frameskip_counter = 0;
2605 retro_audio_buff_active = false;
2606 retro_audio_buff_occupancy = 0;
2607 retro_audio_buff_underrun = false;
2609 update_audio_latency = false;
2611 update_variables(true);
2614 void retro_deinit(void)
2623 linearFree(vout_buf);
2624 #elif defined(RENDER_GSKIT_PS2)
2626 free(retro_palette);
2628 #elif defined(__PS3__)
2630 if (page_table[0] > 0 && page_table[1] > 0)
2631 ps3mapi_process_page_free(sysProcessGetPid(), 0x2F, page_table);
2637 if (vout_ghosting_buf)
2638 free(vout_ghosting_buf);
2639 vout_ghosting_buf = NULL;
2642 pico_overlay = NULL;
2644 libretro_supports_bitmasks = false;