From 2135365d5cd1088882e8fb1b949513761b0c3301 Mon Sep 17 00:00:00 2001 From: Brian Koropoff Date: Sun, 1 Oct 2017 23:05:07 -0700 Subject: [PATCH] Add experimental overclock option. This reduces all cycle costs by half for the primary CPU. There is a delay upon loading before overclocking is applied as a workaround for some games that detect PAL/NTSC systems by counting cycles in a frame at startup. --- Makefile.libretro | 8 +++-- core/m68k/m68k.h | 4 +++ core/m68k/m68kcpu.c | 4 +++ core/m68k/m68kcpu.h | 4 +++ core/m68k/s68kcpu.c | 4 +++ core/z80/z80.c | 29 +++++++++++++----- core/z80/z80.h | 4 +++ libretro/libretro.c | 74 +++++++++++++++++++++++++++++++++++++++++++++ libretro/osd.h | 1 + 9 files changed, 121 insertions(+), 11 deletions(-) diff --git a/Makefile.libretro b/Makefile.libretro index e265ab3..4cc673f 100644 --- a/Makefile.libretro +++ b/Makefile.libretro @@ -437,9 +437,11 @@ endif LIBRETRO_CFLAGS += $(INCFLAGS) $(INCFLAGS_PLATFORM) LIBRETRO_CFLAGS += $(BPP_DEFINES) \ - $(ENDIANNESS_DEFINES) \ - $(PLATFORM_DEFINES) \ - -D__LIBRETRO__ + $(ENDIANNESS_DEFINES) \ + $(PLATFORM_DEFINES) \ + -D__LIBRETRO__ \ + -DM68K_ALLOW_OVERCLOCK \ + -DZ80_ALLOW_OVERCLOCK ifneq (,$(findstring msvc,$(platform))) LIBRETRO_CFLAGS += -DINLINE="static _inline" diff --git a/core/m68k/m68k.h b/core/m68k/m68k.h index 7c84334..93bb033 100644 --- a/core/m68k/m68k.h +++ b/core/m68k/m68k.h @@ -268,6 +268,10 @@ typedef struct uint address_space; /* Current FC code */ +#ifdef M68K_ALLOW_OVERCLOCK + uint8 overclock_ratio; +#endif + /* Callbacks to host */ int (*int_ack_callback)(int int_line); /* Interrupt Acknowledge */ void (*reset_instr_callback)(void); /* Called when a RESET instruction is encountered */ diff --git a/core/m68k/m68kcpu.c b/core/m68k/m68kcpu.c index e7d762e..245bef9 100644 --- a/core/m68k/m68kcpu.c +++ b/core/m68k/m68kcpu.c @@ -319,6 +319,10 @@ void m68k_init(void) } #endif +#ifdef M68K_ALLOW_OVERCLOCK + m68k.overclock_ratio = 1; +#endif + #if M68K_EMULATE_INT_ACK == OPT_ON m68k_set_int_ack_callback(NULL); #endif diff --git a/core/m68k/m68kcpu.h b/core/m68k/m68kcpu.h index de394b5..8b3355e 100644 --- a/core/m68k/m68kcpu.h +++ b/core/m68k/m68kcpu.h @@ -514,7 +514,11 @@ /* ---------------------------- Cycle Counting ---------------------------- */ +#ifdef M68K_ALLOW_OVERCLOCK +#define USE_CYCLES(A) m68ki_cpu.cycles += (A) / m68ki_cpu.overclock_ratio +#else #define USE_CYCLES(A) m68ki_cpu.cycles += (A) +#endif #define SET_CYCLES(A) m68ki_cpu.cycles = (A) diff --git a/core/m68k/s68kcpu.c b/core/m68k/s68kcpu.c index 7de4652..9665085 100644 --- a/core/m68k/s68kcpu.c +++ b/core/m68k/s68kcpu.c @@ -284,6 +284,10 @@ void s68k_init(void) } #endif +#ifdef M68K_ALLOW_OVERCLOCK + s68k.overclock_ratio = 1; +#endif + #if M68K_EMULATE_INT_ACK == OPT_ON s68k_set_int_ack_callback(NULL); #endif diff --git a/core/z80/z80.c b/core/z80/z80.c index 7a1754d..59d7f4f 100644 --- a/core/z80/z80.c +++ b/core/z80/z80.c @@ -201,8 +201,18 @@ #define IFF2 Z80.iff2 #define HALT Z80.halt +#ifdef Z80_ALLOW_OVERCLOCK +#define USE_CYCLES(A) Z80.cycles += (A) / z80_overclock_ratio +#else +#define USE_CYCLES(A) Z80.cycles += (A) +#endif + Z80_Regs Z80; +#ifdef Z80_ALLOW_OVERCLOCK +UINT8 z80_overclock_ratio; +#endif + unsigned char *z80_readmap[64]; unsigned char *z80_writemap[64]; @@ -473,7 +483,7 @@ INLINE void BURNODD(int cycles, int opcodes, int cyclesum) if( cycles > 0 ) { R += (cycles / cyclesum) * opcodes; - Z80.cycles += (cycles / cyclesum) * cyclesum * 15; + USE_CYCLES((cycles / cyclesum) * cyclesum * 15); } } @@ -485,7 +495,7 @@ INLINE void BURNODD(int cycles, int opcodes, int cyclesum) /*************************************************************** * adjust cycle count by n T-states ***************************************************************/ -#define CC(prefix,opcode) Z80.cycles += cc[Z80_TABLE_##prefix][opcode] +#define CC(prefix,opcode) USE_CYCLES(cc[Z80_TABLE_##prefix][opcode]) /*************************************************************** * execute an opcode @@ -3228,7 +3238,7 @@ static void take_interrupt(void) PUSH( pc ); PCD = 0x0038; /* RST $38 + 'interrupt latency' cycles */ - Z80.cycles += cc[Z80_TABLE_op][0xff] + cc[Z80_TABLE_ex][0xff]; + USE_CYCLES(cc[Z80_TABLE_op][0xff] + cc[Z80_TABLE_ex][0xff]); } else { @@ -3243,7 +3253,7 @@ static void take_interrupt(void) RM16( irq_vector, &Z80.pc ); LOG(("Z80 #%d IM2 [$%04x] = $%04x\n",cpu_getactivecpu() , irq_vector, PCD)); /* CALL $xxxx + 'interrupt latency' cycles */ - Z80.cycles += cc[Z80_TABLE_op][0xcd] + cc[Z80_TABLE_ex][0xff]; + USE_CYCLES(cc[Z80_TABLE_op][0xcd] + cc[Z80_TABLE_ex][0xff]); } else { @@ -3257,18 +3267,18 @@ static void take_interrupt(void) PUSH( pc ); PCD = irq_vector & 0xffff; /* CALL $xxxx + 'interrupt latency' cycles */ - Z80.cycles += cc[Z80_TABLE_op][0xcd] + cc[Z80_TABLE_ex][0xff]; + USE_CYCLES(cc[Z80_TABLE_op][0xcd] + cc[Z80_TABLE_ex][0xff]); break; case 0xc30000: /* jump */ PCD = irq_vector & 0xffff; /* JP $xxxx + 2 cycles */ - Z80.cycles += cc[Z80_TABLE_op][0xc3] + cc[Z80_TABLE_ex][0xff]; + USE_CYCLES(cc[Z80_TABLE_op][0xc3] + cc[Z80_TABLE_ex][0xff]); break; default: /* rst (or other opcodes?) */ PUSH( pc ); PCD = irq_vector & 0x0038; /* RST $xx + 2 cycles */ - Z80.cycles += cc[Z80_TABLE_op][0xff] + cc[Z80_TABLE_ex][0xff]; + USE_CYCLES(cc[Z80_TABLE_op][0xff] + cc[Z80_TABLE_ex][0xff]); break; } } @@ -3358,6 +3368,9 @@ void z80_init(const void *config, int (*irqcallback)(int)) memset(&Z80, 0, sizeof(Z80)); Z80.daisy = config; Z80.irq_callback = irqcallback; +#ifdef Z80_ALLOW_OVERCLOCK + z80_overclock_ratio = 1; +#endif /* Clear registers values (NB: should be random on real hardware ?) */ AF = BC = DE = HL = SP = IX = IY =0; @@ -3449,7 +3462,7 @@ void z80_set_nmi_line(unsigned int state) PCD = 0x0066; WZ=PCD; - Z80.cycles += 11*15; + USE_CYCLES(11*15); } Z80.nmi_state = state; diff --git a/core/z80/z80.h b/core/z80/z80.h index d505775..8ee5f6b 100644 --- a/core/z80/z80.h +++ b/core/z80/z80.h @@ -51,6 +51,10 @@ typedef struct extern Z80_Regs Z80; +#ifdef Z80_ALLOW_OVERCLOCK +extern UINT8 z80_overclock_ratio; +#endif + extern unsigned char *z80_readmap[64]; extern unsigned char *z80_writemap[64]; diff --git a/libretro/libretro.c b/libretro/libretro.c index b6e3ccf..0921d4d 100644 --- a/libretro/libretro.c +++ b/libretro/libretro.c @@ -54,6 +54,12 @@ #include #endif +#if defined(M68K_ALLOW_OVERCLOCK) || defined(Z80_ALLOW_OVERCLOCK) +#define HAVE_OVERCLOCK +/* Overclocking frame delay (hack) */ +#define OVERCLOCK_FRAME_DELAY 100 +#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) @@ -150,6 +156,14 @@ 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 + #define SOUND_FREQUENCY 44100 /* Hide the EQ settings for now */ @@ -526,6 +540,9 @@ static void config_default(void) config.bios = 0; config.lock_on = 0; config.lcd = 0; /* 0.8 fixed point */ +#ifdef HAVE_OVERCLOCK + config.overclock = 0; +#endif /* video options */ config.overscan = 0; @@ -1236,8 +1253,22 @@ static void check_variables(void) config.invert_mouse = 1; } +#ifdef HAVE_OVERCLOCK + var.key = "genesis_plus_gx_overclock"; + environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); + { + if (strcmp(var.value, "1x") == 0) + config.overclock = 0; + else if (strcmp(var.value, "2x") == 0) + config.overclock = 1; + } +#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(); @@ -1677,6 +1708,9 @@ void retro_set_environment(retro_environment_t cb) { "genesis_plus_gx_render", "Interlaced mode 2 output; single field|double field" }, { "genesis_plus_gx_gun_cursor", "Show Lightgun crosshair; disabled|enabled" }, { "genesis_plus_gx_invert_mouse", "Invert Mouse Y-axis; disabled|enabled" }, +#ifdef HAVE_OVERCLOCK + { "genesis_plus_gx_overclock", "Overclock CPU; 1x|2x" }, +#endif { NULL, NULL }, }; @@ -2020,6 +2054,10 @@ bool retro_unserialize(const void *data, size_t size) if (!state_load((uint8_t*)data)) return FALSE; +#ifdef HAVE_OVERCLOCK + overclock_delay = OVERCLOCK_FRAME_DELAY; +#endif + return TRUE; } @@ -2184,6 +2222,9 @@ bool retro_load_game(const struct retro_game_info *info) } } +#ifdef HAVE_OVERCLOCK + overclock_delay = OVERCLOCK_FRAME_DELAY; +#endif audio_init(SOUND_FREQUENCY, 0); system_init(); system_reset(); @@ -2299,6 +2340,9 @@ void retro_deinit(void) void retro_reset(void) { +#ifdef HAVE_OVERCLOCK + overclock_delay = OVERCLOCK_FRAME_DELAY; +#endif gen_reset(0); } @@ -2307,12 +2351,42 @@ void retro_run(void) bool updated = false; is_running = true; +#ifdef HAVE_OVERCLOCK + /* update overclock delay */ + if (overclock_delay) + overclock_delay--; +#endif + if (system_hw == SYSTEM_MCD) + { +#ifdef M68K_ALLOW_OVERCLOCK + if (config.overclock && overclock_delay == 0) + m68k.overclock_ratio = 2; + else + m68k.overclock_ratio = 1; +#endif system_frame_scd(0); + } else if ((system_hw & SYSTEM_PBC) == SYSTEM_MD) + { +#ifdef M68K_ALLOW_OVERCLOCK + if (config.overclock && overclock_delay == 0) + m68k.overclock_ratio = 2; + else + m68k.overclock_ratio = 1; +#endif system_frame_gen(0); + } else + { +#ifdef Z80_ALLOW_OVERCLOCK + if (config.overclock && overclock_delay == 0) + z80_overclock_ratio = 2; + else + z80_overclock_ratio = 1; +#endif system_frame_sms(0); + } if (bitmap.viewport.changed & 9) { diff --git a/libretro/osd.h b/libretro/osd.h index aaa8890..089bb90 100644 --- a/libretro/osd.h +++ b/libretro/osd.h @@ -120,6 +120,7 @@ struct t_input_config input[MAX_INPUTS]; uint8 invert_mouse; uint8 gun_cursor; + uint8 overclock; } config; extern char GG_ROM[256];