aboutsummaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorNebuleon Fumika2013-02-06 00:34:01 -0500
committerNebuleon Fumika2013-02-06 00:34:01 -0500
commit9e87a7a2b2659785bc05266fbb3292b60aecdf27 (patch)
tree11236b0ac0ac764bea2ff973499ff2ef11448158 /source
parentf2adea7bb2250876df3d1b6e076a6b5380c6b13e (diff)
downloadsnes9x2005-9e87a7a2b2659785bc05266fbb3292b60aecdf27.tar.gz
snes9x2005-9e87a7a2b2659785bc05266fbb3292b60aecdf27.tar.bz2
snes9x2005-9e87a7a2b2659785bc05266fbb3292b60aecdf27.zip
Implement automatic CPU frequency switching, which improves battery life if playing games that don't use all of the MIPS CPU. If all of it is indeed needed, then the game will constantly play at 396 MHz.
Diffstat (limited to 'source')
-rw-r--r--source/nds/entry.cpp244
-rw-r--r--source/nds/entry.h2
-rw-r--r--source/nds/gui.c22
-rw-r--r--source/nds/gui.h23
-rw-r--r--source/nds/message.h7
5 files changed, 185 insertions, 113 deletions
diff --git a/source/nds/entry.cpp b/source/nds/entry.cpp
index 6cbf3ef..08e95cb 100644
--- a/source/nds/entry.cpp
+++ b/source/nds/entry.cpp
@@ -642,10 +642,19 @@ static unsigned int sync_next = 0;
static unsigned int skip_rate= 0;
+#define CPU_DOWNCLOCK_EARLY_TIME 293 /* 1 = 42.667 us. 391 = 16.67 ms */
+#define CPU_DOWNCLOCK_DETERMINATION_INTERVAL 46874 /* 23437 = 1 s */
+
+static unsigned int LastAutoCPUTime = 0;
+unsigned int AutoCPUFrequency = 5;
+static bool8 ConsistentlyEarly = FALSE;
+static bool8 FastForwardLastFrame = FALSE;
+
void S9xSyncSpeed ()
{
uint32 syncnow;
int32 syncdif;
+ unsigned int LastAutoCPUFrequency = AutoCPUFrequency;
#if 0
if (Settings.SoundSync == 2)
@@ -657,8 +666,19 @@ void S9xSyncSpeed ()
#endif
syncnow = getSysTime();
- if (game_fast_forward || temporary_fast_forward /* hotkey is held */)
+ bool8 FastForward = game_fast_forward || temporary_fast_forward /* hotkey is held */;
+
+ if (game_config.clock_speed_number == 0)
+ if (FastForward && !FastForwardLastFrame)
+ HighFrequencyCPU ();
+ else if (!FastForward && FastForwardLastFrame)
+ GameFrequencyCPU ();
+
+ FastForwardLastFrame = FastForward;
+
+ if (FastForward)
{
+ ConsistentlyEarly = FALSE; // don't use fast-forward to lower CPU
sync_last = syncnow;
sync_next = syncnow;
@@ -670,137 +690,149 @@ void S9xSyncSpeed ()
IPPU.RenderThisFrame = true;
}
}
- else if (Settings.SkipFrames == AUTO_FRAMERATE /* && !game_fast_forward && !temporary_fast_forward */)
+ else
{
- // frame_time is in getSysTime units: 42.667 microseconds.
- int32 frame_time = Settings.PAL ? 468 /* = 20.0 ms */ : 391 /* = 16.67 ms */;
- if (sync_last > syncnow) // Overflow occurred! (every 50 hrs)
+ // Manual or automatic frame skipping, no fast-forward.
+ if (Settings.SkipFrames == AUTO_FRAMERATE)
{
- // Render this frame regardless, set the
- // sync_next, and get the hell out.
- IPPU.RenderThisFrame = TRUE;
+ // frame_time is in getSysTime units: 42.667 microseconds.
+ int32 frame_time = Settings.PAL ? 468 /* = 20.0 ms */ : 391 /* = 16.67 ms */;
+ if (sync_last > syncnow) // Overflow occurred! (every 50 hrs)
+ {
+ // Render this frame regardless, set the
+ // sync_next, and get the hell out.
+ ConsistentlyEarly = FALSE;
+ AutoCPUFrequency = 5;
+ LastAutoCPUTime = syncnow;
+
+ IPPU.RenderThisFrame = TRUE;
+ sync_last = syncnow;
+ sync_next = syncnow + frame_time;
+ goto finalise;
+ }
sync_last = syncnow;
- sync_next = syncnow + frame_time;
- return;
- }
- sync_last = syncnow;
- // If this is positive, we have syncdif*42.66 microseconds to
- // spare.
- // If this is negative, we're late by syncdif*42.66
- // microseconds.
- syncdif = sync_next - syncnow;
- if(skip_rate < 2 /* did not skip 2 frames yet */)
- {
- // Skip a minimum of 2 frames between rendered frames.
- // This prevents the DSTwo-DS link from being too busy
- // to return button statuses.
- ++skip_rate;
- IPPU.RenderThisFrame = FALSE;
- sync_next += frame_time;
- }
- else if (syncdif < 0 && syncdif >= -(frame_time / 2))
- {
- // We're late, but by less than half a frame. Draw it
- // anyway. If the next frame is too late, it'll be
- // skipped.
- skip_rate = 0;
- IPPU.RenderThisFrame = true;
- sync_next += frame_time;
- }
- else if(syncdif < 0)
- {
- /*
- * If we're consistently late, delay up to 8 frames.
- *
- * That really helps with certain games, such as
- * Super Mario RPG and Yoshi's Island.
- */
- if(++skip_rate < 10)
+ // If this is positive, we have syncdif*42.66 microseconds to
+ // spare.
+ // If this is negative, we're late by syncdif*42.66
+ // microseconds.
+ syncdif = sync_next - syncnow;
+ if(skip_rate < 2 /* did not skip 2 frames yet */)
{
- if(syncdif >= -11719 /* not more than 500.0 ms late */)
+ // Skip a minimum of 2 frames between rendered frames.
+ // This prevents the DSTwo-DS link from being too busy
+ // to return button statuses.
+ ++skip_rate;
+ IPPU.RenderThisFrame = FALSE;
+ sync_next += frame_time;
+ }
+ else if(syncdif < 0)
+ {
+ ConsistentlyEarly = FALSE;
+ AutoCPUFrequency = 5;
+ LastAutoCPUTime = syncnow;
+
+ /*
+ * If we're consistently late, delay up to 8 frames.
+ *
+ * That really helps with certain games, such as
+ * Super Mario RPG and Yoshi's Island.
+ */
+ if(++skip_rate < 10)
{
- IPPU.RenderThisFrame = FALSE;
- sync_next += frame_time;
+ if(syncdif >= -11719 /* not more than 500.0 ms late */)
+ {
+ IPPU.RenderThisFrame = FALSE;
+ sync_next += frame_time;
+ }
+ else
+ { //lag more than 0.5s, maybe paused
+ IPPU.RenderThisFrame = TRUE;
+ sync_next = syncnow + frame_time;
+ }
}
else
- { //lag more than 0.5s, maybe paused
+ {
+ skip_rate = 0;
IPPU.RenderThisFrame = TRUE;
sync_next = syncnow + frame_time;
}
}
- else
+ else // Early
{
+ ConsistentlyEarly = ConsistentlyEarly && syncdif >= CPU_DOWNCLOCK_EARLY_TIME;
skip_rate = 0;
- IPPU.RenderThisFrame = TRUE;
- sync_next = syncnow + frame_time;
- }
- }
- else // Early
- {
- skip_rate = 0;
- if (syncdif > 0)
- {
- do {
- S9xProcessSound (0);
+ if (syncdif > 0)
+ {
+ do {
+ S9xProcessSound (0);
#ifdef ACCUMULATE_JOYPAD
/*
* This call allows NDSSFC to synchronise the DS controller more often.
* If porting a later version of Snes9x into NDSSFC, it is essential to
* preserve it.
*/
- NDSSFCAccumulateJoypad ();
+ NDSSFCAccumulateJoypad ();
#endif
- syncdif = sync_next - getSysTime();
- } while (syncdif > 0);
- }
+ syncdif = sync_next - getSysTime();
+ } while (syncdif > 0);
+ }
- IPPU.RenderThisFrame = TRUE;
- sync_next += frame_time;
- }
+ IPPU.RenderThisFrame = TRUE;
+ sync_next += frame_time;
+ }
#if 0
- if(++framenum >= 60)
- {
- syncdif = syncnow - sync_last;
- sync_last = syncnow;
- framenum = 0;
- //printf("T %d %d\n", syncdif*42667/1000, realframe);
- realframe = 0;
- }
+ if(++framenum >= 60)
+ {
+ syncdif = syncnow - sync_last;
+ sync_last = syncnow;
+ framenum = 0;
+ //printf("T %d %d\n", syncdif*42667/1000, realframe);
+ realframe = 0;
+ }
#endif
- }
- else /* if (Settings.SkipFrames != AUTO_FRAMERATE && !game_fast_forward && !temporary_fast_forward) */
- {
- // frame_time is in getSysTime units: 42.667 microseconds.
- uint32 frame_time = Settings.PAL ? 468 /* = 20.0 ms */ : 391 /* = 16.67 ms */;
- sync_last = syncnow;
- if (++skip_rate > Settings.SkipFrames)
+ }
+ else /* if (Settings.SkipFrames != AUTO_FRAMERATE) */
{
- skip_rate = 0;
- IPPU.RenderThisFrame = TRUE;
- // Are we early?
- syncdif = sync_next - syncnow;
- if (syncdif > 0)
+ // frame_time is in getSysTime units: 42.667 microseconds.
+ uint32 frame_time = Settings.PAL ? 468 /* = 20.0 ms */ : 391 /* = 16.67 ms */;
+ sync_last = syncnow;
+ if (++skip_rate > Settings.SkipFrames)
{
- do {
- S9xProcessSound (0);
+ skip_rate = 0;
+ IPPU.RenderThisFrame = TRUE;
+ // Are we early?
+ syncdif = sync_next - syncnow;
+ if (syncdif > 0)
+ {
+ ConsistentlyEarly = ConsistentlyEarly && syncdif >= CPU_DOWNCLOCK_EARLY_TIME;
+ do {
+ S9xProcessSound (0);
#ifdef ACCUMULATE_JOYPAD
/*
* This call allows NDSSFC to synchronise the DS controller more often.
* If porting a later version of Snes9x into NDSSFC, it is essential to
* preserve it.
*/
- NDSSFCAccumulateJoypad ();
+ NDSSFCAccumulateJoypad ();
#endif
- syncdif = sync_next - getSysTime();
- } while (syncdif > 0);
- // After that little delay, what time is it?
- syncnow = getSysTime();
+ syncdif = sync_next - getSysTime();
+ } while (syncdif > 0);
+ // After that little delay, what time is it?
+ syncnow = getSysTime();
+ }
+ else
+ {
+ // Nope, we're late.
+ ConsistentlyEarly = FALSE;
+ AutoCPUFrequency = 5;
+ LastAutoCPUTime = syncnow;
+ }
+ sync_next = syncnow + frame_time * (Settings.SkipFrames + 1);
+ }
+ else
+ {
+ IPPU.RenderThisFrame = FALSE;
}
- sync_next = syncnow + frame_time * (Settings.SkipFrames + 1);
- }
- else
- {
- IPPU.RenderThisFrame = FALSE;
}
}
@@ -887,6 +919,20 @@ void S9xSyncSpeed ()
next1.tv_usec %= 1000000;
}
#endif
+
+finalise: ;
+
+ if (syncnow - LastAutoCPUTime >= CPU_DOWNCLOCK_DETERMINATION_INTERVAL) {
+ if (ConsistentlyEarly && AutoCPUFrequency > 0)
+ AutoCPUFrequency--;
+
+ LastAutoCPUTime = syncnow;
+ ConsistentlyEarly = TRUE;
+ // will get unset if the CPU should stay the same at next check
+ }
+
+ if (game_config.clock_speed_number == 0 && LastAutoCPUFrequency != AutoCPUFrequency)
+ GameFrequencyCPU ();
}
bool8 S9xOpenSoundDevice (int mode, bool8 stereo, int buffer_size)
diff --git a/source/nds/entry.h b/source/nds/entry.h
index a6bd350..2d3f7db 100644
--- a/source/nds/entry.h
+++ b/source/nds/entry.h
@@ -8,6 +8,8 @@ extern "C" {
void game_restart(void);
int load_gamepak(char* file);
+
+ extern unsigned int AutoCPUFrequency;
#ifdef __cplusplus
}
#endif
diff --git a/source/nds/gui.c b/source/nds/gui.c
index 4b5955a..2dad9c8 100644
--- a/source/nds/gui.c
+++ b/source/nds/gui.c
@@ -289,7 +289,6 @@ u32 game_enable_audio = 1;
/******************************************************************************
******************************************************************************/
static u32 menu_cheat_page = 0;
-u32 clock_speed_number = 5;
u32 gamepad_config_menu;
/******************************************************************************
@@ -1665,8 +1664,10 @@ void GameFrequencyCPU()
{
u32 clock_speed_table[6] = {6, 9, 10, 11, 12, 13}; //240, 300, 336, 360, 384, 396
- if(clock_speed_number <= 5)
- ds2_setCPUclocklevel(clock_speed_table[clock_speed_number]);
+ if (game_config.clock_speed_number == 0)
+ ds2_setCPUclocklevel(clock_speed_table[AutoCPUFrequency]);
+ else if(game_config.clock_speed_number <= 6)
+ ds2_setCPUclocklevel(clock_speed_table[game_config.clock_speed_number - 1]);
}
void savefast_int(void)
@@ -1776,8 +1777,6 @@ u32 menu(u16 *screen, bool8 FirstInvocation)
HighFrequencyCPU(); // Crank it up, leave quickly
if(gamepak_name[0] != 0)
{
- game_config.clock_speed_number = clock_speed_number;
-
reorder_latest_file();
S9xAutoSaveSRAM ();
save_game_config_file();
@@ -2828,6 +2827,8 @@ u32 menu(u16 *screen, bool8 FirstInvocation)
char *frameskip_options[] = { (char*)&msg[MSG_VIDEO_FRAME_SKIPPING_AUTOMATIC], (char*)&msg[MSG_VIDEO_FRAME_SKIPPING_2], (char*)&msg[MSG_VIDEO_FRAME_SKIPPING_3], (char*)&msg[MSG_VIDEO_FRAME_SKIPPING_4], (char*)&msg[MSG_VIDEO_FRAME_SKIPPING_5], (char*)&msg[MSG_VIDEO_FRAME_SKIPPING_6], (char*)&msg[MSG_VIDEO_FRAME_SKIPPING_7], (char*)&msg[MSG_VIDEO_FRAME_SKIPPING_8], (char*)&msg[MSG_VIDEO_FRAME_SKIPPING_9], (char*)&msg[MSG_VIDEO_FRAME_SKIPPING_10] };
+ char *cpu_frequency_options[] = { (char*)&msg[MSG_OPTIONS_CPU_FREQUENCY_AUTOMATIC], (char*)&msg[MSG_OPTIONS_CPU_FREQUENCY_0], (char*)&msg[MSG_OPTIONS_CPU_FREQUENCY_1], (char*)&msg[MSG_OPTIONS_CPU_FREQUENCY_2], (char*)&msg[MSG_OPTIONS_CPU_FREQUENCY_3], (char*)&msg[MSG_OPTIONS_CPU_FREQUENCY_4], (char*)&msg[MSG_OPTIONS_CPU_FREQUENCY_5] };
+
char *fluidity_options[] = { (char*)&msg[MSG_VIDEO_AUDIO_FLUIDITY_PREFER_VIDEO], (char*)&msg[MSG_VIDEO_AUDIO_FLUIDITY_PREFER_AUDIO] };
char *on_off_options[] = { (char*)&msg[MSG_GENERAL_OFF], (char*)&msg[MSG_GENERAL_ON] };
@@ -3002,8 +3003,9 @@ u32 menu(u16 *screen, bool8 FirstInvocation)
{
/* 00 */ SUBMENU_OPTION(NULL, &msg[MSG_MAIN_MENU_OPTIONS], NULL, 0),
- //CPU speed
- /* 01 */ NUMERIC_SELECTION_OPTION(NULL, &msg[FMT_OPTIONS_CPU_FREQUENCY], &clock_speed_number, 6, NULL, 1),
+ //CPU speed (string: shows MHz)
+ /* 01 */ STRING_SELECTION_OPTION(NULL, NULL, &msg[FMT_OPTIONS_CPU_FREQUENCY], cpu_frequency_options,
+ &game_config.clock_speed_number, 7, NULL, PASSIVE_TYPE, 1),
/* 02 */ STRING_SELECTION_OPTION(language_set, NULL, &msg[FMT_OPTIONS_LANGUAGE], language_options,
&emu_config.language, sizeof(language_options) / sizeof(language_options[0]) /* number of possible languages */, NULL, ACTION_TYPE, 2),
@@ -4113,8 +4115,6 @@ u32 menu(u16 *screen, bool8 FirstInvocation)
if(gamepak_name[0] != 0)
{
- game_config.clock_speed_number = clock_speed_number;
-
reorder_latest_file();
S9xAutoSaveSRAM ();
save_game_config_file();
@@ -4284,8 +4284,7 @@ u32 load_font()
--------------------------------------------------------*/
void init_game_config(void)
{
- game_config.clock_speed_number = 5; // 396 MHz by default
- clock_speed_number = 5;
+ game_config.clock_speed_number = 0; // "Auto" by default
game_config.graphic = 3; // By default, have a good-looking aspect ratio
game_config.frameskip_value = 0; // Automatic frame skipping
game_config.SoundSync = 0; // Prefer fluid images by default
@@ -4341,7 +4340,6 @@ void load_game_config_file(void)
{
fread(&game_config, 1, sizeof(GAME_CONFIG), fp);
- clock_speed_number = game_config.clock_speed_number;
game_set_frameskip();
game_set_fluidity();
}
diff --git a/source/nds/gui.h b/source/nds/gui.h
index b390b69..df1bff9 100644
--- a/source/nds/gui.h
+++ b/source/nds/gui.h
@@ -48,7 +48,25 @@ struct _EMU_CONFIG
struct _GAME_CONFIG
{
- u32 clock_speed_number;
+ /*
+ * PreviouslyUsed_20130205_2 was formerly known as
+ * 'clock_speed_number'; its values were in [0, 5]. [0, 5] were mapped
+ * to 240, 300, 336, 360, 384 and 394 MHz respectively.
+ * Version 1.29 changes the value range for 'clock_speed_number' to
+ * [0, 6], with 0 as an automatic CPU speed setting.
+ * Change rationale: The default value becomes 0 instead of 5.
+ * If this variable were to be used as is, the meaning of the default
+ * value would change. Games which had a configuration file before
+ * 1.29 would be using the older default of 5 (394 MHz), the meaning
+ * of which would become 384 MHz instead of "staying the default".
+ * Games which did not have a configuration file before 1.29 would be
+ * using the correct default.
+ * This would confuse users or cause undue hassle.
+ * THIS VALUE IS NOT GUARANTEED TO BE RESERVED AND SET TO 0.
+ * DO NOT USE THIS VALUE FOR ANY PURPOSE OTHER THAN EXACTLY THE ONE
+ * FOR WHICH IT WAS INTENDED.
+ */
+ u32 PreviouslyUsed_20130205_2;
u32 Reserved0;
/*
* PreviouslyUsed_20130205_1 was formerly known as 'frameskip_value';
@@ -78,7 +96,8 @@ struct _GAME_CONFIG
u32 HotkeyToggleSound;
u32 SoundSync;
u32 frameskip_value;
- u32 Reserved2[43];
+ u32 clock_speed_number;
+ u32 Reserved2[42];
};
typedef enum
diff --git a/source/nds/message.h b/source/nds/message.h
index 86389b1..e58d7b8 100644
--- a/source/nds/message.h
+++ b/source/nds/message.h
@@ -48,6 +48,13 @@ enum MSG
MSG_TOOLS_GAME_HOTKEY_GENERAL,
FMT_OPTIONS_LANGUAGE,
FMT_OPTIONS_CPU_FREQUENCY,
+ MSG_OPTIONS_CPU_FREQUENCY_AUTOMATIC,
+ MSG_OPTIONS_CPU_FREQUENCY_0,
+ MSG_OPTIONS_CPU_FREQUENCY_1,
+ MSG_OPTIONS_CPU_FREQUENCY_2,
+ MSG_OPTIONS_CPU_FREQUENCY_3,
+ MSG_OPTIONS_CPU_FREQUENCY_4,
+ MSG_OPTIONS_CPU_FREQUENCY_5,
MSG_OPTIONS_CARD_CAPACITY /* unused if !defined(ENABLE_FREE_SPACE) */,
MSG_OPTIONS_RESET,
MSG_OPTIONS_VERSION,