/**************************************************************************** * libretro.c * * Genesis Plus GX libretro port * * Copyright Eke-Eke (2007-2022) * * Copyright Daniel De Matteis (2012-2016) * * Redistribution and use of this code or any derivative works are permitted * provided that the following conditions are met: * * - Redistributions may not be sold, nor may they be used in a commercial * product or activity. * * - Redistributions that are modified from the original source must include the * complete source code, including the source code for all components used by a * binary built from the modified sources. However, as a special exception, the * source code distributed need not include anything that is normally distributed * (in either source or binary form) with the major components (compiler, kernel, * and so on) of the operating system on which the executable runs, unless that * component itself accompanies the executable. * * - Redistributions must reproduce the above copyright notice, this list of * conditions and the following disclaimer in the documentation and/or other * materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************************/ #ifndef _MSC_VER #include #endif #include #include #include #ifdef _MSC_VER #define snprintf _snprintf #endif #ifdef _XBOX1 #include #endif #define RETRO_DEVICE_MDPAD_3B RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 0) #define RETRO_DEVICE_MDPAD_6B RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 1) #define RETRO_DEVICE_MSPAD_2B RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 2) #define RETRO_DEVICE_MDPAD_3B_WAYPLAY RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 3) #define RETRO_DEVICE_MDPAD_6B_WAYPLAY RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 4) #define RETRO_DEVICE_MDPAD_3B_TEAMPLAYER RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 5) #define RETRO_DEVICE_MDPAD_6B_TEAMPLAYER RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 6) #define RETRO_DEVICE_MSPAD_2B_MASTERTAP RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 7) #define RETRO_DEVICE_PADDLE RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_ANALOG, 0) #define RETRO_DEVICE_SPORTSPAD RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_ANALOG, 1) #define RETRO_DEVICE_XE_1AP RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_ANALOG, 2) #define RETRO_DEVICE_PHASER RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_LIGHTGUN, 0) #define RETRO_DEVICE_MENACER RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_LIGHTGUN, 1) #define RETRO_DEVICE_JUSTIFIERS RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_LIGHTGUN, 2) #define RETRO_DEVICE_GRAPHIC_BOARD RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_POINTER, 0) #include #include #include "libretro_core_options.h" #include "shared.h" #include "md_ntsc.h" #include "sms_ntsc.h" #define STATIC_ASSERT(name, test) typedef struct { int assert_[(test)?1:-1]; } assert_ ## name ## _ #define M68K_MAX_CYCLES 1107 #define Z80_MAX_CYCLES 345 #define OVERCLOCK_FRAME_DELAY 100 #ifdef M68K_OVERCLOCK_SHIFT #ifndef HAVE_OVERCLOCK #define HAVE_OVERCLOCK #endif STATIC_ASSERT(m68k_overflow, M68K_MAX_CYCLES <= UINT_MAX >> (M68K_OVERCLOCK_SHIFT + 1)); #endif #ifdef Z80_OVERCLOCK_SHIFT #ifndef HAVE_OVERCLOCK #define HAVE_OVERCLOCK #endif STATIC_ASSERT(z80_overflow, Z80_MAX_CYCLES <= UINT_MAX >> (Z80_OVERCLOCK_SHIFT + 1)); #endif t_config config; sms_ntsc_t *sms_ntsc; md_ntsc_t *md_ntsc; char GG_ROM[256]; char AR_ROM[256]; char SK_ROM[256]; char SK_UPMEM[256]; char MD_BIOS[256]; char GG_BIOS[256]; char MS_BIOS_EU[256]; char MS_BIOS_JP[256]; char MS_BIOS_US[256]; char CD_BIOS_EU[256]; char CD_BIOS_US[256]; char CD_BIOS_JP[256]; char CD_BRAM_JP[256]; char CD_BRAM_US[256]; char CD_BRAM_EU[256]; char CART_BRAM[256]; static int vwidth; static int vheight; static double vaspect_ratio; static uint32_t brm_crc[2]; static uint8_t brm_format[0x40] = { 0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x00,0x00,0x00,0x00,0x40, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x53,0x45,0x47,0x41,0x5f,0x43,0x44,0x5f,0x52,0x4f,0x4d,0x00,0x01,0x00,0x00,0x00, 0x52,0x41,0x4d,0x5f,0x43,0x41,0x52,0x54,0x52,0x49,0x44,0x47,0x45,0x5f,0x5f,0x5f }; static bool is_running = 0; static uint8_t temp[0x10000]; static int16 soundbuffer[3068]; static uint16_t bitmap_data_[720 * 576]; static bool restart_eq = false; static char g_rom_dir[256]; static char g_rom_name[256]; static const void *g_rom_data = NULL; static size_t g_rom_size = 0; static char *save_dir = NULL; 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_cb; enum RetroLightgunInputModes{RetroLightgun, RetroPointer}; static enum RetroLightgunInputModes retro_gun_mode = RetroLightgun; /* Cheat Support */ #define MAX_CHEATS (150) typedef struct { bool enable; uint16_t data; uint16_t old; uint32_t address; uint8_t *prev; } CHEATENTRY; static int maxcheats = 0; static int maxROMcheats = 0; static int maxRAMcheats = 0; static CHEATENTRY cheatlist[MAX_CHEATS]; static uint8_t cheatIndexes[MAX_CHEATS]; static char ggvalidchars[] = "ABCDEFGHJKLMNPRSTVWXYZ0123456789"; static char arvalidchars[] = "0123456789ABCDEF"; /* Some games appear to calibrate music playback speed for PAL/NTSC by actually counting CPU cycles per frame during startup, resulting in hilariously fast music. Delay overclocking for a while as a workaround */ #ifdef HAVE_OVERCLOCK static uint32_t overclock_delay; #endif static bool libretro_supports_option_categories = false; static bool libretro_supports_bitmasks = false; #define SOUND_FREQUENCY 44100 /* Hide the EQ settings for now */ /*#define HAVE_EQ*/ /* Frameskipping Support */ static unsigned frameskip_type = 0; static unsigned frameskip_threshold = 0; static uint16_t frameskip_counter = 0; static bool retro_audio_buff_active = false; static unsigned retro_audio_buff_occupancy = 0; static bool retro_audio_buff_underrun = false; /* Maximum number of consecutive frames that * can be skipped */ #define FRAMESKIP_MAX 60 static unsigned audio_latency = 0; static bool update_audio_latency = false; #ifdef USE_PER_SOUND_CHANNELS_CONFIG static bool show_advanced_av_settings = true; #endif static void retro_audio_buff_status_cb( bool active, unsigned occupancy, bool underrun_likely) { retro_audio_buff_active = active; retro_audio_buff_occupancy = occupancy; retro_audio_buff_underrun = underrun_likely; } static void init_frameskip(void) { if (frameskip_type > 0) { struct retro_audio_buffer_status_callback buf_status_cb; buf_status_cb.callback = retro_audio_buff_status_cb; if (!environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK, &buf_status_cb)) { if (log_cb) log_cb(RETRO_LOG_WARN, "Frameskip disabled - frontend does not support audio buffer status monitoring.\n"); retro_audio_buff_active = false; retro_audio_buff_occupancy = 0; retro_audio_buff_underrun = false; audio_latency = 0; } else { /* Frameskip is enabled - increase frontend * audio latency to minimise potential * buffer underruns */ float frames_per_sec; float frame_time_msec; /* While system_clock and lines_per_frame will * always be valid when this function is called, * it is not guaranteed. Just add a fallback to * prevent any possible divide-by-zero errors */ if ((system_clock <= 0) || (lines_per_frame <= 0)) frames_per_sec = 60.0f; else frames_per_sec = (float)system_clock / (float)lines_per_frame / (float)MCYCLES_PER_LINE; frame_time_msec = 1000.0f / frames_per_sec; /* Set latency to 6x current frame time... */ audio_latency = (unsigned)((6.0f * frame_time_msec) + 0.5f); /* ...then round up to nearest multiple of 32 */ audio_latency = (audio_latency + 0x1F) & ~0x1F; } } else { environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK, NULL); audio_latency = 0; } update_audio_latency = true; } /************************************ * Genesis Plus GX implementation ************************************/ #undef CHUNKSIZE #define CHUNKSIZE (0x10000) void error(char * fmt, ...) { char buffer[256]; va_list ap; va_start(ap, fmt); vsprintf(buffer, fmt, ap); if (log_cb) log_cb(RETRO_LOG_ERROR, "%s\n", buffer); va_end(ap); } int load_archive(char *filename, unsigned char *buffer, int maxsize, char *extension) { int64_t left = 0; int64_t size = 0; RFILE *fd; /* Get filename extension */ if (extension) { memcpy(extension, &filename[strlen(filename) - 3], 3); extension[3] = 0; } /* Check if this was called to load ROM file from the frontend (not BOOT ROM or Lock-On ROM files from the core) */ if (maxsize >= 0x800000) { /* Check if loaded game is already in memory */ if ((g_rom_data != NULL) && (g_rom_size > 0)) { size = g_rom_size; if (size > maxsize) size = maxsize; memcpy(buffer, g_rom_data, size); return size; } } /* Open file */ fd = filestream_open(filename, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE); if (!fd) { /* Master System & Game Gear BIOS are optional files */ if (!strcmp(filename,MS_BIOS_US) || !strcmp(filename,MS_BIOS_EU) || !strcmp(filename,MS_BIOS_JP) || !strcmp(filename,GG_BIOS)) { return 0; } /* Mega CD BIOS are required files */ if (!strcmp(filename,CD_BIOS_US) || !strcmp(filename,CD_BIOS_EU) || !strcmp(filename,CD_BIOS_JP)) { if (log_cb) log_cb(RETRO_LOG_ERROR, "Unable to open CD BIOS: %s.\n", filename); return 0; } if (log_cb) log_cb(RETRO_LOG_ERROR, "Unable to open file.\n"); return 0; } /* Get file size */ filestream_seek(fd, 0, SEEK_END); size = filestream_tell(fd); /* size limit */ if (size > MAXROMSIZE) { filestream_close(fd); if (log_cb) log_cb(RETRO_LOG_ERROR, "File is too large.\n"); return 0; } else if (size > maxsize) size = maxsize; if (log_cb) log_cb(RETRO_LOG_INFO, "INFORMATION - Loading %d bytes ...\n", size); /* Read into buffer */ left = size; filestream_seek(fd, 0, SEEK_SET); while (left > CHUNKSIZE) { filestream_read(fd, buffer, CHUNKSIZE); buffer += CHUNKSIZE; left -= CHUNKSIZE; } /* Read remaining bytes */ filestream_read(fd, buffer, left); /* Close file */ filestream_close(fd); /* Return loaded ROM size */ return size; } static void RAMCheatUpdate(void); static void osd_input_update_internal_bitmasks(void) { int i, player = 0; unsigned int temp; int16_t ret = input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK); for (i = 0; i < MAX_INPUTS; i++) { temp = 0; switch (input.dev[i]) { case DEVICE_PAD6B: if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_L)) temp |= INPUT_X; if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_X)) temp |= INPUT_Y; if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_R)) temp |= INPUT_Z; if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_SELECT)) temp |= INPUT_MODE; case DEVICE_PAD3B: if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_Y)) temp |= INPUT_A; case DEVICE_PAD2B: if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_B)) temp |= INPUT_B; if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_A)) temp |= INPUT_C; if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_START)) temp |= INPUT_START; if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_UP)) temp |= INPUT_UP; if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_DOWN)) temp |= INPUT_DOWN; if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_LEFT)) temp |= INPUT_LEFT; if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_RIGHT)) temp |= INPUT_RIGHT; player++; ret = input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK); break; case DEVICE_MOUSE: input.analog[i][0] = input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_X); if (config.invert_mouse) input.analog[i][1] = input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_Y); else input.analog[i][1] = -input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_Y); if (input.analog[i][0] < -255) input.analog[i][0] = -255; else if (input.analog[i][0] > 255) input.analog[i][0] = 255; if (input.analog[i][1] < -255) input.analog[i][1] = -255; else if (input.analog[i][1] > 255) input.analog[i][1] = 255; if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT)) temp |= INPUT_MOUSE_LEFT; if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_RIGHT)) temp |= INPUT_MOUSE_RIGHT; if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_WHEELDOWN)) temp |= INPUT_MOUSE_CENTER; if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_MIDDLE)) temp |= INPUT_START; player++; ret = input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK); break; case DEVICE_LIGHTGUN: if ( retro_gun_mode == RetroPointer ) { int touch_count; input.analog[i][0] = ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) + 0x7fff) * bitmap.viewport.w) / 0xfffe; input.analog[i][1] = ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y) + 0x7fff) * bitmap.viewport.h) / 0xfffe; if (input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED)) temp |= INPUT_A; touch_count = input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_COUNT); if (touch_count == 2) temp |= INPUT_B; else if (touch_count == 3) temp |= INPUT_START; else if (touch_count == 4) temp |= INPUT_C; } else { /* RetroLightgun is default */ if ( input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN) ) { input.analog[i][0] = -1000; input.analog[i][1] = -1000; } else { input.analog[i][0] = ((input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X) + 0x7fff) * bitmap.viewport.w) / 0xfffe; input.analog[i][1] = ((input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y) + 0x7fff) * bitmap.viewport.h) / 0xfffe; } if (input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_TRIGGER)) temp |= INPUT_A; if (input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_AUX_A)) temp |= INPUT_B; if (input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_AUX_B)) temp |= INPUT_C; if (input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_START)) temp |= INPUT_START; } player++; ret = input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK); break; case DEVICE_PADDLE: input.analog[i][0] = (input_state_cb(player, RETRO_DEVICE_ANALOG, 0, RETRO_DEVICE_ID_ANALOG_X) + 0x8000) >> 8; if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_B)) temp |= INPUT_BUTTON1; if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_START)) temp |= INPUT_START; player++; ret = input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK); break; case DEVICE_SPORTSPAD: input.analog[i][0] = (input_state_cb(player, RETRO_DEVICE_ANALOG, 0, RETRO_DEVICE_ID_ANALOG_X) + 0x8000) >> 8; input.analog[i][1] = (input_state_cb(player, RETRO_DEVICE_ANALOG, 0, RETRO_DEVICE_ID_ANALOG_Y) + 0x8000) >> 8; if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_B)) temp |= INPUT_BUTTON1; if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_A)) temp |= INPUT_BUTTON2; if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_START)) temp |= INPUT_START; player++; ret = input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK); break; case DEVICE_PICO: input.analog[i][0] = 0x03c + ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) + 0x7fff) * (0x17c-0x03c)) / 0xfffe; input.analog[i][1] = 0x1fc + ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y) + 0x7fff) * (0x2f7-0x1fc)) / 0xfffe; if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT)) temp |= INPUT_PICO_PEN; if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_RIGHT)) temp |= INPUT_PICO_RED; if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_WHEELUP)) pico_current = (pico_current - 1) & 7; if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_WHEELDOWN)) pico_current = (pico_current + 1) & 7; if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_UP)) temp |= INPUT_UP; if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_DOWN)) temp |= INPUT_DOWN; if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_LEFT)) temp |= INPUT_LEFT; if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_RIGHT)) temp |= INPUT_RIGHT; player++; ret = input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK); break; case DEVICE_TEREBI: input.analog[i][0] = ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) + 0x7fff) * 250) / 0xfffe; input.analog[i][1] = ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y) + 0x7fff) * 250) / 0xfffe; if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT)) temp |= INPUT_BUTTON1; if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_MIDDLE)) temp |= INPUT_START; player++; ret = input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK); break; case DEVICE_GRAPHIC_BOARD: input.analog[i][0] = ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) + 0x7fff) * 255) / 0xfffe; input.analog[i][1] = ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y) + 0x7fff) * 255) / 0xfffe; if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT)) temp |= INPUT_GRAPHIC_PEN; if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_MIDDLE)) temp |= INPUT_GRAPHIC_DO; if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_RIGHT)) temp |= INPUT_GRAPHIC_MENU; player++; ret = input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK); break; case DEVICE_XE_1AP: { int rx = input.analog[i][0] = input_state_cb(player, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X); int ry = input.analog[i][1] = input_state_cb(player, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y); if (abs(rx) > abs(ry)) input.analog[i+1][0] = (rx + 0x8000) >> 8; else input.analog[i+1][0] = (0x7fff - ry) >> 8; input.analog[i][0] = (input_state_cb(player, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X) + 0x8000) >> 8; input.analog[i][1] = (input_state_cb(player, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y) + 0x8000) >> 8; if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_R)) temp |= INPUT_XE_A; if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_R2)) temp |= INPUT_XE_B; if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_L)) temp |= INPUT_XE_C; if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_L2)) temp |= INPUT_XE_D; if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_Y)) temp |= INPUT_XE_E1; if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_B)) temp |= INPUT_XE_E2; if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_SELECT)) temp |= INPUT_XE_SELECT; if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_START)) temp |= INPUT_XE_START; player++; ret = input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK); break; } default: break; } input.pad[i] = temp; } } static void osd_input_update_internal(void) { int i, player = 0; unsigned int temp; for (i = 0; i < MAX_INPUTS; i++) { temp = 0; switch (input.dev[i]) { case DEVICE_PAD6B: if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L)) temp |= INPUT_X; if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X)) temp |= INPUT_Y; if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R)) temp |= INPUT_Z; if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT)) temp |= INPUT_MODE; case DEVICE_PAD3B: if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y)) temp |= INPUT_A; case DEVICE_PAD2B: if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B)) temp |= INPUT_B; if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A)) temp |= INPUT_C; if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START)) temp |= INPUT_START; if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP)) temp |= INPUT_UP; if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN)) temp |= INPUT_DOWN; if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT)) temp |= INPUT_LEFT; if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT)) temp |= INPUT_RIGHT; player++; break; case DEVICE_MOUSE: input.analog[i][0] = input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_X); if (config.invert_mouse) input.analog[i][1] = input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_Y); else input.analog[i][1] = -input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_Y); if (input.analog[i][0] < -255) input.analog[i][0] = -255; else if (input.analog[i][0] > 255) input.analog[i][0] = 255; if (input.analog[i][1] < -255) input.analog[i][1] = -255; else if (input.analog[i][1] > 255) input.analog[i][1] = 255; if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT)) temp |= INPUT_MOUSE_LEFT; if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_RIGHT)) temp |= INPUT_MOUSE_RIGHT; if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_WHEELDOWN)) temp |= INPUT_MOUSE_CENTER; if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_MIDDLE)) temp |= INPUT_START; player++; break; case DEVICE_LIGHTGUN: if ( retro_gun_mode == RetroPointer ) { input.analog[i][0] = ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) + 0x7fff) * bitmap.viewport.w) / 0xfffe; input.analog[i][1] = ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y) + 0x7fff) * bitmap.viewport.h) / 0xfffe; if (input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED)) temp |= INPUT_A; } else { /* RetroLightgun is default */ if ( input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN) ) { input.analog[i][0] = -1000; input.analog[i][1] = -1000; } else { input.analog[i][0] = ((input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X) + 0x7fff) * bitmap.viewport.w) / 0xfffe; input.analog[i][1] = ((input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y) + 0x7fff) * bitmap.viewport.h) / 0xfffe; } if (input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_TRIGGER)) temp |= INPUT_A; if (input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_AUX_A)) temp |= INPUT_B; if (input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_AUX_B)) temp |= INPUT_C; if (input_state_cb(player, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_START)) temp |= INPUT_START; } player++; break; case DEVICE_PADDLE: input.analog[i][0] = (input_state_cb(player, RETRO_DEVICE_ANALOG, 0, RETRO_DEVICE_ID_ANALOG_X) + 0x8000) >> 8; if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B)) temp |= INPUT_BUTTON1; if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START)) temp |= INPUT_START; player++; break; case DEVICE_SPORTSPAD: input.analog[i][0] = (input_state_cb(player, RETRO_DEVICE_ANALOG, 0, RETRO_DEVICE_ID_ANALOG_X) + 0x8000) >> 8; input.analog[i][1] = (input_state_cb(player, RETRO_DEVICE_ANALOG, 0, RETRO_DEVICE_ID_ANALOG_Y) + 0x8000) >> 8; if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B)) temp |= INPUT_BUTTON1; if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A)) temp |= INPUT_BUTTON2; if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START)) temp |= INPUT_START; player++; break; case DEVICE_PICO: input.analog[i][0] = 0x03c + ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) + 0x7fff) * (0x17c-0x03c)) / 0xfffe; input.analog[i][1] = 0x1fc + ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y) + 0x7fff) * (0x2f7-0x1fc)) / 0xfffe; if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT)) temp |= INPUT_PICO_PEN; if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_RIGHT)) temp |= INPUT_PICO_RED; if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_WHEELUP)) pico_current = (pico_current - 1) & 7; if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_WHEELDOWN)) pico_current = (pico_current + 1) & 7; if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP)) temp |= INPUT_UP; if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN)) temp |= INPUT_DOWN; if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT)) temp |= INPUT_LEFT; if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT)) temp |= INPUT_RIGHT; player++; break; case DEVICE_TEREBI: input.analog[i][0] = ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) + 0x7fff) * 250) / 0xfffe; input.analog[i][1] = ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y) + 0x7fff) * 250) / 0xfffe; if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT)) temp |= INPUT_BUTTON1; if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_MIDDLE)) temp |= INPUT_START; player++; break; case DEVICE_GRAPHIC_BOARD: input.analog[i][0] = ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) + 0x7fff) * 255) / 0xfffe; input.analog[i][1] = ((input_state_cb(player, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y) + 0x7fff) * 255) / 0xfffe; if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT)) temp |= INPUT_GRAPHIC_PEN; if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_MIDDLE)) temp |= INPUT_GRAPHIC_DO; if (input_state_cb(player, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_RIGHT)) temp |= INPUT_GRAPHIC_MENU; player++; break; case DEVICE_XE_1AP: { int rx = input.analog[i][0] = input_state_cb(player, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X); int ry = input.analog[i][1] = input_state_cb(player, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y); if (abs(rx) > abs(ry)) input.analog[i+1][0] = (rx + 0x8000) >> 8; else input.analog[i+1][0] = (0x7fff - ry) >> 8; input.analog[i][0] = (input_state_cb(player, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X) + 0x8000) >> 8; input.analog[i][1] = (input_state_cb(player, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y) + 0x8000) >> 8; if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R)) temp |= INPUT_XE_A; if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2)) temp |= INPUT_XE_B; if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L)) temp |= INPUT_XE_C; if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2)) temp |= INPUT_XE_D; if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y)) temp |= INPUT_XE_E1; if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B)) temp |= INPUT_XE_E2; if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT)) temp |= INPUT_XE_SELECT; if (input_state_cb(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START)) temp |= INPUT_XE_START; player++; break; } default: break; } input.pad[i] = temp; } } void osd_input_update(void) { input_poll_cb(); /* Update RAM patches */ RAMCheatUpdate(); if (libretro_supports_bitmasks) osd_input_update_internal_bitmasks(); else osd_input_update_internal(); } static void draw_cursor(int16_t x, int16_t y, uint16_t color) { int i; /* crosshair center position */ uint16_t *ptr = (uint16_t *)bitmap.data + ((bitmap.viewport.y + y) * bitmap.width) + x + bitmap.viewport.x; /* default crosshair dimension */ int x_start = x - 3; int x_end = x + 3; int y_start = y - 3; int y_end = y + 3; /* off-screen? */ if ( x < 0 && y < 0 ) return; /* framebuffer limits */ if (x_start < -bitmap.viewport.x) x_start = -bitmap.viewport.x; if (x_end >= (bitmap.viewport.w + bitmap.viewport.x)) x_end = bitmap.viewport.w + bitmap.viewport.x - 1; if (y_start < -bitmap.viewport.y) y_start = -bitmap.viewport.y; if (y_end >= (bitmap.viewport.h + bitmap.viewport.y)) y_end = bitmap.viewport.h + bitmap.viewport.y - 1; /* draw crosshair */ for (i = (x_start - x); i <= (x_end - x); i++) ptr[i] = (i & 1) ? color : 0xffff; for (i = (y_start - y); i <= (y_end - y); i++) ptr[i * bitmap.width] = (i & 1) ? color : 0xffff; } static void init_bitmap(void) { memset(&bitmap, 0, sizeof(bitmap)); bitmap.width = 720; bitmap.height = 576; bitmap.pitch = 720 * 2; bitmap.data = (uint8_t *)bitmap_data_; } static void config_default(void) { int i; /* sound options */ config.psg_preamp = 150; config.fm_preamp = 100; config.cdda_volume = 100; config.pcm_volume = 100; config.hq_fm = 1; /* high-quality FM resampling (slower) */ config.hq_psg = 1; /* high-quality PSG resampling (slower) */ config.filter = 1; /* no filter */ config.lp_range = 0x9999; /* 0.6 in 0.16 fixed point */ config.low_freq = 880; config.high_freq = 5000; config.lg = 100; config.mg = 100; config.hg = 100; config.ym2612 = YM2612_DISCRETE; config.ym2413 = 2; /* AUTO */ config.mono = 0; /* STEREO output */ #ifdef USE_PER_SOUND_CHANNELS_CONFIG for (i = 0; i < 4; i++) config.psg_ch_volumes[i] = 100; /* individual channel volumes */ for (i = 0; i < 6; i++) config.md_ch_volumes[i] = 100; /* individual channel volumes */ for (i = 0; i < 9; i++) config.sms_fm_ch_volumes[i] = 100; /* individual channel volumes */ #endif #ifdef HAVE_YM3438_CORE config.ym3438 = 0; #endif #ifdef HAVE_OPLL_CORE config.opll = 0; #endif /* system options */ config.system = 0; /* AUTO */ config.region_detect = 0; /* AUTO */ config.vdp_mode = 0; /* AUTO */ config.master_clock = 0; /* AUTO */ config.force_dtack = 0; config.addr_error = 1; config.bios = 0; config.lock_on = 0; config.add_on = HW_ADDON_AUTO; config.lcd = 0; /* 0.8 fixed point */ #ifdef HAVE_OVERCLOCK config.overclock = 100; #endif config.no_sprite_limit = 0; config.enhanced_vscroll = 0; config.enhanced_vscroll_limit = 8; /* video options */ config.overscan = 0; config.aspect_ratio = 0; config.gg_extra = 0; config.ntsc = 0; config.lcd = 0; config.render = 0; config.left_border = 0; /* input options */ input.system[0] = SYSTEM_GAMEPAD; input.system[1] = SYSTEM_GAMEPAD; for (i=0; i CHUNKSIZE) { filestream_read(fp, scd.cartridge.area + done, CHUNKSIZE); done += CHUNKSIZE; filesize -= CHUNKSIZE; } /* Read remaining bytes */ if (filesize) { filestream_read(fp, scd.cartridge.area + done, filesize); } /* close file */ filestream_close(fp); /* update CRC */ brm_crc[1] = crc32(0, scd.cartridge.area, scd.cartridge.mask + 1); } /* check if cartridge backup RAM is correctly formatted */ if (memcmp(scd.cartridge.area + scd.cartridge.mask + 1 - 0x20, brm_format + 0x20, 0x20)) { /* clear cartridge backup RAM */ memset(scd.cartridge.area, 0x00, scd.cartridge.mask + 1); /* Cartridge Backup RAM size fields */ brm_format[0x10] = brm_format[0x12] = brm_format[0x14] = brm_format[0x16] = (((scd.cartridge.mask + 1) / 64) - 3) >> 8; brm_format[0x11] = brm_format[0x13] = brm_format[0x15] = brm_format[0x17] = (((scd.cartridge.mask + 1) / 64) - 3) & 0xff; /* format cartridge backup RAM */ memcpy(scd.cartridge.area + scd.cartridge.mask + 1 - 0x40, brm_format, 0x40); } } } static void bram_save(void) { RFILE *fp; /* verify that internal backup RAM has been modified */ if (crc32(0, scd.bram, 0x2000) != brm_crc[0]) { /* check if it is correctly formatted before saving */ if (!memcmp(scd.bram + 0x2000 - 0x20, brm_format + 0x20, 0x20)) { switch (region_code) { case REGION_JAPAN_NTSC: fp = filestream_open(CD_BRAM_JP, RETRO_VFS_FILE_ACCESS_WRITE, RETRO_VFS_FILE_ACCESS_HINT_NONE); break; case REGION_EUROPE: fp = filestream_open(CD_BRAM_EU, RETRO_VFS_FILE_ACCESS_WRITE, RETRO_VFS_FILE_ACCESS_HINT_NONE); break; case REGION_USA: fp = filestream_open(CD_BRAM_US, RETRO_VFS_FILE_ACCESS_WRITE, RETRO_VFS_FILE_ACCESS_HINT_NONE); break; default: return; } if (fp != NULL) { filestream_write(fp, scd.bram, 0x2000); filestream_close(fp); /* update CRC */ brm_crc[0] = crc32(0, scd.bram, 0x2000); } } } /* verify that cartridge backup RAM has been modified */ if (scd.cartridge.id && (crc32(0, scd.cartridge.area, scd.cartridge.mask + 1) != brm_crc[1])) { /* check if it is correctly formatted before saving */ if (!memcmp(scd.cartridge.area + scd.cartridge.mask + 1 - 0x20, brm_format + 0x20, 0x20)) { fp = filestream_open(CART_BRAM, RETRO_VFS_FILE_ACCESS_WRITE, RETRO_VFS_FILE_ACCESS_HINT_NONE); if (fp != NULL) { int filesize = scd.cartridge.mask + 1; int done = 0; /* Write to file (2k blocks) */ while (filesize > CHUNKSIZE) { filestream_write(fp, scd.cartridge.area + done, CHUNKSIZE); done += CHUNKSIZE; filesize -= CHUNKSIZE; } /* Write remaining bytes */ if (filesize) { filestream_write(fp, scd.cartridge.area + done, filesize); } /* Close file */ filestream_close(fp); /* update CRC */ brm_crc[1] = crc32(0, scd.cartridge.area, scd.cartridge.mask + 1); } } } } static void extract_name(char *buf, const char *path, size_t size) { char *base = strrchr(path, '/'); if (!base) base = strrchr(path, '\\'); if (base) { snprintf(buf, size, "%s", base); base = strrchr(buf, '.'); if (base) *base = '\0'; } else buf[0] = '\0'; } static void extract_directory(char *buf, const char *path, size_t size) { char *base; strncpy(buf, path, size - 1); buf[size - 1] = '\0'; base = strrchr(buf, '/'); if (!base) base = strrchr(buf, '\\'); if (base) *base = '\0'; else buf[0] = '\0'; } static double calculate_display_aspect_ratio(void) { double videosamplerate, dotrate; bool is_h40 = false; if (config.aspect_ratio == 0) { if ((system_hw == SYSTEM_GG || system_hw == SYSTEM_GGMS) && config.overscan == 0 && config.gg_extra == 0) return (6.0 / 5.0) * ((double)vwidth / (double)vheight); } is_h40 = bitmap.viewport.w == 320; /* Could be read directly from the register as well. */ dotrate = system_clock / (is_h40 ? 8.0 : 10.0); if (config.aspect_ratio == 1) /* Force NTSC PAR */ videosamplerate = 135000000.0 / 11.0; else if (config.aspect_ratio == 2) /* Force PAL PAR */ videosamplerate = 14750000.0; else videosamplerate = vdp_pal ? 14750000.0 : 135000000.0 / 11.0; return (videosamplerate / dotrate) * ((double)vwidth / ((double)vheight * 2.0)); } static bool update_viewport(void) { int ow = vwidth; int oh = vheight; double oar = vaspect_ratio; vwidth = bitmap.viewport.w + (bitmap.viewport.x * 2); vheight = bitmap.viewport.h + (bitmap.viewport.y * 2); vaspect_ratio = calculate_display_aspect_ratio(); if (config.ntsc) { if (reg[12] & 1) vwidth = MD_NTSC_OUT_WIDTH(vwidth); else vwidth = SMS_NTSC_OUT_WIDTH(vwidth); } if (config.render && interlaced) { vheight = vheight * 2; } return ((ow != vwidth) || (oh != vheight) || (oar != vaspect_ratio)); } #ifdef HAVE_OVERCLOCK static void update_overclock(void) { #ifdef M68K_OVERCLOCK_SHIFT m68k.cycle_ratio = 1 << M68K_OVERCLOCK_SHIFT; #endif #ifdef Z80_OVERCLOCK_SHIFT z80_cycle_ratio = 1 << Z80_OVERCLOCK_SHIFT; #endif if (overclock_delay == 0) { /* Cycle ratios multiply per-instruction cycle counts, so use reciprocals */ #ifdef M68K_OVERCLOCK_SHIFT if ((system_hw & SYSTEM_PBC) == SYSTEM_MD) m68k.cycle_ratio = (100 << M68K_OVERCLOCK_SHIFT) / config.overclock; #endif #ifdef Z80_OVERCLOCK_SHIFT if ((system_hw & SYSTEM_PBC) != SYSTEM_MD) z80_cycle_ratio = (100 << Z80_OVERCLOCK_SHIFT) / config.overclock; #endif } } #endif static void check_variables(bool first_run) { unsigned orig_value; struct retro_system_av_info info; #ifdef USE_PER_SOUND_CHANNELS_CONFIG unsigned c; char md_fm_channel_volume_base_str[] = "genesis_plus_gx_md_channel_0_volume"; char sms_fm_channel_volume_base_str[] = "genesis_plus_gx_sms_fm_channel_0_volume"; char psg_channel_volume_base_str[] = "genesis_plus_gx_psg_channel_0_volume"; #endif bool update_viewports = false; bool reinit = false; bool update_frameskip = false; struct retro_variable var = {0}; var.key = "genesis_plus_gx_bram"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { #if defined(_WIN32) char slash = '\\'; #else char slash = '/'; #endif if (!var.value || !strcmp(var.value, "per bios")) { snprintf(CD_BRAM_EU, sizeof(CD_BRAM_EU), "%s%cscd_E.brm", save_dir, slash); snprintf(CD_BRAM_US, sizeof(CD_BRAM_US), "%s%cscd_U.brm", save_dir, slash); snprintf(CD_BRAM_JP, sizeof(CD_BRAM_JP), "%s%cscd_J.brm", save_dir, slash); } else { snprintf(CD_BRAM_EU, sizeof(CD_BRAM_EU), "%s%c%s.brm", save_dir, slash, g_rom_name); snprintf(CD_BRAM_US, sizeof(CD_BRAM_US), "%s%c%s.brm", save_dir, slash, g_rom_name); snprintf(CD_BRAM_JP, sizeof(CD_BRAM_JP), "%s%c%s.brm", save_dir, slash, g_rom_name); } } var.key = "genesis_plus_gx_system_hw"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { orig_value = config.system; if (var.value && !strcmp(var.value, "sg-1000")) config.system = SYSTEM_SG; else if (var.value && !strcmp(var.value, "sg-1000 II")) config.system = SYSTEM_SGII; else if (var.value && !strcmp(var.value, "mark-III")) config.system = SYSTEM_MARKIII; else if (var.value && !strcmp(var.value, "master system")) config.system = SYSTEM_SMS; else if (var.value && !strcmp(var.value, "master system II")) config.system = SYSTEM_SMS2; else if (var.value && !strcmp(var.value, "game gear")) config.system = SYSTEM_GG; else if (var.value && !strcmp(var.value, "mega drive / genesis")) config.system = SYSTEM_MD; else config.system = 0; if (orig_value != config.system) { if (system_hw) { switch (config.system) { case 0: system_hw = romtype; /* AUTO */ break; case SYSTEM_MD: system_hw = (romtype & SYSTEM_MD) ? romtype : SYSTEM_PBC; break; case SYSTEM_GG: system_hw = (romtype == SYSTEM_GG) ? SYSTEM_GG : SYSTEM_GGMS; break; default: system_hw = config.system; break; } reinit = true; } } } var.key = "genesis_plus_gx_bios"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { orig_value = config.bios; if (var.value && !strcmp(var.value, "enabled")) config.bios = 3; else config.bios = 0; if (orig_value != config.bios) { if (system_hw) { reinit = true; } } } var.key = "genesis_plus_gx_region_detect"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { orig_value = config.region_detect; if (var.value && !strcmp(var.value, "ntsc-u")) config.region_detect = 1; else if (var.value && !strcmp(var.value, "pal")) config.region_detect = 2; else if (var.value && !strcmp(var.value, "ntsc-j")) config.region_detect = 3; else config.region_detect = 0; if (orig_value != config.region_detect) { if (system_hw) { get_region(NULL); if ((system_hw == SYSTEM_MCD) || ((system_hw & SYSTEM_SMS) && config.bios)) { /* system with region BIOS should be reinitialized */ reinit = true; } else { static const uint16 vc_table[4][2] = { /* NTSC, PAL */ {0xDA , 0xF2}, /* Mode 4 (192 lines) */ {0xEA , 0x102}, /* Mode 5 (224 lines) */ {0xDA , 0xF2}, /* Mode 4 (192 lines) */ {0x106, 0x10A} /* Mode 5 (240 lines) */ }; /* framerate might have changed, reinitialize audio timings */ audio_set_rate(44100, 0); /* reinitialize I/O region register */ if (system_hw == SYSTEM_MD) { io_reg[0x00] = 0x20 | region_code | (config.bios & 1); } else if (system_hw == SYSTEM_MCD) { io_reg[0x00] = region_code | (config.bios & 1); } else { io_reg[0x00] = 0x80 | (region_code >> 1); } /* reinitialize VDP timings */ lines_per_frame = vdp_pal ? 313 : 262; /* reinitialize NTSC/PAL mode in VDP status */ if (system_hw & SYSTEM_MD) { status = (status & ~1) | vdp_pal; } /* reinitialize VC max value */ switch (bitmap.viewport.h) { case 192: vc_max = vc_table[0][vdp_pal]; break; case 224: vc_max = vc_table[1][vdp_pal]; break; case 240: vc_max = vc_table[3][vdp_pal]; break; } update_viewports = true; } update_frameskip = true; } } } var.key = "genesis_plus_gx_force_dtack"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { if (!var.value || !strcmp(var.value, "enabled")) config.force_dtack = 1; else config.force_dtack = 0; } var.key = "genesis_plus_gx_addr_error"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { if (!var.value || !strcmp(var.value, "enabled")) m68k.aerr_enabled = config.addr_error = 1; else m68k.aerr_enabled = config.addr_error = 0; } var.key = "genesis_plus_gx_cd_latency"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { if (!var.value || !strcmp(var.value, "enabled")) config.cd_latency = 1; else config.cd_latency = 0; } var.key = "genesis_plus_gx_add_on"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { orig_value = config.add_on; if (var.value && !strcmp(var.value, "sega/mega cd")) config.add_on = HW_ADDON_MEGACD; else if (var.value && !strcmp(var.value, "megasd")) config.add_on = HW_ADDON_MEGASD; else if (var.value && !strcmp(var.value, "none")) config.add_on = HW_ADDON_NONE; else config.add_on = HW_ADDON_AUTO; /* note: game needs to be reloaded for change to take effect */ } var.key = "genesis_plus_gx_lock_on"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { orig_value = config.lock_on; if (var.value && !strcmp(var.value, "game genie")) config.lock_on = TYPE_GG; else if (var.value && !strcmp(var.value, "action replay (pro)")) config.lock_on = TYPE_AR; else if (var.value && !strcmp(var.value, "sonic & knuckles")) config.lock_on = TYPE_SK; else config.lock_on = 0; if (orig_value != config.lock_on) { if (system_hw == SYSTEM_MD) reinit = true; } } var.key = "genesis_plus_gx_ym2413"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { orig_value = config.ym2413; if (var.value && !strcmp(var.value, "enabled")) config.ym2413 = 1; else if (var.value && !strcmp(var.value, "disabled")) config.ym2413 = 0; else config.ym2413 = 2; if (orig_value != config.ym2413) { if (system_hw && (config.ym2413 & 2) && ((system_hw & SYSTEM_PBC) != SYSTEM_MD)) { memcpy(temp, sram.sram, sizeof(temp)); sms_cart_init(); memcpy(sram.sram, temp, sizeof(temp)); } } } #ifdef HAVE_OPLL_CORE var.key = "genesis_plus_gx_ym2413_core"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { orig_value = config.opll; if (var.value && !strcmp(var.value, "nuked")) { config.opll = 1; } else { config.opll = 0; } if (((orig_value == 0) && (config.opll > 0)) || ((orig_value > 0) && (config.opll == 0))) { sound_init(); sound_reset(); } } #endif var.key = "genesis_plus_gx_sound_output"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { if (var.value && !strcmp(var.value, "mono")) config.mono = 1; else if (!var.value || !strcmp(var.value, "stereo")) config.mono = 0; } var.key = "genesis_plus_gx_psg_preamp"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { config.psg_preamp = (!var.value) ? 150: atoi(var.value); if ((system_hw & SYSTEM_PBC) == SYSTEM_MD) { psg_config(0, config.psg_preamp, 0xff); } else { psg_config(0, config.psg_preamp, io_reg[6]); } } var.key = "genesis_plus_gx_fm_preamp"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { config.fm_preamp = (!var.value) ? 100: atoi(var.value); } var.key = "genesis_plus_gx_cdda_volume"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { config.cdda_volume = (!var.value) ? 100: atoi(var.value); } var.key = "genesis_plus_gx_pcm_volume"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { config.pcm_volume = (!var.value) ? 100: atoi(var.value); } var.key = "genesis_plus_gx_audio_filter"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { if (var.value && !strcmp(var.value, "low-pass")) config.filter = 1; #if HAVE_EQ else if (var.value && !strcmp(var.value, "EQ")) config.filter = 2; #endif else config.filter = 0; } var.key = "genesis_plus_gx_lowpass_range"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { config.lp_range = (!var.value) ? 0x9999 : ((atoi(var.value) * 65536) / 100); } #if HAVE_EQ var.key = "genesis_plus_gx_audio_eq_low"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { uint8_t new_lg = (!var.value) ? 100 : atoi(var.value); if (new_lg != config.lg) restart_eq = true; config.lg = new_lg; } var.key = "genesis_plus_gx_audio_eq_mid"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { uint8_t new_mg = (!var.value) ? 100 : atoi(var.value); if (new_mg != config.mg) restart_eq = true; config.mg = new_mg; } var.key = "genesis_plus_gx_audio_eq_high"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { uint8_t new_hg = (!var.value) ? 100 : atoi(var.value); if (new_hg != config.hg) restart_eq = true; config.hg = new_hg; } #endif var.key = "genesis_plus_gx_ym2612"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { #ifdef HAVE_YM3438_CORE orig_value = config.ym3438; if (var.value && !strcmp(var.value, "nuked (ym2612)")) { OPN2_SetChipType(ym3438_mode_ym2612); config.ym3438 = 1; } else if (var.value && !strcmp(var.value, "nuked (ym3438)")) { OPN2_SetChipType(ym3438_mode_readmode); config.ym3438 = 2; } else { config.ym3438 = 0; } if (((orig_value == 0) && (config.ym3438 > 0)) || ((orig_value > 0) && (config.ym3438 == 0))) { sound_init(); sound_reset(); } #endif if (!var.value || !strcmp(var.value, "mame (ym2612)")) { config.ym2612 = YM2612_DISCRETE; YM2612Config(YM2612_DISCRETE); } else if (var.value && !strcmp(var.value, "mame (asic ym3438)")) { config.ym2612 = YM2612_INTEGRATED; YM2612Config(YM2612_INTEGRATED); } else { config.ym2612 = YM2612_ENHANCED; YM2612Config(YM2612_ENHANCED); } } var.key = "genesis_plus_gx_frameskip"; var.value = NULL; orig_value = frameskip_type; frameskip_type = 0; if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { if (strcmp(var.value, "auto") == 0) frameskip_type = 1; else if (strcmp(var.value, "manual") == 0) frameskip_type = 2; } update_frameskip = update_frameskip || (frameskip_type != orig_value); var.key = "genesis_plus_gx_frameskip_threshold"; var.value = NULL; frameskip_threshold = 33; if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) frameskip_threshold = strtol(var.value, NULL, 10); var.key = "genesis_plus_gx_blargg_ntsc_filter"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { orig_value = config.ntsc; if (!var.value || !strcmp(var.value, "disabled")) config.ntsc = 0; else if (var.value && !strcmp(var.value, "monochrome")) { config.ntsc = 1; sms_ntsc_init(sms_ntsc, &sms_ntsc_monochrome); md_ntsc_init(md_ntsc, &md_ntsc_monochrome); } else if (var.value && !strcmp(var.value, "composite")) { config.ntsc = 1; sms_ntsc_init(sms_ntsc, &sms_ntsc_composite); md_ntsc_init(md_ntsc, &md_ntsc_composite); } else if (var.value && !strcmp(var.value, "svideo")) { config.ntsc = 1; sms_ntsc_init(sms_ntsc, &sms_ntsc_svideo); md_ntsc_init(md_ntsc, &md_ntsc_svideo); } else if (var.value && !strcmp(var.value, "rgb")) { config.ntsc = 1; sms_ntsc_init(sms_ntsc, &sms_ntsc_rgb); md_ntsc_init(md_ntsc, &md_ntsc_rgb); } if (orig_value != config.ntsc) update_viewports = true; } var.key = "genesis_plus_gx_lcd_filter"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { if (!var.value || !strcmp(var.value, "disabled")) config.lcd = 0; else if (var.value && !strcmp(var.value, "enabled")) config.lcd = (uint8)(0.80 * 256); } var.key = "genesis_plus_gx_overscan"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { orig_value = config.overscan; if (!var.value || !strcmp(var.value, "disabled")) config.overscan = 0; else if (var.value && !strcmp(var.value, "top/bottom")) config.overscan = 1; else if (var.value && !strcmp(var.value, "left/right")) config.overscan = 2; else if (var.value && !strcmp(var.value, "full")) config.overscan = 3; if (orig_value != config.overscan) update_viewports = true; } var.key = "genesis_plus_gx_gg_extra"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { orig_value = config.gg_extra; if (!var.value || !strcmp(var.value, "disabled")) config.gg_extra = 0; else if (var.value && !strcmp(var.value, "enabled")) config.gg_extra = 1; if (orig_value != config.gg_extra) update_viewports = true; } var.key = "genesis_plus_gx_aspect_ratio"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { orig_value = config.aspect_ratio; if (var.value && !strcmp(var.value, "NTSC PAR")) config.aspect_ratio = 1; else if (var.value && !strcmp(var.value, "PAL PAR")) config.aspect_ratio = 2; else config.aspect_ratio = 0; if (orig_value != config.aspect_ratio) update_viewports = true; } var.key = "genesis_plus_gx_render"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { orig_value = config.render; if (!var.value || !strcmp(var.value, "single field")) config.render = 0; else config.render = 1; if (orig_value != config.render) update_viewports = true; } var.key = "genesis_plus_gx_gun_cursor"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { if (!var.value || !strcmp(var.value, "disabled")) config.gun_cursor = 0; else config.gun_cursor = 1; } var.key = "genesis_plus_gx_gun_input"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { if (!var.value || !strcmp(var.value, "touchscreen")) retro_gun_mode = RetroPointer; else retro_gun_mode = RetroLightgun; } var.key = "genesis_plus_gx_invert_mouse"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { if (!var.value || !strcmp(var.value, "disabled")) config.invert_mouse = 0; else config.invert_mouse = 1; } var.key = "genesis_plus_gx_left_border"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { orig_value = config.left_border; if (!var.value || !strcmp(var.value, "disabled")) config.left_border = 0; else if (var.value && !strcmp(var.value, "left border")) config.left_border = 1; else if (var.value && !strcmp(var.value, "left & right borders")) config.left_border = 2; if (orig_value != config.left_border) update_viewports = true; } #ifdef HAVE_OVERCLOCK var.key = "genesis_plus_gx_overclock"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { if (!var.value || !strcmp(var.value, "100%")) config.overclock = 100; else if (var.value && !strcmp(var.value, "125%")) config.overclock = 125; else if (var.value && !strcmp(var.value, "150%")) config.overclock = 150; else if (var.value && !strcmp(var.value, "175%")) config.overclock = 175; else if (var.value && !strcmp(var.value, "200%")) config.overclock = 200; if (system_hw) update_overclock(); } #endif var.key = "genesis_plus_gx_no_sprite_limit"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { if (!var.value || !strcmp(var.value, "disabled")) config.no_sprite_limit = 0; else config.no_sprite_limit = 1; } var.key = "genesis_plus_gx_enhanced_vscroll"; environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); { if (!var.value || !strcmp(var.value, "disabled")) config.enhanced_vscroll = 0; else config.enhanced_vscroll = 1; } var.key = "genesis_plus_gx_enhanced_vscroll_limit"; if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) config.enhanced_vscroll_limit = strtol(var.value, NULL, 10); #ifdef USE_PER_SOUND_CHANNELS_CONFIG var.key = psg_channel_volume_base_str; for (c = 0; c < 4; c++) { psg_channel_volume_base_str[28] = c+'0'; if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var)) { config.psg_ch_volumes[c] = atoi(var.value); /* need to recall config to have the settings applied */ if ((system_hw & SYSTEM_PBC) == SYSTEM_MD) psg_config(0, config.psg_preamp, 0xff); else psg_config(0, config.psg_preamp, io_reg[6]); } } var.key = md_fm_channel_volume_base_str; for (c = 0; c < 6; c++) { md_fm_channel_volume_base_str[27] = c+'0'; if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var)) config.md_ch_volumes[c] = atoi(var.value); } var.key = sms_fm_channel_volume_base_str; for (c = 0; c < 9; c++) { sms_fm_channel_volume_base_str[31] = c+'0'; if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var)) config.sms_fm_ch_volumes[c] = atoi(var.value); } var.key = "genesis_plus_gx_show_advanced_audio_settings"; var.value = NULL; /* If frontend supports core option categories, * then genesis_plus_gx_show_advanced_audio_settings * is ignored and no options should be hidden */ if (!libretro_supports_option_categories && environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { bool show_advanced_av_settings_prev = show_advanced_av_settings; show_advanced_av_settings = true; if (strcmp(var.value, "disabled") == 0) show_advanced_av_settings = false; if (show_advanced_av_settings != show_advanced_av_settings_prev) { size_t i; struct retro_core_option_display option_display; char av_keys[19][40] = { "genesis_plus_gx_psg_channel_0_volume", "genesis_plus_gx_psg_channel_1_volume", "genesis_plus_gx_psg_channel_2_volume", "genesis_plus_gx_psg_channel_3_volume", "genesis_plus_gx_md_channel_0_volume", "genesis_plus_gx_md_channel_1_volume", "genesis_plus_gx_md_channel_2_volume", "genesis_plus_gx_md_channel_3_volume", "genesis_plus_gx_md_channel_4_volume", "genesis_plus_gx_md_channel_5_volume", "genesis_plus_gx_sms_fm_channel_0_volume", "genesis_plus_gx_sms_fm_channel_1_volume", "genesis_plus_gx_sms_fm_channel_2_volume", "genesis_plus_gx_sms_fm_channel_3_volume", "genesis_plus_gx_sms_fm_channel_4_volume", "genesis_plus_gx_sms_fm_channel_5_volume", "genesis_plus_gx_sms_fm_channel_6_volume", "genesis_plus_gx_sms_fm_channel_7_volume", "genesis_plus_gx_sms_fm_channel_8_volume" }; option_display.visible = show_advanced_av_settings; for (i = 0; i < 19; i++) { option_display.key = av_keys[i]; environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display); } } } #endif if (reinit) { #ifdef HAVE_OVERCLOCK overclock_delay = OVERCLOCK_FRAME_DELAY; #endif audio_init(SOUND_FREQUENCY, 0); memcpy(temp, sram.sram, sizeof(temp)); system_init(); system_reset(); memcpy(sram.sram, temp, sizeof(temp)); update_viewports = true; } if (update_viewports) { bitmap.viewport.changed = 11; if ((system_hw == SYSTEM_GG) && !config.gg_extra) bitmap.viewport.x = (config.overscan & 2) ? 14 : -48; else bitmap.viewport.x = (config.overscan & 2) * 7; } /* Reinitialise frameskipping, if required */ if ((update_frameskip || reinit) && !first_run) init_frameskip(); } /* Cheat Support */ static uint32_t decode_cheat(char *string, int index) { char *p; int i,n; uint32_t len = 0; uint32_t address = 0; uint16_t data = 0; uint8_t ref = 0; if ((system_hw & SYSTEM_PBC) == SYSTEM_MD){ /*If system is Genesis-based*/ /*Game-Genie*/ if ((strlen(string) >= 9) && (string[4] == '-')) { for (i = 0; i < 8; i++) { if (i == 4) string++; p = strchr (ggvalidchars, *string++); if (!p) return 0; n = p - ggvalidchars; switch (i) { case 0: data |= n << 3; break; case 1: data |= n >> 2; address |= (n & 3) << 14; break; case 2: address |= n << 9; break; case 3: address |= (n & 0xF) << 20 | (n >> 4) << 8; break; case 4: data |= (n & 1) << 12; address |= (n >> 1) << 16; break; case 5: data |= (n & 1) << 15 | (n >> 1) << 8; break; case 6: data |= (n >> 3) << 13; address |= (n & 7) << 5; break; case 7: address |= n; break; } } /* code length */ len = 9; } /*Patch and PAR*/ else if ((strlen(string) >=9) && (string[6] == ':')) { /* decode 24-bit address */ for (i=0; i<6; i++) { p = strchr (arvalidchars, *string++); if (!p) return 0; n = (p - arvalidchars) & 0xF; address |= (n << ((5 - i) * 4)); } /* decode 16-bit data */ string++; for (i=0; i<4; i++) { p = strchr (arvalidchars, *string++); if (!p) break; n = (p - arvalidchars) & 0xF; data |= (n << ((3 - i) * 4)); } /* code length */ len = 11; } } else { /*If System is Master-based*/ /*Game Genie*/ if ((strlen(string) >=7) && (string[3] == '-')) { /* decode 8-bit data */ for (i=0; i<2; i++) { p = strchr (arvalidchars, *string++); if (!p) return 0; n = (p - arvalidchars) & 0xF; data |= (n << ((1 - i) * 4)); } /* decode 16-bit address (low 12-bits) */ for (i=0; i<3; i++) { if (i==1) string++; /* skip separator */ p = strchr (arvalidchars, *string++); if (!p) return 0; n = (p - arvalidchars) & 0xF; address |= (n << ((2 - i) * 4)); } /* decode 16-bit address (high 4-bits) */ p = strchr (arvalidchars, *string++); if (!p) return 0; n = (p - arvalidchars) & 0xF; n ^= 0xF; /* bits inversion */ address |= (n << 12); /* Optional: decode reference 8-bit data */ if (*string=='-') { for (i=0; i<2; i++) { string++; /* skip separator and 2nd digit */ p = strchr (arvalidchars, *string++); if (!p) return 0; n = (p - arvalidchars) & 0xF; ref |= (n << ((1 - i) * 4)); } ref = (ref >> 2) | ((ref & 0x03) << 6); /* 2-bit right rotation */ ref ^= 0xBA; /* XOR */ /* code length */ len = 11; } else { /* code length */ len = 7; } } /*Action Replay*/ else if ((strlen(string) >=9) && (string[4] == '-')){ string+=2; /* decode 16-bit address */ for (i=0; i<4; i++) { p = strchr (arvalidchars, *string++); if (!p) return 0; n = (p - arvalidchars) & 0xF; address |= (n << ((3 - i) * 4)); if (i==1) string++; } /* decode 8-bit data */ for (i=0; i<2; i++) { p = strchr (arvalidchars, *string++); if (!p) return 0; n = (p - arvalidchars) & 0xF; data |= (n << ((1 - i) * 4)); } /* code length */ len = 9; } /*Fusion RAM*/ else if ((strlen(string) >=7) && (string[4] == ':')) { /* decode 16-bit address */ for (i=0; i<4; i++) { p = strchr (arvalidchars, *string++); if (!p) return 0; n = (p - arvalidchars) & 0xF; address |= (n << ((3 - i) * 4)); } /* decode 8-bit data */ string++; for (i=0; i<2; i++) { p = strchr (arvalidchars, *string++); if (!p) return 0; n = (p - arvalidchars) & 0xF; data |= (n << ((1 - i) * 4)); } /* code length */ len = 7; } /*Fusion ROM*/ else if ((strlen(string) >=9) && (string[6] == ':')) { /* decode reference 8-bit data */ for (i=0; i<2; i++) { p = strchr (arvalidchars, *string++); if (!p) return 0; n = (p - arvalidchars) & 0xF; ref |= (n << ((1 - i) * 4)); } /* decode 16-bit address */ for (i=0; i<4; i++) { p = strchr (arvalidchars, *string++); if (!p) return 0; n = (p - arvalidchars) & 0xF; address |= (n << ((3 - i) * 4)); } /* decode 8-bit data */ string++; for (i=0; i<2; i++) { p = strchr (arvalidchars, *string++); if (!p) return 0; n = (p - arvalidchars) & 0xF; data |= (n << ((1 - i) * 4)); } /* code length */ len = 9; } /* convert to 24-bit Work RAM address */ if (address >= 0xC000) address = 0xFF0000 | (address & 0x1FFF); } /* Valid code found ? */ if (len) { /* update cheat address & data values */ cheatlist[index].address = address; cheatlist[index].data = data; cheatlist[index].old = ref; } /* return code length (0 = invalid) */ return len; } static void apply_cheats(void) { uint8_t *ptr; int i; /* clear ROM&RAM patches counter */ maxROMcheats = maxRAMcheats = 0; for (i = 0; i < maxcheats; i++) { if (cheatlist[i].enable) { if (cheatlist[i].address < cart.romsize) { if ((system_hw & SYSTEM_PBC) == SYSTEM_MD) { /* patch ROM data */ cheatlist[i].old = *(uint16_t *)(cart.rom + (cheatlist[i].address & 0xFFFFFE)); *(uint16_t *)(cart.rom + (cheatlist[i].address & 0xFFFFFE)) = cheatlist[i].data; } else { /* add ROM patch */ maxROMcheats++; cheatIndexes[MAX_CHEATS - maxROMcheats] = i; /* get current banked ROM address */ ptr = &z80_readmap[(cheatlist[i].address) >> 10][cheatlist[i].address & 0x03FF]; /* check if reference matches original ROM data */ if (((uint8_t)cheatlist[i].old) == *ptr) { /* patch data */ *ptr = cheatlist[i].data; /* save patched ROM address */ cheatlist[i].prev = ptr; } else { /* no patched ROM address yet */ cheatlist[i].prev = NULL; } } } else if (cheatlist[i].address >= 0xFF0000) { /* add RAM patch */ cheatIndexes[maxRAMcheats++] = i; } } } } static void clear_cheats(void) { int i = maxcheats; /* disable cheats in reversed order in case the same address is used by multiple patches */ while (i > 0) { if (cheatlist[i-1].enable) { if (cheatlist[i-1].address < cart.romsize) { if ((system_hw & SYSTEM_PBC) == SYSTEM_MD) { /* restore original ROM data */ *(uint16_t *)(cart.rom + (cheatlist[i-1].address & 0xFFFFFE)) = cheatlist[i-1].old; } else { /* check if previous banked ROM address has been patched */ if (cheatlist[i-1].prev != NULL) { /* restore original data */ *cheatlist[i-1].prev = cheatlist[i-1].old; /* no more patched ROM address */ cheatlist[i-1].prev = NULL; } } } } i--; } } /**************************************************************************** * RAMCheatUpdate * * Apply RAM patches (this should be called once per frame) * ****************************************************************************/ static void RAMCheatUpdate(void) { int index, cnt = maxRAMcheats; while (cnt) { /* get cheat index */ index = cheatIndexes[--cnt]; /* apply RAM patch */ if (cheatlist[index].data & 0xFF00) { /* 16-bit patch */ *(uint16_t *)(work_ram + (cheatlist[index].address & 0xFFFE)) = cheatlist[index].data; } else { /* 8-bit patch */ work_ram[cheatlist[index].address & 0xFFFF] = cheatlist[index].data; } } } /**************************************************************************** * ROMCheatUpdate * * Apply ROM patches (this should be called each time banking is changed) * ****************************************************************************/ void ROMCheatUpdate(void) { int index, cnt = maxROMcheats; uint8_t *ptr; while (cnt) { /* get cheat index */ index = cheatIndexes[MAX_CHEATS - cnt]; /* check if previous banked ROM address was patched */ if (cheatlist[index].prev != NULL) { /* restore original data */ *cheatlist[index].prev = cheatlist[index].old; /* no more patched ROM address */ cheatlist[index].prev = NULL; } /* get current banked ROM address */ ptr = &z80_readmap[(cheatlist[index].address) >> 10][cheatlist[index].address & 0x03FF]; /* check if reference exists and matches original ROM data */ if (!cheatlist[index].old || ((uint8_t)cheatlist[index].old) == *ptr) { /* patch data */ *ptr = cheatlist[index].data; /* save patched ROM address */ cheatlist[index].prev = ptr; } /* next ROM patch */ cnt--; } } static void set_memory_maps() { if (system_hw == SYSTEM_MCD) { const size_t SCD_BIT = 1ULL << 31ULL; const uint64_t mem = RETRO_MEMDESC_SYSTEM_RAM; struct retro_memory_map mmaps; struct retro_memory_descriptor descs[] = { { mem, work_ram, 0, 0xFF0000, 0, 0, 0x10000, "68KRAM" }, /* virtual address using SCD_BIT so all 512M of prg_ram can be accessed */ /* at address $80020000 */ { mem, scd.prg_ram, 0, SCD_BIT | 0x020000, 0, 0, 0x80000, "PRGRAM" }, }; mmaps.descriptors = descs; mmaps.num_descriptors = sizeof(descs) / sizeof(descs[0]); environ_cb(RETRO_ENVIRONMENT_SET_MEMORY_MAPS, &mmaps); } } /**************************************************************************** * Disk control interface ****************************************************************************/ #define MAX_DISKS 4 static int disk_index; static int disk_count; static char *disk_info[MAX_DISKS]; static bool disk_set_eject_state(bool ejected) { if (system_hw != SYSTEM_MCD) return false; if (ejected) { cdd.status = CD_OPEN; scd.regs[0x36>>1].byte.h = 0x01; } else if (cdd.status == CD_OPEN) { cdd.status = cdd.loaded ? CD_TOC : NO_DISC; } return true; } static bool disk_get_eject_state(void) { if (system_hw != SYSTEM_MCD) return false; return (cdd.status == CD_OPEN); } static unsigned int disk_get_image_index(void) { if ((system_hw != SYSTEM_MCD) || !cdd.loaded) return disk_count; return disk_index; } static bool disk_set_image_index(unsigned int index) { char header[0x210]; if (system_hw != SYSTEM_MCD) return false; if (index >= disk_count) { cdd.loaded = 0; return true; } if (disk_info[index] == NULL) return false; cdd_load(disk_info[index], header); if (!cdd.loaded) return false; disk_index = index; return true; } static unsigned int disk_get_num_images(void) { return disk_count; } static bool disk_replace_image_index(unsigned index, const struct retro_game_info *info) { if (system_hw != SYSTEM_MCD) return false; if (index >= disk_count) return false; if (disk_info[index] != NULL) free(disk_info[index]); disk_info[index] = NULL; if (info != NULL) { if (info->path == NULL) return false; disk_info[index] = strdup(info->path); if (index == disk_index) return disk_set_image_index(index); } else { int i = index; while (i < (disk_count-1)) { disk_info[i] = disk_info[i+1]; disk_info[i+1] = NULL; } disk_count--; if (index < disk_index) disk_index--; } return true; } static bool disk_add_image_index(void) { if (system_hw != SYSTEM_MCD) return false; if (disk_count >= MAX_DISKS) return false; disk_count++; return true; } static struct retro_disk_control_callback disk_ctrl = { disk_set_eject_state, disk_get_eject_state, disk_get_image_index, disk_set_image_index, disk_get_num_images, disk_replace_image_index, disk_add_image_index }; /************************************ * libretro implementation ************************************/ unsigned retro_api_version(void) { return RETRO_API_VERSION; } void retro_set_environment(retro_environment_t cb) { struct retro_vfs_interface_info vfs_iface_info; static const struct retro_controller_description port_1[] = { { "Joypad Auto", RETRO_DEVICE_JOYPAD }, { "Joypad Port Empty", RETRO_DEVICE_NONE }, { "MD Joypad 3 Button", RETRO_DEVICE_MDPAD_3B }, { "MD Joypad 6 Button", RETRO_DEVICE_MDPAD_6B }, { "MS Joypad 2 Button", RETRO_DEVICE_MSPAD_2B }, { "MD Joypad 3 Button + 4-WayPlay", RETRO_DEVICE_MDPAD_3B_WAYPLAY }, { "MD Joypad 6 Button + 4-WayPlay", RETRO_DEVICE_MDPAD_6B_WAYPLAY }, { "MD Joypad 3 Button + Teamplayer", RETRO_DEVICE_MDPAD_3B_TEAMPLAYER }, { "MD Joypad 6 Button + Teamplayer", RETRO_DEVICE_MDPAD_6B_TEAMPLAYER }, { "MS Joypad 2 Button + Master Tap", RETRO_DEVICE_MSPAD_2B_MASTERTAP }, { "MS Light Phaser", RETRO_DEVICE_PHASER }, { "MS Paddle Control", RETRO_DEVICE_PADDLE }, { "MS Sports Pad", RETRO_DEVICE_SPORTSPAD }, { "MS Graphic Board", RETRO_DEVICE_GRAPHIC_BOARD }, { "MD XE-1AP", RETRO_DEVICE_XE_1AP }, { "MD Mouse", RETRO_DEVICE_MOUSE }, }; static const struct retro_controller_description port_2[] = { { "Joypad Auto", RETRO_DEVICE_JOYPAD }, { "Joypad Port Empty", RETRO_DEVICE_NONE }, { "MD Joypad 3 Button", RETRO_DEVICE_MDPAD_3B }, { "MD Joypad 6 Button", RETRO_DEVICE_MDPAD_6B }, { "MS Joypad 2 Button", RETRO_DEVICE_MSPAD_2B }, { "MD Joypad 3 Button + 4-WayPlay", RETRO_DEVICE_MDPAD_3B_WAYPLAY }, { "MD Joypad 6 Button + 4-WayPlay", RETRO_DEVICE_MDPAD_6B_WAYPLAY }, { "MD Joypad 3 Button + Teamplayer", RETRO_DEVICE_MDPAD_3B_TEAMPLAYER }, { "MD Joypad 6 Button + Teamplayer", RETRO_DEVICE_MDPAD_6B_TEAMPLAYER }, { "MS Joypad 2 Button + Master Tap", RETRO_DEVICE_MSPAD_2B_MASTERTAP }, { "MD Menacer", RETRO_DEVICE_MENACER }, { "MD Justifiers", RETRO_DEVICE_JUSTIFIERS }, { "MS Light Phaser", RETRO_DEVICE_PHASER }, { "MS Paddle Control", RETRO_DEVICE_PADDLE }, { "MS Sports Pad", RETRO_DEVICE_SPORTSPAD }, { "MS Graphic Board", RETRO_DEVICE_GRAPHIC_BOARD }, { "MD XE-1AP", RETRO_DEVICE_XE_1AP }, { "MD Mouse", RETRO_DEVICE_MOUSE }, }; static const struct retro_controller_info ports[] = { { port_1, 16 }, { port_2, 18 }, { 0 }, }; static const struct retro_input_descriptor desc[] = { { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" }, { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" }, { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" }, { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" }, { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" }, { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "C" }, { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Y" }, { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "A" }, { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "X" }, { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "Z" }, { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Mode" }, { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" }, { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" }, { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" }, { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" }, { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" }, { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" }, { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "C" }, { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Y" }, { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "A" }, { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "X" }, { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "Z" }, { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Mode" }, { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" }, { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" }, { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" }, { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" }, { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" }, { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" }, { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "C" }, { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Y" }, { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "A" }, { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "X" }, { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "Z" }, { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Mode" }, { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" }, { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" }, { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" }, { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" }, { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" }, { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" }, { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "C" }, { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Y" }, { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "A" }, { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "X" }, { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "Z" }, { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Mode" }, { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" }, { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" }, { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" }, { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" }, { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" }, { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" }, { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "C" }, { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Y" }, { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "A" }, { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "X" }, { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "Z" }, { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Mode" }, { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" }, { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" }, { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" }, { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" }, { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" }, { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" }, { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "C" }, { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Y" }, { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "A" }, { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "X" }, { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "Z" }, { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Mode" }, { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" }, { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" }, { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" }, { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" }, { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" }, { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" }, { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "C" }, { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Y" }, { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "A" }, { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "X" }, { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "Z" }, { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Mode" }, { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" }, { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" }, { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" }, { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" }, { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" }, { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" }, { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "C" }, { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Y" }, { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "A" }, { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "X" }, { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "Z" }, { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Mode" }, { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" }, { 0 }, }; static const struct retro_system_content_info_override content_overrides[] = { { "mdx|md|smd|gen|bms|sms|gg|sg|68k|sgd", /* extensions */ #if defined(LOW_MEMORY) true, /* need_fullpath */ #else false, /* need_fullpath */ #endif false /* persistent_data */ }, { NULL, false, false } }; environ_cb = cb; libretro_supports_option_categories = false; libretro_set_core_options(environ_cb, &libretro_supports_option_categories); /* If frontend supports core option categories, * genesis_plus_gx_show_advanced_audio_settings * is unused and should be hidden */ if (libretro_supports_option_categories) { struct retro_core_option_display option_display; option_display.visible = false; option_display.key = "genesis_plus_gx_show_advanced_audio_settings"; environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display); } cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports); cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, (void*)desc); cb(RETRO_ENVIRONMENT_SET_CONTENT_INFO_OVERRIDE, (void*)content_overrides); vfs_iface_info.required_interface_version = 1; vfs_iface_info.iface = NULL; if (environ_cb(RETRO_ENVIRONMENT_GET_VFS_INTERFACE, &vfs_iface_info)) filestream_vfs_init(&vfs_iface_info); } void retro_set_video_refresh(retro_video_refresh_t cb) { video_cb = cb; } void retro_set_audio_sample(retro_audio_sample_t cb) { (void)cb; } void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) { audio_cb = cb; } void retro_set_input_poll(retro_input_poll_t cb) { input_poll_cb = cb; } void retro_set_input_state(retro_input_state_t cb) { input_state_cb = cb; } void retro_get_system_info(struct retro_system_info *info) { info->library_name = "Genesis Plus GX"; #ifndef GIT_VERSION #define GIT_VERSION "" #endif info->library_version = "v1.7.4" GIT_VERSION; info->valid_extensions = "m3u|mdx|md|smd|gen|bin|cue|iso|chd|bms|sms|gg|sg|68k|sgd"; info->block_extract = false; info->need_fullpath = true; } void retro_get_system_av_info(struct retro_system_av_info *info) { info->geometry.base_width = vwidth; info->geometry.base_height = bitmap.viewport.h + (2 * bitmap.viewport.y); /* Set maximum dimensions based upon emulated system/config */ if ((system_hw & SYSTEM_PBC) == SYSTEM_MD) { /* 16 bit system */ if (config.ntsc) { info->geometry.max_width = MD_NTSC_OUT_WIDTH(320 + (bitmap.viewport.x * 2)); } else { info->geometry.max_width = 320 + (bitmap.viewport.x * 2); } if (config.render) { info->geometry.max_height = 480 + (vdp_pal * 96 * (config.overscan & 1)); } else { info->geometry.max_height = 240 + (vdp_pal * 48 * (config.overscan & 1)); } } else { /* 8 bit system */ if (config.ntsc) { info->geometry.max_width = SMS_NTSC_OUT_WIDTH(256 + (bitmap.viewport.x * 2)); } else { info->geometry.max_width = 256 + (bitmap.viewport.x * 2); } info->geometry.max_height = 240 + (vdp_pal * 48 * (config.overscan & 1)); } info->geometry.aspect_ratio = vaspect_ratio; info->timing.fps = (double)(system_clock) / (double)lines_per_frame / (double)MCYCLES_PER_LINE; info->timing.sample_rate = SOUND_FREQUENCY; } void retro_set_controller_port_device(unsigned port, unsigned device) { if (port > 1) return; switch(device) { case RETRO_DEVICE_NONE: input.system[port] = NO_SYSTEM; break; case RETRO_DEVICE_MDPAD_3B: { if (port && (input.system[0] >= SYSTEM_MASTERTAP) && (input.system[0] <= SYSTEM_WAYPLAY)) config.input[4].padtype = DEVICE_PAD3B; else config.input[port].padtype = DEVICE_PAD3B; input.system[port] = SYSTEM_GAMEPAD; break; } case RETRO_DEVICE_MDPAD_6B: { if (port && (input.system[0] >= SYSTEM_MASTERTAP) && (input.system[0] <= SYSTEM_WAYPLAY)) config.input[4].padtype = DEVICE_PAD6B; else config.input[port].padtype = DEVICE_PAD6B; input.system[port] = SYSTEM_GAMEPAD; break; } case RETRO_DEVICE_MSPAD_2B: { if (port && (input.system[0] >= SYSTEM_MASTERTAP) && (input.system[0] <= SYSTEM_WAYPLAY)) config.input[4].padtype = DEVICE_PAD2B; else config.input[port].padtype = DEVICE_PAD2B; input.system[port] = SYSTEM_GAMEPAD; break; } case RETRO_DEVICE_MDPAD_3B_WAYPLAY: { int i; for (i=0; i<4; i++) { config.input[i].padtype = DEVICE_PAD3B; } input.system[0] = input.system[1] = SYSTEM_WAYPLAY; break; } case RETRO_DEVICE_MDPAD_6B_WAYPLAY: { int i; for (i=0; i<4; i++) { config.input[i].padtype = DEVICE_PAD6B; } input.system[0] = input.system[1] = SYSTEM_WAYPLAY; break; } case RETRO_DEVICE_MDPAD_3B_TEAMPLAYER: { int i; for (i=0; i<4; i++) { config.input[port*4 + i].padtype = DEVICE_PAD3B; } input.system[port] = SYSTEM_TEAMPLAYER; break; } case RETRO_DEVICE_MDPAD_6B_TEAMPLAYER: { int i; for (i=0; i<4; i++) { config.input[port*4 + i].padtype = DEVICE_PAD6B; } input.system[port] = SYSTEM_TEAMPLAYER; break; } case RETRO_DEVICE_MSPAD_2B_MASTERTAP: { int i; for (i=0; i<4; i++) { config.input[port*4 + i].padtype = DEVICE_PAD2B; } input.system[port] = SYSTEM_MASTERTAP; break; } case RETRO_DEVICE_MENACER: input.system[1] = SYSTEM_MENACER; break; case RETRO_DEVICE_JUSTIFIERS: input.system[1] = SYSTEM_JUSTIFIER; break; case RETRO_DEVICE_PHASER: input.system[port] = SYSTEM_LIGHTPHASER; break; case RETRO_DEVICE_PADDLE: input.system[port] = SYSTEM_PADDLE; break; case RETRO_DEVICE_SPORTSPAD: input.system[port] = SYSTEM_SPORTSPAD; break; case RETRO_DEVICE_XE_1AP: input.system[port] = SYSTEM_XE_1AP; break; case RETRO_DEVICE_MOUSE: input.system[port] = SYSTEM_MOUSE; break; case RETRO_DEVICE_GRAPHIC_BOARD: input.system[port] = SYSTEM_GRAPHIC_BOARD; break; case RETRO_DEVICE_JOYPAD: default: { if (port && (input.system[0] >= SYSTEM_MASTERTAP) && (input.system[0] <= SYSTEM_WAYPLAY)) config.input[4].padtype = DEVICE_PAD2B | DEVICE_PAD6B | DEVICE_PAD3B; else config.input[port].padtype = DEVICE_PAD2B | DEVICE_PAD6B | DEVICE_PAD3B; input.system[port] = SYSTEM_GAMEPAD; break; } } old_system[0] = input.system[0]; old_system[1] = input.system[1]; io_init(); input_reset(); } size_t retro_serialize_size(void) { return STATE_SIZE; } bool retro_serialize(void *data, size_t size) { if (size != STATE_SIZE) return FALSE; state_save(data); return TRUE; } bool retro_unserialize(const void *data, size_t size) { if (size != STATE_SIZE) return FALSE; if (!state_load((uint8_t*)data)) return FALSE; #ifdef HAVE_OVERCLOCK update_overclock(); #endif return TRUE; } void retro_cheat_reset(void) { /* clear existing ROM patches */ clear_cheats(); /* delete all cheats */ maxcheats = maxROMcheats = maxRAMcheats = 0; } void retro_cheat_set(unsigned index, bool enabled, const char *code) { char codeCopy[256]; char *buff; /* Avoid crashing when giving no input */ if (code==NULL) return; /* clear existing ROM patches */ clear_cheats(); /* Detect and split multiline cheats */ strncpy(codeCopy,code,255); codeCopy[255] = '\0'; buff = strtok(codeCopy,"+"); while (buff != NULL) { /* interpret code and check if this is a valid cheat code */ if (decode_cheat((char *)buff, maxcheats)) { int i; /* check if cheat code already exists */ for (i=0; idata; g_rom_size = info_ext->size; #endif strncpy(g_rom_dir, info_ext->dir, sizeof(g_rom_dir)); g_rom_dir[sizeof(g_rom_dir) - 1] = '\0'; strncpy(g_rom_name, info_ext->name, sizeof(g_rom_name)); g_rom_name[sizeof(g_rom_name) - 1] = '\0'; strncpy(content_ext, info_ext->ext, sizeof(content_ext)); content_ext[sizeof(content_ext) - 1] = '\0'; if (info_ext->file_in_archive) { /* We don't have a 'physical' file in this * case, but the core still needs a filename * in order to detect associated content * (Mega CD Mode 1/MegaSD MD+ mode). We therefore * fake it, using the content directory, * canonical content name, and content file * extension */ snprintf(content_path, sizeof(content_path), "%s%c%s.%s", g_rom_dir, slash, g_rom_name, content_ext); } else { strncpy(content_path, info_ext->full_path, sizeof(content_path)); content_path[sizeof(content_path) - 1] = '\0'; } } else { const char *ext = NULL; if (!info || !info->path) return false; extract_directory(g_rom_dir, info->path, sizeof(g_rom_dir)); extract_name(g_rom_name, info->path, sizeof(g_rom_name)); strncpy(content_path, info->path, sizeof(content_path)); content_path[sizeof(content_path) - 1] = '\0'; if (ext = strrchr(info->path, '.')) { strncpy(content_ext, ext + 1, sizeof(content_ext)); content_ext[sizeof(content_ext) - 1] = '\0'; } } #ifdef FRONTEND_SUPPORTS_RGB565 { unsigned rgb565 = RETRO_PIXEL_FORMAT_RGB565; if(environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &rgb565)) if (log_cb) log_cb(RETRO_LOG_INFO, "Frontend supports RGB565 - will use that instead of XRGB1555.\n"); } #endif sms_ntsc = calloc(1, sizeof(sms_ntsc_t)); md_ntsc = calloc(1, sizeof(md_ntsc_t)); init_bitmap(); config_default(); if (!environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir) || !dir) { if (log_cb) log_cb(RETRO_LOG_INFO, "[genplus]: Defaulting system directory to %s.\n", g_rom_dir); dir = g_rom_dir; } if (!environ_cb(RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY, &save_dir) || !save_dir) { if (log_cb) log_cb(RETRO_LOG_INFO, "[genplus]: Defaulting save directory to %s.\n", g_rom_dir); save_dir = g_rom_dir; } snprintf(GG_ROM, sizeof(GG_ROM), "%s%cggenie.bin", dir, slash); snprintf(AR_ROM, sizeof(AR_ROM), "%s%careplay.bin", dir, slash); snprintf(SK_ROM, sizeof(SK_ROM), "%s%csk.bin", dir, slash); snprintf(SK_UPMEM, sizeof(SK_UPMEM), "%s%csk2chip.bin", dir, slash); snprintf(MD_BIOS, sizeof(MD_BIOS), "%s%cbios_MD.bin", dir, slash); snprintf(GG_BIOS, sizeof(GG_BIOS), "%s%cbios.gg", dir, slash); snprintf(MS_BIOS_EU, sizeof(MS_BIOS_EU), "%s%cbios_E.sms", dir, slash); snprintf(MS_BIOS_US, sizeof(MS_BIOS_US), "%s%cbios_U.sms", dir, slash); snprintf(MS_BIOS_JP, sizeof(MS_BIOS_JP), "%s%cbios_J.sms", dir, slash); snprintf(CD_BIOS_EU, sizeof(CD_BIOS_EU), "%s%cbios_CD_E.bin", dir, slash); snprintf(CD_BIOS_US, sizeof(CD_BIOS_US), "%s%cbios_CD_U.bin", dir, slash); snprintf(CD_BIOS_JP, sizeof(CD_BIOS_JP), "%s%cbios_CD_J.bin", dir, slash); snprintf(CART_BRAM, sizeof(CART_BRAM), "%s%ccart.brm", save_dir, slash); check_variables(true); if (log_cb) { log_cb(RETRO_LOG_INFO, "Game Genie ROM should be located at: %s\n", GG_ROM); log_cb(RETRO_LOG_INFO, "Action Replay (Pro) ROM should be located at: %s\n", AR_ROM); log_cb(RETRO_LOG_INFO, "Sonic & Knuckles (2 MB) ROM should be located at: %s\n", SK_ROM); log_cb(RETRO_LOG_INFO, "Sonic & Knuckles UPMEM (256 KB) ROM should be located at: %s\n", SK_UPMEM); log_cb(RETRO_LOG_INFO, "Mega Drive TMSS BOOTROM should be located at: %s\n", MD_BIOS); log_cb(RETRO_LOG_INFO, "Game Gear TMSS BOOTROM should be located at: %s\n", GG_BIOS); log_cb(RETRO_LOG_INFO, "Master System (PAL) BOOTROM should be located at: %s\n", MS_BIOS_EU); log_cb(RETRO_LOG_INFO, "Master System (NTSC-U) BOOTROM should be located at: %s\n", MS_BIOS_US); log_cb(RETRO_LOG_INFO, "Master System (NTSC-J) BOOTROM should be located at: %s\n", MS_BIOS_JP); log_cb(RETRO_LOG_INFO, "Mega CD (PAL) BIOS should be located at: %s\n", CD_BIOS_EU); log_cb(RETRO_LOG_INFO, "Sega CD (NTSC-U) BIOS should be located at: %s\n", CD_BIOS_US); log_cb(RETRO_LOG_INFO, "Mega CD (NTSC-J) BIOS should be located at: %s\n", CD_BIOS_JP); log_cb(RETRO_LOG_INFO, "Mega CD (PAL) BRAM is located at: %s\n", CD_BRAM_EU); log_cb(RETRO_LOG_INFO, "Sega CD (NTSC-U) BRAM is located at: %s\n", CD_BRAM_US); log_cb(RETRO_LOG_INFO, "Mega CD (NTSC-J) BRAM is located at: %s\n", CD_BRAM_JP); log_cb(RETRO_LOG_INFO, "Sega/Mega CD RAM CART is located at: %s\n", CART_BRAM); } /* Clear disk interface (already done in retro_unload_game but better be safe) */ disk_count = 0; disk_index = 0; for (i=0; i 0) { /* remove end-of-line character */ if (linebuf[len-1] == '\n') { linebuf[len-1] = 0; len--; } /* remove carriage-return character */ if (len && (linebuf[len-1] == '\r')) { linebuf[len-1] = 0; len--; } /* append file path to disk image file name */ disk_info[disk_count] = malloc(strlen(g_rom_dir) + len + 2); if (disk_info[disk_count] != NULL) { /* add file to disk interface */ sprintf(disk_info[disk_count], "%s%c%s", g_rom_dir, slash, linebuf); disk_count++; if (log_cb) log_cb(RETRO_LOG_INFO, "Disk #%d added from M3U file list: %s\n", disk_count, disk_info[disk_count-1]); } } } } } /* automatically try to load first disk if at least one file was added to disk interface from M3U file list */ if (disk_count > 0) { if (!load_rom(disk_info[0])) { if (log_cb) log_cb(RETRO_LOG_ERROR, "Could not load %s from M3U file list\n", disk_info[0]); /* clear initialized disk interface before returning an error */ for (i=0; i 0) { if (!memcmp((char *)(boot_rom + 0x120),"GENESIS OS", 10)) { system_bios |= SYSTEM_MD; } #ifdef LSB_FIRST for (i=0; i<0x800; i+=2) { uint8 temp = boot_rom[i]; boot_rom[i] = boot_rom[i+1]; boot_rom[i+1] = temp; } #endif } } audio_init(SOUND_FREQUENCY, 0); system_init(); system_reset(); is_running = false; if (system_hw == SYSTEM_MCD) bram_load(); update_viewport(); #ifdef HAVE_OVERCLOCK overclock_delay = OVERCLOCK_FRAME_DELAY; update_overclock(); #endif set_memory_maps(); init_frameskip(); return true; } bool retro_load_game_special(unsigned game_type, const struct retro_game_info *info, size_t num_info) { (void)game_type; (void)info; (void)num_info; return FALSE; } void retro_unload_game(void) { /* Clear disk interface */ int i; disk_count = 0; disk_index = 0; for (i=0; i=0; i--) { if (sram.sram[i] != 0xff) { /* only save modified size */ return (i+1); } } } case RETRO_MEMORY_SYSTEM_RAM: if (system_hw == SYSTEM_SMS || system_hw == SYSTEM_SMS2 || system_hw == SYSTEM_GG || system_hw == SYSTEM_GGMS) return 0x02000; else return 0x10000; default: return 0; } } static void check_system_specs(void) { unsigned level = 7; environ_cb(RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL, &level); } void retro_init(void) { struct retro_log_callback log; unsigned level = 1; uint64_t serialization_quirks = RETRO_SERIALIZATION_QUIRK_PLATFORM_DEPENDENT; 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; if (environ_cb(RETRO_ENVIRONMENT_GET_INPUT_BITMASKS, NULL)) libretro_supports_bitmasks = true; check_system_specs(); environ_cb(RETRO_ENVIRONMENT_SET_SERIALIZATION_QUIRKS, &serialization_quirks); environ_cb(RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE, &disk_ctrl); frameskip_type = 0; frameskip_threshold = 0; frameskip_counter = 0; retro_audio_buff_active = false; retro_audio_buff_occupancy = 0; retro_audio_buff_underrun = false; audio_latency = 0; update_audio_latency = false; } void retro_deinit(void) { libretro_supports_option_categories = false; libretro_supports_bitmasks = false; g_rom_data = NULL; g_rom_size = 0; } void retro_reset(void) { #ifdef HAVE_OVERCLOCK overclock_delay = OVERCLOCK_FRAME_DELAY; update_overclock(); #endif gen_reset(0); } void retro_run(void) { int do_skip = 0; bool updated = false; int vwoffset = 0; int bmdoffset = 0; is_running = true; #ifdef HAVE_OVERCLOCK /* update overclock delay */ if (overclock_delay && --overclock_delay == 0) update_overclock(); #endif environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated); if (updated) { check_variables(false); if (restart_eq) { audio_set_equalizer(); restart_eq = false; } } /* Check whether current frame should * be skipped */ if ((frameskip_type > 0) && retro_audio_buff_active && !do_skip) { switch (frameskip_type) { case 1: /* auto */ do_skip = retro_audio_buff_underrun ? 1 : 0; break; case 2: /* manual */ do_skip = (retro_audio_buff_occupancy < frameskip_threshold) ? 1 : 0; break; default: do_skip = 0; break; } if (!do_skip || (frameskip_counter >= FRAMESKIP_MAX)) { do_skip = 0; frameskip_counter = 0; } else frameskip_counter++; } /* If frameskip settings have changed, update * frontend audio latency */ if (update_audio_latency) { environ_cb(RETRO_ENVIRONMENT_SET_MINIMUM_AUDIO_LATENCY, &audio_latency); update_audio_latency = false; } if (system_hw == SYSTEM_MCD) { system_frame_scd(do_skip); } else if ((system_hw & SYSTEM_PBC) == SYSTEM_MD) { system_frame_gen(do_skip); } else { system_frame_sms(do_skip); } if (bitmap.viewport.changed & 9) { bool geometry_updated = update_viewport(); bitmap.viewport.changed &= ~1; if (bitmap.viewport.changed & 8) { struct retro_system_av_info info; bitmap.viewport.changed &= ~8; retro_get_system_av_info(&info); environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &info); } else if (geometry_updated) { struct retro_system_av_info info; retro_get_system_av_info(&info); environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &info.geometry); } } if (config.gun_cursor) { if (input.system[0] == SYSTEM_LIGHTPHASER) { draw_cursor(input.analog[0][0], input.analog[0][1], 0x001f); } else if (input.dev[4] == DEVICE_LIGHTGUN) { draw_cursor(input.analog[4][0], input.analog[4][1], 0x001f); } if (input.system[1] == SYSTEM_LIGHTPHASER) { draw_cursor(input.analog[4][0], input.analog[4][1], 0xf800); } else if (input.dev[5] == DEVICE_LIGHTGUN) { draw_cursor(input.analog[5][0], input.analog[5][1], 0xf800); } } if ((config.left_border != 0) && (reg[0] & 0x20) && (bitmap.viewport.x == 0) && ((system_hw == SYSTEM_MARKIII) || (system_hw & SYSTEM_SMS) || (system_hw == SYSTEM_PBC))) { bmdoffset = (16 + (config.ntsc ? 24 : 0)); if (config.left_border == 1) vwoffset = (8 + (config.ntsc ? 12 : 0)); else vwoffset = (16 + (config.ntsc ? 24 : 0)); } if (!do_skip) { video_cb(bitmap.data + bmdoffset, vwidth - vwoffset, vheight, 720 * 2); } else { video_cb(NULL, vwidth - vwoffset, vheight, 720 * 2); } audio_cb(soundbuffer, audio_update(soundbuffer)); } #undef CHUNKSIZE