From b3a7f8f1fceddcd45ec62bcbf75ba128e4f84f5a Mon Sep 17 00:00:00 2001 From: Nebuleon Fumika Date: Sun, 3 Feb 2013 19:26:34 -0500 Subject: 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. --- source/cpuexec.cpp | 35 +++++------ source/display.h | 3 + source/gfx.cpp | 16 +++++ source/nds/entry.cpp | 162 +++++++++++++++++++++++++++++++++------------------ source/nds/gui.h | 4 -- source/ppu.cpp | 33 ----------- source/ppu.h | 3 - source/soundux.cpp | 9 +++ 8 files changed, 149 insertions(+), 116 deletions(-) (limited to 'source') 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) -- cgit v1.2.3