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.
This commit is contained in:
Brian Koropoff 2017-10-01 23:05:07 -07:00
parent accc1c030e
commit 2135365d5c
9 changed files with 121 additions and 11 deletions

View File

@ -439,7 +439,9 @@ LIBRETRO_CFLAGS += $(INCFLAGS) $(INCFLAGS_PLATFORM)
LIBRETRO_CFLAGS += $(BPP_DEFINES) \ LIBRETRO_CFLAGS += $(BPP_DEFINES) \
$(ENDIANNESS_DEFINES) \ $(ENDIANNESS_DEFINES) \
$(PLATFORM_DEFINES) \ $(PLATFORM_DEFINES) \
-D__LIBRETRO__ -D__LIBRETRO__ \
-DM68K_ALLOW_OVERCLOCK \
-DZ80_ALLOW_OVERCLOCK
ifneq (,$(findstring msvc,$(platform))) ifneq (,$(findstring msvc,$(platform)))
LIBRETRO_CFLAGS += -DINLINE="static _inline" LIBRETRO_CFLAGS += -DINLINE="static _inline"

View File

@ -268,6 +268,10 @@ typedef struct
uint address_space; /* Current FC code */ uint address_space; /* Current FC code */
#ifdef M68K_ALLOW_OVERCLOCK
uint8 overclock_ratio;
#endif
/* Callbacks to host */ /* Callbacks to host */
int (*int_ack_callback)(int int_line); /* Interrupt Acknowledge */ int (*int_ack_callback)(int int_line); /* Interrupt Acknowledge */
void (*reset_instr_callback)(void); /* Called when a RESET instruction is encountered */ void (*reset_instr_callback)(void); /* Called when a RESET instruction is encountered */

View File

@ -319,6 +319,10 @@ void m68k_init(void)
} }
#endif #endif
#ifdef M68K_ALLOW_OVERCLOCK
m68k.overclock_ratio = 1;
#endif
#if M68K_EMULATE_INT_ACK == OPT_ON #if M68K_EMULATE_INT_ACK == OPT_ON
m68k_set_int_ack_callback(NULL); m68k_set_int_ack_callback(NULL);
#endif #endif

View File

@ -514,7 +514,11 @@
/* ---------------------------- Cycle Counting ---------------------------- */ /* ---------------------------- 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) #define USE_CYCLES(A) m68ki_cpu.cycles += (A)
#endif
#define SET_CYCLES(A) m68ki_cpu.cycles = (A) #define SET_CYCLES(A) m68ki_cpu.cycles = (A)

View File

@ -284,6 +284,10 @@ void s68k_init(void)
} }
#endif #endif
#ifdef M68K_ALLOW_OVERCLOCK
s68k.overclock_ratio = 1;
#endif
#if M68K_EMULATE_INT_ACK == OPT_ON #if M68K_EMULATE_INT_ACK == OPT_ON
s68k_set_int_ack_callback(NULL); s68k_set_int_ack_callback(NULL);
#endif #endif

View File

@ -201,8 +201,18 @@
#define IFF2 Z80.iff2 #define IFF2 Z80.iff2
#define HALT Z80.halt #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; Z80_Regs Z80;
#ifdef Z80_ALLOW_OVERCLOCK
UINT8 z80_overclock_ratio;
#endif
unsigned char *z80_readmap[64]; unsigned char *z80_readmap[64];
unsigned char *z80_writemap[64]; unsigned char *z80_writemap[64];
@ -473,7 +483,7 @@ INLINE void BURNODD(int cycles, int opcodes, int cyclesum)
if( cycles > 0 ) if( cycles > 0 )
{ {
R += (cycles / cyclesum) * opcodes; 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 * 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 * execute an opcode
@ -3228,7 +3238,7 @@ static void take_interrupt(void)
PUSH( pc ); PUSH( pc );
PCD = 0x0038; PCD = 0x0038;
/* RST $38 + 'interrupt latency' cycles */ /* 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 else
{ {
@ -3243,7 +3253,7 @@ static void take_interrupt(void)
RM16( irq_vector, &Z80.pc ); RM16( irq_vector, &Z80.pc );
LOG(("Z80 #%d IM2 [$%04x] = $%04x\n",cpu_getactivecpu() , irq_vector, PCD)); LOG(("Z80 #%d IM2 [$%04x] = $%04x\n",cpu_getactivecpu() , irq_vector, PCD));
/* CALL $xxxx + 'interrupt latency' cycles */ /* 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 else
{ {
@ -3257,18 +3267,18 @@ static void take_interrupt(void)
PUSH( pc ); PUSH( pc );
PCD = irq_vector & 0xffff; PCD = irq_vector & 0xffff;
/* CALL $xxxx + 'interrupt latency' cycles */ /* 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; break;
case 0xc30000: /* jump */ case 0xc30000: /* jump */
PCD = irq_vector & 0xffff; PCD = irq_vector & 0xffff;
/* JP $xxxx + 2 cycles */ /* 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; break;
default: /* rst (or other opcodes?) */ default: /* rst (or other opcodes?) */
PUSH( pc ); PUSH( pc );
PCD = irq_vector & 0x0038; PCD = irq_vector & 0x0038;
/* RST $xx + 2 cycles */ /* 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; break;
} }
} }
@ -3358,6 +3368,9 @@ void z80_init(const void *config, int (*irqcallback)(int))
memset(&Z80, 0, sizeof(Z80)); memset(&Z80, 0, sizeof(Z80));
Z80.daisy = config; Z80.daisy = config;
Z80.irq_callback = irqcallback; Z80.irq_callback = irqcallback;
#ifdef Z80_ALLOW_OVERCLOCK
z80_overclock_ratio = 1;
#endif
/* Clear registers values (NB: should be random on real hardware ?) */ /* Clear registers values (NB: should be random on real hardware ?) */
AF = BC = DE = HL = SP = IX = IY =0; AF = BC = DE = HL = SP = IX = IY =0;
@ -3449,7 +3462,7 @@ void z80_set_nmi_line(unsigned int state)
PCD = 0x0066; PCD = 0x0066;
WZ=PCD; WZ=PCD;
Z80.cycles += 11*15; USE_CYCLES(11*15);
} }
Z80.nmi_state = state; Z80.nmi_state = state;

View File

@ -51,6 +51,10 @@ typedef struct
extern Z80_Regs Z80; extern Z80_Regs Z80;
#ifdef Z80_ALLOW_OVERCLOCK
extern UINT8 z80_overclock_ratio;
#endif
extern unsigned char *z80_readmap[64]; extern unsigned char *z80_readmap[64];
extern unsigned char *z80_writemap[64]; extern unsigned char *z80_writemap[64];

View File

@ -54,6 +54,12 @@
#include <xtl.h> #include <xtl.h>
#endif #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_3B RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 0)
#define RETRO_DEVICE_MDPAD_6B RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 1) #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_MSPAD_2B RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 2)
@ -150,6 +156,14 @@ static char ggvalidchars[] = "ABCDEFGHJKLMNPRSTVWXYZ0123456789";
static char arvalidchars[] = "0123456789ABCDEF"; 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 #define SOUND_FREQUENCY 44100
/* Hide the EQ settings for now */ /* Hide the EQ settings for now */
@ -526,6 +540,9 @@ static void config_default(void)
config.bios = 0; config.bios = 0;
config.lock_on = 0; config.lock_on = 0;
config.lcd = 0; /* 0.8 fixed point */ config.lcd = 0; /* 0.8 fixed point */
#ifdef HAVE_OVERCLOCK
config.overclock = 0;
#endif
/* video options */ /* video options */
config.overscan = 0; config.overscan = 0;
@ -1236,8 +1253,22 @@ static void check_variables(void)
config.invert_mouse = 1; 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) if (reinit)
{ {
#ifdef HAVE_OVERCLOCK
overclock_delay = OVERCLOCK_FRAME_DELAY;
#endif
audio_init(SOUND_FREQUENCY, 0); audio_init(SOUND_FREQUENCY, 0);
memcpy(temp, sram.sram, sizeof(temp)); memcpy(temp, sram.sram, sizeof(temp));
system_init(); 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_render", "Interlaced mode 2 output; single field|double field" },
{ "genesis_plus_gx_gun_cursor", "Show Lightgun crosshair; disabled|enabled" }, { "genesis_plus_gx_gun_cursor", "Show Lightgun crosshair; disabled|enabled" },
{ "genesis_plus_gx_invert_mouse", "Invert Mouse Y-axis; 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 }, { NULL, NULL },
}; };
@ -2020,6 +2054,10 @@ bool retro_unserialize(const void *data, size_t size)
if (!state_load((uint8_t*)data)) if (!state_load((uint8_t*)data))
return FALSE; return FALSE;
#ifdef HAVE_OVERCLOCK
overclock_delay = OVERCLOCK_FRAME_DELAY;
#endif
return TRUE; 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); audio_init(SOUND_FREQUENCY, 0);
system_init(); system_init();
system_reset(); system_reset();
@ -2299,6 +2340,9 @@ void retro_deinit(void)
void retro_reset(void) void retro_reset(void)
{ {
#ifdef HAVE_OVERCLOCK
overclock_delay = OVERCLOCK_FRAME_DELAY;
#endif
gen_reset(0); gen_reset(0);
} }
@ -2307,12 +2351,42 @@ void retro_run(void)
bool updated = false; bool updated = false;
is_running = true; is_running = true;
#ifdef HAVE_OVERCLOCK
/* update overclock delay */
if (overclock_delay)
overclock_delay--;
#endif
if (system_hw == SYSTEM_MCD) if (system_hw == SYSTEM_MCD)
system_frame_scd(0); {
else if ((system_hw & SYSTEM_PBC) == SYSTEM_MD) #ifdef M68K_ALLOW_OVERCLOCK
system_frame_gen(0); if (config.overclock && overclock_delay == 0)
m68k.overclock_ratio = 2;
else 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); system_frame_sms(0);
}
if (bitmap.viewport.changed & 9) if (bitmap.viewport.changed & 9)
{ {

View File

@ -120,6 +120,7 @@ struct
t_input_config input[MAX_INPUTS]; t_input_config input[MAX_INPUTS];
uint8 invert_mouse; uint8 invert_mouse;
uint8 gun_cursor; uint8 gun_cursor;
uint8 overclock;
} config; } config;
extern char GG_ROM[256]; extern char GG_ROM[256];