aboutsummaryrefslogtreecommitdiff
path: root/source/nds/entry.cpp
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/nds/entry.cpp
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/nds/entry.cpp')
-rw-r--r--source/nds/entry.cpp244
1 files changed, 145 insertions, 99 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)