aboutsummaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorNebuleon Fumika2013-02-03 19:26:34 -0500
committerNebuleon Fumika2013-02-03 19:26:34 -0500
commitb3a7f8f1fceddcd45ec62bcbf75ba128e4f84f5a (patch)
treeb1bb5a056cf23ca56a480f4c2bfe92f6f0daafc0 /source
parent1ddc8b53fe92a88578f23ace849168bc19ca0cd0 (diff)
downloadsnesemu-b3a7f8f1fceddcd45ec62bcbf75ba128e4f84f5a.tar.gz
snesemu-b3a7f8f1fceddcd45ec62bcbf75ba128e4f84f5a.tar.bz2
snesemu-b3a7f8f1fceddcd45ec62bcbf75ba128e4f84f5a.zip
Synchronise the controller status more spread out inside a rendered frame:
* before rendering a background; * before rendering sprites; * while rendering more than 128 samples of audio at once ("Prefer fluid video"); * after every 16 scanlines of CPU execution instead of every 1; * while waiting for an audio buffer to become available; * while killing time between frames with fast-forward disabled. Controller presses and releases are now combined in a DS button bitfield using a shorter 32-bit algorithm. See entry.cpp:NDSSFCAccumulateJoypad and #define ACCUMULATE_JOYPAD in the source. This is still not suitable for playing platformers frame-perfectly, but it's much better than half a second of latency to press or release a button, and one still needs to press buttons a bit more than just light taps. I'd say 50 milliseconds is the latency now. Platformers requiring more precision can be played with frameskip 0. DMA does not require double-buffered displaying, so synchronise the controller more often by disabling double-buffered displaying again.
Diffstat (limited to 'source')
-rw-r--r--source/cpuexec.cpp35
-rw-r--r--source/display.h3
-rw-r--r--source/gfx.cpp16
-rw-r--r--source/nds/entry.cpp162
-rw-r--r--source/nds/gui.h4
-rw-r--r--source/ppu.cpp33
-rw-r--r--source/ppu.h3
-rw-r--r--source/soundux.cpp9
8 files changed, 149 insertions, 116 deletions
diff --git a/source/cpuexec.cpp b/source/cpuexec.cpp
index a0c7ebc..30dff21 100644
--- a/source/cpuexec.cpp
+++ b/source/cpuexec.cpp
@@ -105,7 +105,7 @@
#include "sa1.h"
#include "spc7110.h"
-#ifdef SYNC_JOYPAD_AT_HBLANK
+#ifdef ACCUMULATE_JOYPAD
#include "display.h"
#endif
@@ -473,15 +473,14 @@ void S9xDoHBlankProcessing_SFX ()
switch (CPU.WhichEvent)
{
case HBLANK_START_EVENT:
-#ifdef SYNC_JOYPAD_AT_HBLANK
- // Re-get the controls every hblank. A resolution algorithm in
- // ppu.cpp will determine with greater accuracy whether a key was
- // pressed or released during the frame.
- uint32 i;
- for (i = 0; i < 5; i++)
- {
- IPPU.JoypadsAtHBlanks [i][CPU.V_Counter] = S9xReadJoypad (i);
- }
+#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.
+ */
+ if ((CPU.V_Counter & 0xF) == 0)
+ NDSSFCAccumulateJoypad ();
#endif
if (IPPU.HDMA && CPU.V_Counter <= PPU.ScreenHeight)
IPPU.HDMA = S9xDoHDMA (IPPU.HDMA);
@@ -663,15 +662,13 @@ void S9xDoHBlankProcessing_NoSFX ()
switch (CPU.WhichEvent)
{
case HBLANK_START_EVENT:
-#ifdef SYNC_JOYPAD_AT_HBLANK
- // Re-get the controls every hblank. A resolution algorithm in
- // ppu.cpp will determine with greater accuracy whether a key was
- // pressed or released during the frame.
- uint32 i;
- for (i = 0; i < 5; i++)
- {
- IPPU.JoypadsAtHBlanks [i][CPU.V_Counter] = S9xReadJoypad (i);
- }
+#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 ();
#endif
if (IPPU.HDMA && CPU.V_Counter <= PPU.ScreenHeight)
IPPU.HDMA = S9xDoHDMA (IPPU.HDMA);
diff --git a/source/display.h b/source/display.h
index 8e65885..d00c5ed 100644
--- a/source/display.h
+++ b/source/display.h
@@ -97,6 +97,9 @@ void S9xGraphicsMode ();
char *S9xParseArgs (char **argv, int argc);
void S9xParseArg (char **argv, int &index, int argc);
void S9xExtraUsage ();
+#ifdef ACCUMULATE_JOYPAD
+void NDSSFCAccumulateJoypad ();
+#endif
uint32 S9xReadJoypad (int which1_0_to_4);
bool8 S9xReadMousePosition (int which1_0_to_1, int &x, int &y, uint32 &buttons);
bool8 S9xReadSuperScopePosition (int &x, int &y, uint32 &buttons);
diff --git a/source/gfx.cpp b/source/gfx.cpp
index 1b429c6..9ea0a97 100644
--- a/source/gfx.cpp
+++ b/source/gfx.cpp
@@ -1169,6 +1169,14 @@ void S9xSetupOBJ ()
static void DrawOBJS (bool8 OnMain = FALSE, uint8 D = 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 ();
+#endif
#ifdef MK_DEBUG_RTO
if(Settings.BGLayering) fprintf(stderr, "Entering DrawOBJS() for %d-%d\n", GFX.StartY, GFX.EndY);
#endif
@@ -2314,6 +2322,14 @@ static void DrawBackgroundMode5 (uint32 /* BGMODE */, uint32 bg, uint8 Z1, uint8
static void DrawBackground (uint32 BGMode, uint32 bg, uint8 Z1, uint8 Z2)
{
+#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 ();
+#endif
GFX.PixSize = 1;
BG.TileSize = BGSizes [PPU.BG[bg].BGSize];
diff --git a/source/nds/entry.cpp b/source/nds/entry.cpp
index 727986e..14c6243 100644
--- a/source/nds/entry.cpp
+++ b/source/nds/entry.cpp
@@ -734,6 +734,14 @@ void S9xSyncSpeed ()
{
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 ();
+#endif
syncdif = sync_next - getSysTime();
} while (syncdif > 0);
}
@@ -767,6 +775,14 @@ void S9xSyncSpeed ()
{
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 ();
+#endif
syncdif = sync_next - getSysTime();
} while (syncdif > 0);
// After that little delay, what time is it?
@@ -968,17 +984,17 @@ void S9xProcessSound (unsigned int)
if (!game_enable_audio)
return;
- if(ds2_checkAudiobuff() > AUDIO_BUFFER_COUNT * 3/4)
- {
- LastSoundEmissionTime++;
- return;
- }
-
- unsigned short *audiobuff;
-
unsigned int Now = getSysTime();
if (Now - LastSoundEmissionTime >= SOUND_EMISSION_INTERVAL)
{
+ if(ds2_checkAudiobuff() > AUDIO_BUFFER_COUNT * 3/4)
+ {
+ LastSoundEmissionTime++;
+ return;
+ }
+
+ unsigned short *audiobuff;
+
if (Now - LastSoundEmissionTime >= 11719 /* 500 milliseconds */)
{
LastSoundEmissionTime = Now;
@@ -1003,9 +1019,14 @@ void S9xProcessSound (unsigned int)
}
#endif
- do {
+ audiobuff = (unsigned short*)ds2_getAudiobuff();
+ while (audiobuff == NULL) //There are audio queue in sending or wait to send
+ {
+#ifdef ACCUMULATE_JOYPAD
+ NDSSFCAccumulateJoypad ();
+#endif
audiobuff = (unsigned short*)ds2_getAudiobuff();
- } while (audiobuff == NULL); //There are audio queue in sending or wait to send
+ }
/* If we need more audio samples */
if (so.samples_mixed_so_far < sample_count)
@@ -1141,72 +1162,99 @@ const unsigned int keymap[12] = {
static bool8 SoundToggleWasHeld = FALSE;
-unsigned int S9xReadJoypad (int which1)
-{
- struct key_buf inputdata;
+#ifdef ACCUMULATE_JOYPAD
+// These are kept as DS key bitfields until it's time to send them to Snes9x.
+static uint32 PreviousControls = 0x00000000;
+static uint32 ControlsPressed = 0x00000000;
+static uint32 ControlsReleased = 0x00000000;
+void NDSSFCAccumulateJoypad ()
+{
+ struct key_buf inputdata;
ds2_getrawInput(&inputdata);
- if (inputdata.key & KEY_LID)
+ ControlsPressed |= inputdata.key & ~PreviousControls;
+ ControlsReleased |= PreviousControls & ~inputdata.key;
+}
+#endif // ACCUMULATE_JOYPAD
+
+uint32 S9xReadJoypad (int which1)
+{
+ if(which1 < 1)
{
- LowFrequencyCPU();
- ds2_setSupend();
- do {
+ uint32 Controls;
+#ifdef ACCUMULATE_JOYPAD
+ Controls = (PreviousControls | ControlsPressed) & ~ControlsReleased;
+ PreviousControls = Controls;
+ ControlsPressed = ControlsReleased = 0x00000000;
+#else
+ {
+ struct key_buf inputdata;
ds2_getrawInput(&inputdata);
- mdelay(1);
- } while (inputdata.key & KEY_LID);
- ds2_wakeup();
- // Before starting to emulate again, turn off the lower
- // screen's backlight.
- mdelay(100); // needed to avoid ds2_setBacklight crashing
- ds2_setBacklight(2);
- GameFrequencyCPU();
- }
- u32 HotkeyReturnToMenu = game_config.HotkeyReturnToMenu != 0 ? game_config.HotkeyReturnToMenu : emu_config.HotkeyReturnToMenu;
- u32 HotkeyTemporaryFastForward = game_config.HotkeyTemporaryFastForward != 0 ? game_config.HotkeyTemporaryFastForward : emu_config.HotkeyTemporaryFastForward;
- u32 HotkeyToggleSound = game_config.HotkeyToggleSound != 0 ? game_config.HotkeyToggleSound : emu_config.HotkeyToggleSound;
+ Controls = inputdata.key;
+ }
+#endif
- if(inputdata.key & KEY_TOUCH ||
- (HotkeyReturnToMenu && ((inputdata.key & HotkeyReturnToMenu) == HotkeyReturnToMenu))
- ) //Active menu
- Settings.Paused = 1;
+ if (Controls & KEY_LID)
+ {
+ LowFrequencyCPU();
+ ds2_setSupend();
+ struct key_buf inputdata;
+ do {
+ ds2_getrawInput(&inputdata);
+ mdelay(1);
+ } while (inputdata.key & KEY_LID);
+ ds2_wakeup();
+ // Before starting to emulate again, turn off the lower
+ // screen's backlight.
+ mdelay(100); // needed to avoid ds2_setBacklight crashing
+ ds2_setBacklight(2);
+ GameFrequencyCPU();
+ }
- temporary_fast_forward =
- (HotkeyTemporaryFastForward && ((inputdata.key & HotkeyTemporaryFastForward) == HotkeyTemporaryFastForward))
- ;
+ u32 HotkeyReturnToMenu = game_config.HotkeyReturnToMenu != 0 ? game_config.HotkeyReturnToMenu : emu_config.HotkeyReturnToMenu;
+ u32 HotkeyTemporaryFastForward = game_config.HotkeyTemporaryFastForward != 0 ? game_config.HotkeyTemporaryFastForward : emu_config.HotkeyTemporaryFastForward;
+ u32 HotkeyToggleSound = game_config.HotkeyToggleSound != 0 ? game_config.HotkeyToggleSound : emu_config.HotkeyToggleSound;
- bool8 SoundToggleIsHeld =
- (HotkeyToggleSound && ((inputdata.key & HotkeyToggleSound) == HotkeyToggleSound))
- ;
+ if(Controls & KEY_TOUCH ||
+ (HotkeyReturnToMenu && ((Controls & HotkeyReturnToMenu) == HotkeyReturnToMenu))
+ ) //Active menu
+ Settings.Paused = 1;
- if (SoundToggleIsHeld && !SoundToggleWasHeld)
- {
- game_enable_audio = !game_enable_audio;
- game_disableAudio();
- }
+ temporary_fast_forward =
+ (HotkeyTemporaryFastForward && ((Controls & HotkeyTemporaryFastForward) == HotkeyTemporaryFastForward))
+ ;
- SoundToggleWasHeld = SoundToggleIsHeld;
+ bool8 SoundToggleIsHeld =
+ (HotkeyToggleSound && ((Controls & HotkeyToggleSound) == HotkeyToggleSound))
+ ;
- if(which1 < 1)
- {
- unsigned int key;
+ if (SoundToggleIsHeld && !SoundToggleWasHeld)
+ {
+ game_enable_audio = !game_enable_audio;
+ game_disableAudio();
+ }
+
+ SoundToggleWasHeld = SoundToggleIsHeld;
+
+ uint32 key = 0x80000000; // Required by Snes9x
// DS -> SNES
- key = (inputdata.key & KEY_A ) << 7; // 0x0001 -> 0x0080
- key |= (inputdata.key & KEY_B ) << 14; // 0x0002 -> 0x8000
- key |= (inputdata.key & KEY_SELECT) << 11; // 0x0004 -> 0x2000
- key |= (inputdata.key & KEY_START ) << 9; // 0x0008 -> 0x1000
- key |= (inputdata.key & KEY_UP ) << 5; // 0x0040 -> 0x0800
+ key |= (Controls & KEY_A ) << 7; // 0x0001 -> 0x0080
+ key |= (Controls & KEY_B ) << 14; // 0x0002 -> 0x8000
+ key |= (Controls & KEY_SELECT) << 11; // 0x0004 -> 0x2000
+ key |= (Controls & KEY_START ) << 9; // 0x0008 -> 0x1000
+ key |= (Controls & KEY_UP ) << 5; // 0x0040 -> 0x0800
// 0x0010 -> 0x0100; 0x0020 -> 0x0200
// 0x0030 -> 0x0300
- key |= (inputdata.key & (KEY_RIGHT | KEY_LEFT)) << 4;
+ key |= (Controls & (KEY_RIGHT | KEY_LEFT)) << 4;
// 0x0100 -> 0x0010; 0x0200 -> 0x0020; 0x0400 -> 0x0040
// 0x0700 -> 0x0070
- key |= (inputdata.key & (KEY_R | KEY_L | KEY_X)) >> 4;
+ key |= (Controls & (KEY_R | KEY_L | KEY_X)) >> 4;
// 0x0080 -> 0x0400; 0x0800 -> 0x4000
// 0x0880 -> 0x4400
- key |= (inputdata.key & (KEY_DOWN | KEY_Y)) << 3;
+ key |= (Controls & (KEY_DOWN | KEY_Y)) << 3;
/*
for(i= 0; i < 12; i++) //remap key
{
@@ -1214,7 +1262,7 @@ unsigned int S9xReadJoypad (int which1)
}
*/
- return (key | 0x80000000);
+ return key;
}
else
return 0;
diff --git a/source/nds/gui.h b/source/nds/gui.h
index 9df2ad2..b2eae62 100644
--- a/source/nds/gui.h
+++ b/source/nds/gui.h
@@ -24,11 +24,7 @@
#include "fs_api.h"
#include "gcheat.h"
-#ifdef DS2_DMA
-#define UP_SCREEN_UPDATE_METHOD 1
-#else
#define UP_SCREEN_UPDATE_METHOD 0
-#endif
#define DOWN_SCREEN_UPDATE_METHOD 2
#define MAX_GAMEPAD_MAP 16
diff --git a/source/ppu.cpp b/source/ppu.cpp
index ad937c4..179838d 100644
--- a/source/ppu.cpp
+++ b/source/ppu.cpp
@@ -2853,43 +2853,10 @@ void S9xUpdateJoypads ()
{
uint32 i;
-#ifdef SYNC_JOYPAD_AT_HBLANK
- uint32 j, k, KeyValue;
- bool8 StartedPressed;
-
- // For each joypad
- for (i = 0; i < 5; i++)
- {
- IPPU.Joypads [i] = 0;
- // Sync each key
- for (k = 1; k != 0; k <<= 1)
- {
- KeyValue = IPPU.JoypadsAtHBlanks[i][0] & k;
- StartedPressed = KeyValue != 0;
- // from each line.
- // If, initially, the key is NOT pressed, one line of it being
- // pressed means that the key MUST be pressed.
- // Otherwise, the key MUST be depressed if it starts pressed.
- for (j = 1; j < (Settings.PAL ? SNES_MAX_PAL_VCOUNTER : SNES_MAX_NTSC_VCOUNTER); j++)
- {
- if ((StartedPressed) && ((IPPU.JoypadsAtHBlanks[i][j] & k) == 0)) {
- KeyValue = 0;
- break;
- }
- else if ((!StartedPressed) && ((IPPU.JoypadsAtHBlanks[i][j] & k) != 0)) {
- KeyValue = k;
- break;
- }
- }
- IPPU.Joypads [i] |= KeyValue;
- }
- }
-#else
for (i = 0; i < 5; i++)
{
IPPU.Joypads [i] = S9xReadJoypad (i);
}
-#endif
// S9xMovieUpdate();
diff --git a/source/ppu.h b/source/ppu.h
index 1d3c7b7..8f8b8e7 100644
--- a/source/ppu.h
+++ b/source/ppu.h
@@ -152,9 +152,6 @@ struct InternalPPU {
int CurrentLine;
int Controller;
uint32 Joypads[5];
-#ifdef SYNC_JOYPAD_AT_HBLANK
- uint32 JoypadsAtHBlanks[5][SNES_MAX_PAL_VCOUNTER];
-#endif
uint32 SuperScope;
uint32 Mouse[2];
int PrevMouseX[2];
diff --git a/source/soundux.cpp b/source/soundux.cpp
index dde8a14..c20f366 100644
--- a/source/soundux.cpp
+++ b/source/soundux.cpp
@@ -1233,6 +1233,15 @@ static inline void MixMono (int sample_count)
for (uint32 I = 0; I < (uint32) sample_count; I++)
{
+#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.
+ */
+ if ((I & 0x7F) == 0x7F)
+ NDSSFCAccumulateJoypad ();
+#endif
unsigned long freq = freq0;
if (mod)