X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=platform%2Flibretro.c;h=72b7a1be126a84fedc4c10916dffcaff7d68681a;hb=f18d0e083ce89df07ef7a0c0cf5dc200638ad5d2;hp=40dc394e96b42e7adca43b1bcda9b253b09ccf28;hpb=2446536be520914616403876d7e49621ac6f4b95;p=picodrive.git diff --git a/platform/libretro.c b/platform/libretro.c index 40dc394..72b7a1b 100644 --- a/platform/libretro.c +++ b/platform/libretro.c @@ -6,39 +6,48 @@ * See COPYING file in the top-level directory. */ +#ifdef PSP +#define NO_MMAP +#endif + #define _GNU_SOURCE 1 // mremap #include #include #include +#ifndef _WIN32 +#ifndef NO_MMAP #include +#endif +#else +#include +#include +#include +#endif +#include #ifdef __MACH__ #include #endif #include +#include #include "common/input_pico.h" #include "common/version.h" #include "libretro.h" +static retro_log_printf_t log_cb; static retro_video_refresh_t video_cb; static retro_input_poll_t input_poll_cb; static retro_input_state_t input_state_cb; static retro_environment_t environ_cb; static retro_audio_sample_batch_t audio_batch_cb; -static FILE *emu_log; - #define VOUT_MAX_WIDTH 320 #define VOUT_MAX_HEIGHT 240 static void *vout_buf; -static int vout_width, vout_height; +static int vout_width, vout_height, vout_offset; static short __attribute__((aligned(4))) sndBuffer[2*44100/50]; -// FIXME: these 2 shouldn't be here -static unsigned char PicoDraw2FB_[(8+320) * (8+240+8)]; -unsigned char *PicoDraw2FB = PicoDraw2FB_; - static void snd_write(int len); #ifdef _WIN32 @@ -64,36 +73,188 @@ void cache_flush_d_inval_i(void *start, void *end) #endif } +#ifdef _WIN32 +/* mmap() replacement for Windows + * + * Author: Mike Frysinger + * Placed into the public domain + */ + +/* References: + * CreateFileMapping: http://msdn.microsoft.com/en-us/library/aa366537(VS.85).aspx + * CloseHandle: http://msdn.microsoft.com/en-us/library/ms724211(VS.85).aspx + * MapViewOfFile: http://msdn.microsoft.com/en-us/library/aa366761(VS.85).aspx + * UnmapViewOfFile: http://msdn.microsoft.com/en-us/library/aa366882(VS.85).aspx + */ + +#define PROT_READ 0x1 +#define PROT_WRITE 0x2 +/* This flag is only available in WinXP+ */ +#ifdef FILE_MAP_EXECUTE +#define PROT_EXEC 0x4 +#else +#define PROT_EXEC 0x0 +#define FILE_MAP_EXECUTE 0 +#endif + +#define MAP_SHARED 0x01 +#define MAP_PRIVATE 0x02 +#define MAP_ANONYMOUS 0x20 +#define MAP_ANON MAP_ANONYMOUS +#define MAP_FAILED ((void *) -1) + +#ifdef __USE_FILE_OFFSET64 +# define DWORD_HI(x) (x >> 32) +# define DWORD_LO(x) ((x) & 0xffffffff) +#else +# define DWORD_HI(x) (0) +# define DWORD_LO(x) (x) +#endif + +static void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset) +{ + if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC)) + return MAP_FAILED; + if (fd == -1) { + if (!(flags & MAP_ANON) || offset) + return MAP_FAILED; + } else if (flags & MAP_ANON) + return MAP_FAILED; + + DWORD flProtect; + if (prot & PROT_WRITE) { + if (prot & PROT_EXEC) + flProtect = PAGE_EXECUTE_READWRITE; + else + flProtect = PAGE_READWRITE; + } else if (prot & PROT_EXEC) { + if (prot & PROT_READ) + flProtect = PAGE_EXECUTE_READ; + else if (prot & PROT_EXEC) + flProtect = PAGE_EXECUTE; + } else + flProtect = PAGE_READONLY; + + off_t end = length + offset; + HANDLE mmap_fd, h; + if (fd == -1) + mmap_fd = INVALID_HANDLE_VALUE; + else + mmap_fd = (HANDLE)_get_osfhandle(fd); + h = CreateFileMapping(mmap_fd, NULL, flProtect, DWORD_HI(end), DWORD_LO(end), NULL); + if (h == NULL) + return MAP_FAILED; + + DWORD dwDesiredAccess; + if (prot & PROT_WRITE) + dwDesiredAccess = FILE_MAP_WRITE; + else + dwDesiredAccess = FILE_MAP_READ; + if (prot & PROT_EXEC) + dwDesiredAccess |= FILE_MAP_EXECUTE; + if (flags & MAP_PRIVATE) + dwDesiredAccess |= FILE_MAP_COPY; + void *ret = MapViewOfFile(h, dwDesiredAccess, DWORD_HI(offset), DWORD_LO(offset), length); + if (ret == NULL) { + CloseHandle(h); + ret = MAP_FAILED; + } + return ret; +} + +static void munmap(void *addr, size_t length) +{ + UnmapViewOfFile(addr); + /* ruh-ro, we leaked handle from CreateFileMapping() ... */ +} +#elif defined(NO_MMAP) +#define PROT_EXEC 0x04 +#define MAP_FAILED 0 +#define PROT_READ 0 +#define PROT_WRITE 0 +#define MAP_PRIVATE 0 +#define MAP_ANONYMOUS 0 + +void* mmap(void *desired_addr, size_t len, int mmap_prot, int mmap_flags, int fildes, size_t off) +{ + return malloc(len); +} + +void munmap(void *base_addr, size_t len) +{ + free(base_addr); +} + +int mprotect(void *addr, size_t len, int prot) +{ + /* stub - not really needed at this point since this codepath has no dynarecs */ + return 0; +} + +#endif + +#ifndef MAP_ANONYMOUS +#define MAP_ANONYMOUS MAP_ANON +#endif + void *plat_mmap(unsigned long addr, size_t size, int need_exec, int is_fixed) { - int flags = MAP_PRIVATE | MAP_ANONYMOUS; - void *req, *ret; + int flags = MAP_PRIVATE | MAP_ANONYMOUS; + void *req, *ret; - req = (void *)addr; - ret = mmap(req, size, PROT_READ | PROT_WRITE, flags, -1, 0); - if (ret == MAP_FAILED) - return NULL; + req = (void *)addr; + ret = mmap(req, size, PROT_READ | PROT_WRITE, flags, -1, 0); + if (ret == MAP_FAILED) { + if (log_cb) + log_cb(RETRO_LOG_ERROR, "mmap(%08lx, %zd) failed: %d\n", addr, size, errno); + return NULL; + } - if (addr != 0 && ret != (void *)addr) { - lprintf("warning: wanted to map @%08lx, got %p\n", - addr, ret); + if (addr != 0 && ret != (void *)addr) { + if (log_cb) + log_cb(RETRO_LOG_WARN, "warning: wanted to map @%08lx, got %p\n", + addr, ret); - if (is_fixed) { - munmap(ret, size); - return NULL; - } - } + if (is_fixed) { + munmap(ret, size); + return NULL; + } + } return ret; } void *plat_mremap(void *ptr, size_t oldsize, size_t newsize) { +#ifdef __linux__ void *ret = mremap(ptr, oldsize, newsize, 0); if (ret == MAP_FAILED) return NULL; return ret; +#else + void *tmp, *ret; + size_t preserve_size; + + preserve_size = oldsize; + if (preserve_size > newsize) + preserve_size = newsize; + tmp = malloc(preserve_size); + if (tmp == NULL) + return NULL; + memcpy(tmp, ptr, preserve_size); + + munmap(ptr, oldsize); + ret = mmap(ptr, newsize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (ret == MAP_FAILED) { + free(tmp); + return NULL; + } + memcpy(ret, tmp, preserve_size); + free(tmp); + return ret; +#endif } void plat_munmap(void *ptr, size_t size) @@ -102,51 +263,57 @@ void plat_munmap(void *ptr, size_t size) munmap(ptr, size); } +int plat_mem_set_exec(void *ptr, size_t size) +{ +#ifdef _WIN32 + int ret = VirtualProtect(ptr,size,PAGE_EXECUTE_READWRITE,0); + if (ret == 0 && log_cb) + log_cb(RETRO_LOG_ERROR, "mprotect(%p, %zd) failed: %d\n", ptr, size, 0); +#else + int ret = mprotect(ptr, size, PROT_READ | PROT_WRITE | PROT_EXEC); + if (ret != 0 && log_cb) + log_cb(RETRO_LOG_ERROR, "mprotect(%p, %zd) failed: %d\n", ptr, size, errno); +#endif + return ret; +} + void emu_video_mode_change(int start_line, int line_count, int is_32cols) { memset(vout_buf, 0, 320 * 240 * 2); vout_width = is_32cols ? 256 : 320; PicoDrawSetOutBuf(vout_buf, vout_width * 2); -} -void emu_32x_startup(void) -{ - PicoDrawSetOutFormat(PDF_RGB555, 1); + vout_height = line_count; + vout_offset = vout_width * start_line; } -#ifndef ANDROID - -void lprintf(const char *fmt, ...) +void emu_32x_startup(void) { - va_list list; - - va_start(list, fmt); - fprintf(emu_log, "PicoDrive: "); - vfprintf(emu_log, fmt, list); - va_end(list); - fflush(emu_log); } -#else - -#include - void lprintf(const char *fmt, ...) { - va_list list; - - va_start(list, fmt); - __android_log_vprint(ANDROID_LOG_INFO, "PicoDrive", fmt, list); - va_end(list); + char buffer[256]; + va_list ap; + va_start(ap, fmt); + vsprintf(buffer, fmt, ap); + /* TODO - add 'level' param for warning/error messages? */ + if (log_cb) + log_cb(RETRO_LOG_INFO, "%s\n", fmt); } -#endif - /* libretro */ void retro_set_environment(retro_environment_t cb) { static const struct retro_variable vars[] = { //{ "region", "Region; Auto|NTSC|PAL" }, + { "picodrive_input1", "Input device 1; 3 button pad|6 button pad|None" }, + { "picodrive_input2", "Input device 2; 3 button pad|6 button pad|None" }, + { "picodrive_sprlim", "No sprite limit; disabled|enabled" }, + { "picodrive_ramcart", "MegaCD RAM cart; disabled|enabled" }, +#ifdef DRC_SH2 + { "picodrive_drc", "Dynamic recompilers; enabled|disabled" }, +#endif { NULL, NULL }, }; @@ -175,7 +342,7 @@ void retro_get_system_info(struct retro_system_info *info) memset(info, 0, sizeof(*info)); info->library_name = "PicoDrive"; info->library_version = VERSION; - info->valid_extensions = "bin|gen|smd|32x|cue|iso|sms"; + info->valid_extensions = "bin|gen|smd|md|32x|cue|iso|sms"; info->need_fullpath = true; } @@ -185,26 +352,132 @@ void retro_get_system_av_info(struct retro_system_av_info *info) info->timing.fps = Pico.m.pal ? 50 : 60; info->timing.sample_rate = 44100; info->geometry.base_width = 320; - info->geometry.base_height = 240; + info->geometry.base_height = vout_height; info->geometry.max_width = VOUT_MAX_WIDTH; info->geometry.max_height = VOUT_MAX_HEIGHT; - info->geometry.aspect_ratio = 4.0 / 3.0; + info->geometry.aspect_ratio = 0.0f; +} + +/* savestates */ +struct savestate_state { + const char *load_buf; + char *save_buf; + size_t size; + size_t pos; +}; + +size_t state_read(void *p, size_t size, size_t nmemb, void *file) +{ + struct savestate_state *state = file; + size_t bsize = size * nmemb; + + if (state->pos + bsize > state->size) { + if (log_cb) + log_cb(RETRO_LOG_ERROR, "savestate error: %u/%u\n", + state->pos + bsize, state->size); + bsize = state->size - state->pos; + if ((int)bsize <= 0) + return 0; + } + + memcpy(p, state->load_buf + state->pos, bsize); + state->pos += bsize; + return bsize; +} + +size_t state_write(void *p, size_t size, size_t nmemb, void *file) +{ + struct savestate_state *state = file; + size_t bsize = size * nmemb; + + if (state->pos + bsize > state->size) { + if (log_cb) + log_cb(RETRO_LOG_ERROR, "savestate error: %u/%u\n", + state->pos + bsize, state->size); + bsize = state->size - state->pos; + if ((int)bsize <= 0) + return 0; + } + + memcpy(state->save_buf + state->pos, p, bsize); + state->pos += bsize; + return bsize; +} + +size_t state_skip(void *p, size_t size, size_t nmemb, void *file) +{ + struct savestate_state *state = file; + size_t bsize = size * nmemb; + + state->pos += bsize; + return bsize; +} + +size_t state_eof(void *file) +{ + struct savestate_state *state = file; + + return state->pos >= state->size; +} + +int state_fseek(void *file, long offset, int whence) +{ + struct savestate_state *state = file; + + switch (whence) { + case SEEK_SET: + state->pos = offset; + break; + case SEEK_CUR: + state->pos += offset; + break; + case SEEK_END: + state->pos = state->size + offset; + break; + } + return (int)state->pos; } -/* savestates - TODO */ +/* savestate sizes vary wildly depending if cd/32x or + * carthw is active, so run the whole thing to get size */ size_t retro_serialize_size(void) { - return 0; + struct savestate_state state = { 0, }; + int ret; + + ret = PicoStateFP(&state, 1, NULL, state_skip, NULL, state_fseek); + if (ret != 0) + return 0; + + return state.pos; } bool retro_serialize(void *data, size_t size) { - return false; + struct savestate_state state = { 0, }; + int ret; + + state.save_buf = data; + state.size = size; + state.pos = 0; + + ret = PicoStateFP(&state, 1, NULL, state_write, + NULL, state_fseek); + return ret == 0; } bool retro_unserialize(const void *data, size_t size) { - return false; + struct savestate_state state = { 0, }; + int ret; + + state.load_buf = data; + state.size = size; + state.pos = 0; + + ret = PicoStateFP(&state, 0, state_read, NULL, + state_eof, state_fseek); + return ret == 0; } /* cheats - TODO */ @@ -243,14 +516,15 @@ static unsigned int disk_get_image_index(void) static bool disk_set_image_index(unsigned int index) { - cd_img_type cd_type; + enum cd_img_type cd_type; int ret; if (index >= sizeof(disks) / sizeof(disks[0])) return false; if (disks[index].fname == NULL) { - lprintf("missing disk #%u\n", index); + if (log_cb) + log_cb(RETRO_LOG_ERROR, "missing disk #%u\n", index); // RetroArch specifies "no disk" with index == count, // so don't fail here.. @@ -258,15 +532,17 @@ static bool disk_set_image_index(unsigned int index) return true; } - lprintf("switching to disk %u: \"%s\"\n", index, - disks[index].fname); + if (log_cb) + log_cb(RETRO_LOG_INFO, "switching to disk %u: \"%s\"\n", index, + disks[index].fname); ret = -1; cd_type = PicoCdCheck(disks[index].fname, NULL); if (cd_type != CIT_NOT_CD) - ret = Insert_CD(disks[index].fname, cd_type); + ret = cdd_load(disks[index].fname, cd_type); if (ret != 0) { - lprintf("Load failed, invalid CD image?\n"); + if (log_cb) + log_cb(RETRO_LOG_ERROR, "Load failed, invalid CD image?\n"); return 0; } @@ -321,25 +597,27 @@ static struct retro_disk_control_callback disk_control = { static void disk_tray_open(void) { - lprintf("cd tray open\n"); + if (log_cb) + log_cb(RETRO_LOG_INFO, "cd tray open\n"); disk_ejected = 1; } static void disk_tray_close(void) { - lprintf("cd tray close\n"); + if (log_cb) + log_cb(RETRO_LOG_INFO, "cd tray close\n"); disk_ejected = 0; } static const char * const biosfiles_us[] = { - "us_scd1_9210", "us_scd2_9306", "SegaCDBIOS9303", "bios_CD_U" + "us_scd2_9306", "SegaCDBIOS9303", "us_scd1_9210", "bios_CD_U" }; static const char * const biosfiles_eu[] = { - "eu_mcd1_9210", "eu_mcd2_9306", "eu_mcd2_9303", "bios_CD_E" + "eu_mcd2_9306", "eu_mcd2_9303", "eu_mcd1_9210", "bios_CD_E" }; static const char * const biosfiles_jp[] = { - "jp_mcd1_9112", "jp_mcd1_9111", "bios_CD_J" + "jp_mcd2_921222", "jp_mcd1_9112", "jp_mcd1_9111", "bios_CD_J" }; static void make_system_path(char *buf, size_t buf_size, @@ -389,7 +667,8 @@ static const char *find_bios(int *region, const char *cd_fname) } if (f != NULL) { - lprintf("using bios: %s\n", path); + if (log_cb) + log_cb(RETRO_LOG_INFO, "using bios: %s\n", path); fclose(f); return path; } @@ -405,12 +684,14 @@ bool retro_load_game(const struct retro_game_info *info) enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_RGB565; if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt)) { - lprintf("RGB565 suppot required, sorry\n"); + if (log_cb) + log_cb(RETRO_LOG_ERROR, "RGB565 support required, sorry\n"); return false; } if (info == NULL || info->path == NULL) { - lprintf("info->path required\n"); + if (log_cb) + log_cb(RETRO_LOG_ERROR, "info->path required\n"); return false; } @@ -432,16 +713,20 @@ bool retro_load_game(const struct retro_game_info *info) switch (media_type) { case PM_BAD_DETECT: - lprintf("Failed to detect ROM/CD image type.\n"); + if (log_cb) + log_cb(RETRO_LOG_ERROR, "Failed to detect ROM/CD image type.\n"); return false; case PM_BAD_CD: - lprintf("Invalid CD image\n"); + if (log_cb) + log_cb(RETRO_LOG_ERROR, "Invalid CD image\n"); return false; case PM_BAD_CD_NO_BIOS: - lprintf("Missing BIOS\n"); + if (log_cb) + log_cb(RETRO_LOG_ERROR, "Missing BIOS\n"); return false; case PM_ERROR: - lprintf("Load error\n"); + if (log_cb) + log_cb(RETRO_LOG_ERROR, "Load error\n"); return false; default: break; @@ -452,7 +737,7 @@ bool retro_load_game(const struct retro_game_info *info) PicoWriteSound = snd_write; memset(sndBuffer, 0, sizeof(sndBuffer)); PsndOut = sndBuffer; - PsndRerate(1); + PsndRerate(0); return true; } @@ -484,14 +769,25 @@ void *retro_get_memory_data(unsigned id) size_t retro_get_memory_size(unsigned id) { + unsigned int i; + int sum; + if (id != RETRO_MEMORY_SAVE_RAM) return 0; if (PicoAHW & PAHW_MCD) // bram return 0x2000; - else + + if (Pico.m.frame_count == 0) return SRam.size; + + // if game doesn't write to sram, don't report it to + // libretro so that RA doesn't write out zeroed .srm + for (i = 0, sum = 0; i < SRam.size; i++) + sum |= SRam.data[i]; + + return (sum != 0) ? SRam.size : 0; } void retro_reset(void) @@ -520,13 +816,71 @@ static void snd_write(int len) audio_batch_cb(PsndOut, len / 4); } +static enum input_device input_name_to_val(const char *name) +{ + if (strcmp(name, "3 button pad") == 0) + return PICO_INPUT_PAD_3BTN; + if (strcmp(name, "6 button pad") == 0) + return PICO_INPUT_PAD_6BTN; + if (strcmp(name, "None") == 0) + return PICO_INPUT_NOTHING; + + if (log_cb) + log_cb(RETRO_LOG_WARN, "invalid picodrive_input: '%s'\n", name); + return PICO_INPUT_PAD_3BTN; +} + +static void update_variables(void) +{ + struct retro_variable var; + + var.value = NULL; + var.key = "picodrive_input1"; + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + PicoSetInputDevice(0, input_name_to_val(var.value)); + + var.value = NULL; + var.key = "picodrive_input2"; + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + PicoSetInputDevice(1, input_name_to_val(var.value)); + + var.value = NULL; + var.key = "picodrive_sprlim"; + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { + if (strcmp(var.value, "enabled") == 0) + PicoOpt |= POPT_DIS_SPRITE_LIM; + else + PicoOpt &= ~POPT_DIS_SPRITE_LIM; + } + + var.value = NULL; + var.key = "picodrive_ramcart"; + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { + if (strcmp(var.value, "enabled") == 0) + PicoOpt |= POPT_EN_MCD_RAMCART; + else + PicoOpt &= ~POPT_EN_MCD_RAMCART; + } + +#ifdef DRC_SH2 + var.value = NULL; + var.key = "picodrive_drc"; + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { + if (strcmp(var.value, "enabled") == 0) + PicoOpt |= POPT_EN_DRC; + else + PicoOpt &= ~POPT_EN_DRC; + } +#endif +} + void retro_run(void) { bool updated = false; int pad, i; if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated) - ; //update_variables(true); + update_variables(); input_poll_cb(); @@ -538,48 +892,48 @@ void retro_run(void) PicoFrame(); - video_cb(vout_buf, vout_width, vout_height, vout_width * 2); + video_cb((short *)vout_buf + vout_offset, + vout_width, vout_height, vout_width * 2); } void retro_init(void) { + struct retro_log_callback log; int level; -#ifdef IOS - emu_log = fopen("/User/Documents/PicoDrive.log", "w"); - if (emu_log == NULL) - emu_log = fopen("PicoDrive.log", "w"); - if (emu_log == NULL) -#endif - emu_log = stdout; - level = 0; environ_cb(RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL, &level); + if (environ_cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log)) + log_cb = log.log; + else + log_cb = NULL; + environ_cb(RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE, &disk_control); PicoOpt = POPT_EN_STEREO|POPT_EN_FM|POPT_EN_PSG|POPT_EN_Z80 - | POPT_EN_MCD_PCM|POPT_EN_MCD_CDDA|POPT_EN_SVP_DRC - | POPT_ACC_SPRITES|POPT_EN_32X|POPT_EN_PWM - | POPT_DIS_32C_BORDER; + | POPT_EN_MCD_PCM|POPT_EN_MCD_CDDA|POPT_EN_MCD_GFX + | POPT_EN_32X|POPT_EN_PWM + | POPT_ACC_SPRITES|POPT_DIS_32C_BORDER; +#ifdef __arm__ + PicoOpt |= POPT_EN_DRC; +#endif PsndRate = 44100; PicoAutoRgnOrder = 0x184; // US, EU, JP - PicoCDBuffers = 0; - - p32x_msh2_multiplier = MSH2_MULTI_DEFAULT; - p32x_ssh2_multiplier = SSH2_MULTI_DEFAULT; vout_width = 320; vout_height = 240; vout_buf = malloc(VOUT_MAX_WIDTH * VOUT_MAX_HEIGHT * 2); PicoInit(); - PicoDrawSetOutFormat(PDF_RGB555, 1); + PicoDrawSetOutFormat(PDF_RGB555, 0); PicoDrawSetOutBuf(vout_buf, vout_width * 2); //PicoMessage = plat_status_msg_busy_next; PicoMCDopenTray = disk_tray_open; PicoMCDcloseTray = disk_tray_close; + + update_variables(); } void retro_deinit(void)