mirror of
https://github.com/ShadauxCat/CATSFC.git
synced 2024-05-31 18:47:23 -04:00
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.
This commit is contained in:
parent
1ddc8b53fe
commit
b3a7f8f1fc
2
Makefile
2
Makefile
|
@ -71,7 +71,7 @@ DEFS := -DSPC700_C -DEXECUTE_SUPERFX_PER_LINE -DSDD1_DECOMP \
|
|||
-DNO_INLINE_SET_GET -DNOASM -DHAVE_MKSTEMP '-DACCEPT_SIZE_T=size_t' \
|
||||
-DUNZIP_SUPPORT -DFOREVER_16_BIT_SOUND -DFOREVER_STEREO \
|
||||
-DFOREVER_FORWARD_STEREO -DNO_VOLATILE_SOUND \
|
||||
-DSYNC_JOYPAD_AT_HBLANK -DDS2_DMA
|
||||
-DACCUMULATE_JOYPAD -DDS2_DMA
|
||||
|
||||
.PHONY: clean makedirs
|
||||
.SUFFIXES: .elf .dat .plg
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
LowFrequencyCPU();
|
||||
ds2_setSupend();
|
||||
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();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if(inputdata.key & KEY_TOUCH ||
|
||||
(HotkeyReturnToMenu && ((inputdata.key & HotkeyReturnToMenu) == HotkeyReturnToMenu))
|
||||
) //Active menu
|
||||
Settings.Paused = 1;
|
||||
|
||||
temporary_fast_forward =
|
||||
(HotkeyTemporaryFastForward && ((inputdata.key & HotkeyTemporaryFastForward) == HotkeyTemporaryFastForward))
|
||||
;
|
||||
|
||||
bool8 SoundToggleIsHeld =
|
||||
(HotkeyToggleSound && ((inputdata.key & HotkeyToggleSound) == HotkeyToggleSound))
|
||||
;
|
||||
|
||||
if (SoundToggleIsHeld && !SoundToggleWasHeld)
|
||||
{
|
||||
game_enable_audio = !game_enable_audio;
|
||||
game_disableAudio();
|
||||
}
|
||||
|
||||
SoundToggleWasHeld = SoundToggleIsHeld;
|
||||
ControlsPressed |= inputdata.key & ~PreviousControls;
|
||||
ControlsReleased |= PreviousControls & ~inputdata.key;
|
||||
}
|
||||
#endif // ACCUMULATE_JOYPAD
|
||||
|
||||
uint32 S9xReadJoypad (int which1)
|
||||
{
|
||||
if(which1 < 1)
|
||||
{
|
||||
unsigned int key;
|
||||
uint32 Controls;
|
||||
#ifdef ACCUMULATE_JOYPAD
|
||||
Controls = (PreviousControls | ControlsPressed) & ~ControlsReleased;
|
||||
PreviousControls = Controls;
|
||||
ControlsPressed = ControlsReleased = 0x00000000;
|
||||
#else
|
||||
{
|
||||
struct key_buf inputdata;
|
||||
ds2_getrawInput(&inputdata);
|
||||
|
||||
Controls = inputdata.key;
|
||||
}
|
||||
#endif
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if(Controls & KEY_TOUCH ||
|
||||
(HotkeyReturnToMenu && ((Controls & HotkeyReturnToMenu) == HotkeyReturnToMenu))
|
||||
) //Active menu
|
||||
Settings.Paused = 1;
|
||||
|
||||
temporary_fast_forward =
|
||||
(HotkeyTemporaryFastForward && ((Controls & HotkeyTemporaryFastForward) == HotkeyTemporaryFastForward))
|
||||
;
|
||||
|
||||
bool8 SoundToggleIsHeld =
|
||||
(HotkeyToggleSound && ((Controls & HotkeyToggleSound) == HotkeyToggleSound))
|
||||
;
|
||||
|
||||
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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue