From efc9e624b4a4a3d965c8fd484a0e0d8c2f17d528 Mon Sep 17 00:00:00 2001 From: Nebuleon Fumika Date: Sun, 6 Jan 2013 19:20:30 -0500 Subject: [PATCH 1/9] Speed hacks, SNESAdvance-style. Speed hacks for 2 games are hardcoded for testing. --- Makefile | 2 +- source/cpuops.cpp | 214 +++++++++++++++++++++++++++++++++++++++++++++- source/memmap.cpp | 14 +++ 3 files changed, 226 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 0642bf7e..d24c05d9 100644 --- a/Makefile +++ b/Makefile @@ -69,7 +69,7 @@ CFLAGS := -mips32 -mno-abicalls -fno-pic -fno-builtin \ DEFS := -DSPC700_C -DEXECUTE_SUPERFX_PER_LINE -DSDD1_DECOMP \ -DVAR_CYCLES -DCPU_SHUTDOWN -DSPC700_SHUTDOWN \ -DNO_INLINE_SET_GET -DNOASM -DHAVE_MKSTEMP '-DACCEPT_SIZE_T=size_t' \ - -DUNZIP_SUPPORT -DSYNC_JOYPAD_AT_HBLANK + -DUNZIP_SUPPORT -DSYNC_JOYPAD_AT_HBLANK -DSNESADVANCE_SPEEDHACKS .PHONY: clean makedirs .SUFFIXES: .elf .dat .plg diff --git a/source/cpuops.cpp b/source/cpuops.cpp index cb10b46b..1922799e 100644 --- a/source/cpuops.cpp +++ b/source/cpuops.cpp @@ -3415,6 +3415,35 @@ inline void CPUShutdown() #define CPUShutdown() #endif +#ifdef CPU_SHUTDOWN +#ifndef SA1_OPCODES +inline void ForceShutdown() +{ + CPU.WaitAddress = NULL; + if (Settings.SA1) + S9xSA1ExecuteDuringSleep (); + CPU.Cycles = CPU.NextEvent; + if (IAPU.APUExecuting) + { + ICPU.CPUExecuting = FALSE; + do + { + APU_EXECUTE1(); + } while (APU.Cycles < CPU.NextEvent); + ICPU.CPUExecuting = TRUE; + } +} +#else +inline void ForceShutdown() +{ + SA1.Executing = FALSE; + SA1.CPUExecuting = FALSE; +} +#endif +#else +#define ForceShutdown() +#endif + /* BCC */ static void Op90 (void) { @@ -5048,22 +5077,201 @@ static void OpCB (void) #ifndef SA1_OPCODES CPU.Cycles += TWO_CYCLES; #endif - } + } #endif } #endif // SA1_OPCODES } -// STP +// Usually an STP opcode +// SNESAdvance speed hack, not implemented in Snes9xTYL | Snes9x-Euphoria static void OpDB (void) { +#if defined SNESADVANCE_SPEEDHACKS && defined CPU_SHUTDOWN + uint8 NextByte = *CPU.PC++; + + ForceShutdown(); + + int8 BranchOffset = (NextByte & 0x7F) | ((NextByte & 0x40) << 1); + // ^ -64 .. +63, sign extend bit 6 into 7 for unpacking + long TargetAddress = ((int) (CPU.PC - CPU.PCBase) + BranchOffset) & 0xffff; + + switch (NextByte & 0x80) + { + case 0x00: // BNE + BranchCheck1 (); + if (!CheckZero ()) { + CPU.PC = CPU.PCBase + TargetAddress; +#ifdef VAR_CYCLES + CPU.Cycles += ONE_CYCLE; +#else +#ifndef SA1_OPCODES + CPU.Cycles++; +#endif +#endif + CPUShutdown (); + } + return; + case 0x80: // BEQ + BranchCheck2 (); + if (CheckZero ()) { + CPU.PC = CPU.PCBase + TargetAddress; +#ifdef VAR_CYCLES + CPU.Cycles += ONE_CYCLE; +#else +#ifndef SA1_OPCODES + CPU.Cycles++; +#endif +#endif + CPUShutdown (); + } + return; + } +#else CPU.PC--; CPU.Flags |= DEBUG_MODE_FLAG; +#endif } -// Reserved S9xOpcode +// SNESAdvance speed hack, as implemented in Snes9xTYL / Snes9x-Euphoria +// https://code.google.com/p/snes9x-euphoria/source/browse/trunk/src/cpuops.cpp static void Op42 (void) { +#if defined SNESADVANCE_SPEEDHACKS && defined CPU_SHUTDOWN + uint8 NextByte = *CPU.PC++; + + ForceShutdown(); + + int8 BranchOffset = 0xF0 | (NextByte & 0xF); // always negative + long TargetAddress = ((int) (CPU.PC - CPU.PCBase) + BranchOffset) & 0xffff; + + switch (NextByte & 0xF0) + { + case 0x10: // BPL + BranchCheck1 (); + if (!CheckNegative ()) { + CPU.PC = CPU.PCBase + TargetAddress; +#ifdef VAR_CYCLES + CPU.Cycles += ONE_CYCLE; +#else +#ifndef SA1_OPCODES + CPU.Cycles++; +#endif +#endif + CPUShutdown (); + } + return; + case 0x30: // BMI + BranchCheck1 (); + if (CheckNegative ()) { + CPU.PC = CPU.PCBase + TargetAddress; +#ifdef VAR_CYCLES + CPU.Cycles += ONE_CYCLE; +#else +#ifndef SA1_OPCODES + CPU.Cycles++; +#endif +#endif + CPUShutdown (); + } + return; + case 0x50: // BVC + BranchCheck0 (); + if (!CheckOverflow ()) { + CPU.PC = CPU.PCBase + TargetAddress; +#ifdef VAR_CYCLES + CPU.Cycles += ONE_CYCLE; +#else +#ifndef SA1_OPCODES + CPU.Cycles++; +#endif +#endif + CPUShutdown (); + } + return; + case 0x70: // BVS + BranchCheck0 (); + if (CheckOverflow ()) { + CPU.PC = CPU.PCBase + TargetAddress; +#ifdef VAR_CYCLES + CPU.Cycles += ONE_CYCLE; +#else +#ifndef SA1_OPCODES + CPU.Cycles++; +#endif +#endif + CPUShutdown (); + } + return; + case 0x80: // BRA + CPU.PC = CPU.PCBase + TargetAddress; +#ifdef VAR_CYCLES + CPU.Cycles += ONE_CYCLE; +#else +#ifndef SA1_OPCODES + CPU.Cycles++; +#endif +#endif + CPUShutdown (); + return; + case 0x90: // BCC + BranchCheck0 (); + if (!CheckCarry ()) { + CPU.PC = CPU.PCBase + TargetAddress; +#ifdef VAR_CYCLES + CPU.Cycles += ONE_CYCLE; +#else +#ifndef SA1_OPCODES + CPU.Cycles++; +#endif +#endif + CPUShutdown (); + } + return; + case 0xB0: // BCS + BranchCheck0 (); + if (CheckCarry ()) { + CPU.PC = CPU.PCBase + TargetAddress; +#ifdef VAR_CYCLES + CPU.Cycles += ONE_CYCLE; +#else +#ifndef SA1_OPCODES + CPU.Cycles++; +#endif +#endif + CPUShutdown (); + } + return; + case 0xD0: // BNE + BranchCheck1 (); + if (!CheckZero ()) { + CPU.PC = CPU.PCBase + TargetAddress; +#ifdef VAR_CYCLES + CPU.Cycles += ONE_CYCLE; +#else +#ifndef SA1_OPCODES + CPU.Cycles++; +#endif +#endif + CPUShutdown (); + } + return; + case 0xF0: // BEQ + BranchCheck2 (); + if (CheckZero ()) { + CPU.PC = CPU.PCBase + TargetAddress; +#ifdef VAR_CYCLES + CPU.Cycles += ONE_CYCLE; +#else +#ifndef SA1_OPCODES + CPU.Cycles++; +#endif +#endif + CPUShutdown (); + } + return; + } +#endif } /*****************************************************************************/ diff --git a/source/memmap.cpp b/source/memmap.cpp index 04bc621e..364cbecc 100644 --- a/source/memmap.cpp +++ b/source/memmap.cpp @@ -617,6 +617,20 @@ bool8 CMemory::LoadROM (const char *filename) S9xMessage(S9X_ERROR,S9X_ROM_CONFUSING_FORMAT_INFO, "Warning! Hacked Dump!"); } +#ifdef SNESADVANCE_SPEEDHACKS + // Apply SNESAdvance speed hacks here + // For now, we just do these two, hardcoded: + if (strncmp("YOSHI'S ISLAND", (char *) &ROM[0x7FC0], 14) == 0) + { + ROM[0x00F4] = 0x42; + ROM[0x00F5] = 0x3B; + } + else if (strncmp("SUPER MARIOWORLD", (char *) &ROM[0x7FC0], 16) == 0) + { + ROM[0x006D] = 0x42; + } +#endif + int orig_hi_score, orig_lo_score; int hi_score, lo_score; From 4ea3566e3db68e3dc0894498c2103b4ac0a771ca Mon Sep 17 00:00:00 2001 From: Nebuleon Fumika Date: Sun, 6 Jan 2013 19:27:58 -0500 Subject: [PATCH 2/9] Release 1.14+speedhack-a1. --- README.md | 2 +- source/nds/gui.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c4c1ffa7..661a4808 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -CATSFC version 1.14, 2013-01-05 +CATSFC version 1.14 SPEEDHACK TEST BUILD 1, 2013-01-06 A Super Nintendo emulator for the Supercard DSTWO. diff --git a/source/nds/gui.c b/source/nds/gui.c index 4ebb2245..dc822bc8 100644 --- a/source/nds/gui.c +++ b/source/nds/gui.c @@ -59,7 +59,7 @@ char *language_options[] = { (char *) &lang[0], (char *) &lang[1], (char *) &lan ******************************************************************************/ #define SUBMENU_ROW_NUM 6 -#define NDSSFC_VERSION "1.14" +#define NDSSFC_VERSION "1.14+speedhack-a1" #define SAVE_STATE_SLOT_NUM 10 From 82ebd50e37ba7cb7cc0e2ad2b995bee7f9cf127d Mon Sep 17 00:00:00 2001 From: Nebuleon Fumika Date: Mon, 7 Jan 2013 02:16:34 -0500 Subject: [PATCH 3/9] Add support for user-selected and automatic frame skipping. Add support for PAL timings (20 ms per frame). User-selected frameskip causes slowdowns if the game runs slower than the resulting frame rate, but synchronises correctly if the game runs faster. Automatic frame skipping is still the default. It now only skips up to 8 frames, but in some games still skips that entire 8 frames. What's needed is an algorithm that averages frame latencies over a few seconds and skips while the latency is LOWER than the average. --- CATSFC/system/language.msg | 108 +++++++++++++++----- source/cheats.h | 2 +- source/cpuexec.cpp | 75 -------------- source/nds/entry.cpp | 195 +++++++++++++++++++++++++++---------- source/nds/gui.c | 21 ++-- source/nds/gui.h | 62 ++++++++++-- source/nds/message.h | 17 +++- source/snes9x.h | 4 + 8 files changed, 308 insertions(+), 176 deletions(-) diff --git a/CATSFC/system/language.msg b/CATSFC/system/language.msg index 5e04014a..9395db1c 100644 --- a/CATSFC/system/language.msg +++ b/CATSFC/system/language.msg @@ -28,15 +28,13 @@ Options #MSG_MAIN_MENU_EXIT Exit #FMT_VIDEO_ASPECT_RATIO -Aspect ratio %s +Aspect ratio %s #FMT_VIDEO_FAST_FORWARD -Fast-forward %s -#FMT_VIDEO_FRAME_SKIP_AUTOMATIC -Frame skipping %s -#FMT_VIDEO_FRAME_SKIP_MANUAL -Frame skipping %d +Fast-forward %s +#FMT_VIDEO_FRAME_SKIPPING +Frame skipping %s #FMT_AUDIO_SOUND -Sound %s +Sound %s #MSG_SAVED_STATE_CREATE Create saved state #FMT_SAVED_STATE_LOAD @@ -78,11 +76,31 @@ Select a game #MSG_VIDEO_ASPECT_RATIO_3 [3] Middle, square pixels #MSG_VIDEO_ASPECT_RATIO_4 -[4] Entire screen, antialiased -#MSG_FRAMESKIP_0 -Manual -#MSG_FRAMESKIP_1 -Automatic +[4] Entire screen, smoothed +#MSG_VIDEO_FRAME_SKIPPING_AUTOMATIC +[-] Keep up with the game +#MSG_VIDEO_FRAME_SKIPPING_0 +[0] Show all frames +#MSG_VIDEO_FRAME_SKIPPING_1 +[1] Show 1 in 2 frames +#MSG_VIDEO_FRAME_SKIPPING_2 +[2] Show 1 in 3 frames +#MSG_VIDEO_FRAME_SKIPPING_3 +[3] Show 1 in 4 frames +#MSG_VIDEO_FRAME_SKIPPING_4 +[4] Show 1 in 5 frames +#MSG_VIDEO_FRAME_SKIPPING_5 +[5] Show 1 in 6 frames +#MSG_VIDEO_FRAME_SKIPPING_6 +[6] Show 1 in 7 frames +#MSG_VIDEO_FRAME_SKIPPING_7 +[7] Show 1 in 8 frames +#MSG_VIDEO_FRAME_SKIPPING_8 +[8] Show 1 in 9 frames +#MSG_VIDEO_FRAME_SKIPPING_9 +[9] Show 1 in 10 frames +#MSG_VIDEO_FRAME_SKIPPING_10 +[10] Show 1 in 11 frames #MSG_GENERAL_OFF Off #MSG_GENERAL_ON @@ -186,10 +204,8 @@ STARTCHINESESIM 画面 %s #FMT_VIDEO_FAST_FORWARD 游戏快进 %s -#FMT_VIDEO_FRAME_SKIP_AUTOMATIC +#FMT_VIDEO_FRAME_SKIPPING 跳帧方式 %s -#FMT_VIDEO_FRAME_SKIP_MANUAL -跳帧级别 %d #FMT_AUDIO_SOUND 声音开关 %s #MSG_SAVED_STATE_CREATE @@ -234,10 +250,30 @@ CPU主频 %d 显示模式 3 #MSG_VIDEO_ASPECT_RATIO_4 显示模式 4 -#MSG_FRAMESKIP_0 -手动 -#MSG_FRAMESKIP_1 -自动 +#MSG_VIDEO_FRAME_SKIPPING_AUTOMATIC +[-] Keep up with the game +#MSG_VIDEO_FRAME_SKIPPING_0 +[0] Show all frames +#MSG_VIDEO_FRAME_SKIPPING_1 +[1] Show 1 in 2 frames +#MSG_VIDEO_FRAME_SKIPPING_2 +[2] Show 1 in 3 frames +#MSG_VIDEO_FRAME_SKIPPING_3 +[3] Show 1 in 4 frames +#MSG_VIDEO_FRAME_SKIPPING_4 +[4] Show 1 in 5 frames +#MSG_VIDEO_FRAME_SKIPPING_5 +[5] Show 1 in 6 frames +#MSG_VIDEO_FRAME_SKIPPING_6 +[6] Show 1 in 7 frames +#MSG_VIDEO_FRAME_SKIPPING_7 +[7] Show 1 in 8 frames +#MSG_VIDEO_FRAME_SKIPPING_8 +[8] Show 1 in 9 frames +#MSG_VIDEO_FRAME_SKIPPING_9 +[9] Show 1 in 10 frames +#MSG_VIDEO_FRAME_SKIPPING_10 +[10] Show 1 in 11 frames #MSG_GENERAL_OFF 关 #MSG_GENERAL_ON @@ -345,10 +381,8 @@ Quitter Format d'image %s #FMT_VIDEO_FAST_FORWARD Avance rapide %s -#FMT_VIDEO_FRAME_SKIP_AUTOMATIC +#FMT_VIDEO_FRAME_SKIPPING Omission d'images %s -#FMT_VIDEO_FRAME_SKIP_MANUAL -Images omises %d #FMT_AUDIO_SOUND Son %s #MSG_SAVED_STATE_CREATE @@ -392,11 +426,31 @@ Sélectionner un jeu #MSG_VIDEO_ASPECT_RATIO_3 [3] Milieu, pixels carrés #MSG_VIDEO_ASPECT_RATIO_4 -[4] Écran entier anticrénelé -#MSG_FRAMESKIP_0 -Manuelle -#MSG_FRAMESKIP_1 -Automatique +[4] Écran entier lissé +#MSG_VIDEO_FRAME_SKIPPING_AUTOMATIC +[-] Suivre le jeu +#MSG_VIDEO_FRAME_SKIPPING_0 +[0] N'omettre aucune image +#MSG_VIDEO_FRAME_SKIPPING_1 +[1] Montrer 1 image sur 2 +#MSG_VIDEO_FRAME_SKIPPING_2 +[2] Montrer 1 image sur 3 +#MSG_VIDEO_FRAME_SKIPPING_3 +[3] Montrer 1 image sur 4 +#MSG_VIDEO_FRAME_SKIPPING_4 +[4] Montrer 1 image sur 5 +#MSG_VIDEO_FRAME_SKIPPING_5 +[5] Montrer 1 image sur 6 +#MSG_VIDEO_FRAME_SKIPPING_6 +[6] Montrer 1 image sur 7 +#MSG_VIDEO_FRAME_SKIPPING_7 +[7] Montrer 1 image sur 8 +#MSG_VIDEO_FRAME_SKIPPING_8 +[8] Montrer 1 image sur 9 +#MSG_VIDEO_FRAME_SKIPPING_9 +[9] Montrer 1 image sur 10 +#MSG_VIDEO_FRAME_SKIPPING_10 +[10] Montrer 1 image sur 11 #MSG_GENERAL_OFF Hors fonction #MSG_GENERAL_ON diff --git a/source/cheats.h b/source/cheats.h index bb2646f9..2f3252e8 100644 --- a/source/cheats.h +++ b/source/cheats.h @@ -102,7 +102,7 @@ struct SCheat uint8 byte; uint8 saved_byte; // bool8 enabled; - uint32 enabled; // THIS IS A TOTAL HACK FOR THE NDSSFC GUI, YOU HAVE BEEN WARNED [Neb] + u32 enabled; // THIS IS A TOTAL HACK FOR THE NDSSFC GUI, YOU HAVE BEEN WARNED [Neb] bool8 saved; char name[MAX_SFCCHEAT_NAME]; }; diff --git a/source/cpuexec.cpp b/source/cpuexec.cpp index 66bb8da5..b90da6da 100644 --- a/source/cpuexec.cpp +++ b/source/cpuexec.cpp @@ -330,81 +330,6 @@ void S9xDoHBlankProcessing () Snapshot (NULL); } #endif - if(!game_fast_forward) - { - syncnow = getSysTime(); - if(syncnow > sync_next) - { - /* - * Little bit of a hack here: - * If we get behind and stay behind for a certain number - * of frames, we automatically enable fast forward. - * That really helps with certain games, such as - * Super Mario RPG and Yoshi's Island. - */ - if(skip_rate++ < 10) - { - syncdif = syncnow - sync_next; - if(syncdif < 11718) - { - IPPU.RenderThisFrame = false; - sync_next += 391; - } - else - { //lag more than 0.5s, maybe paused - IPPU.RenderThisFrame = true; - sync_next = syncnow; - framenum = 0; - sync_last = syncnow; - realframe = 1; - } - } - else - { - skip_rate = 0; - IPPU.RenderThisFrame = true; - sync_last= syncnow; - sync_next = syncnow+391; - } - } - else - { - skip_rate = 0; - syncdif = sync_next - syncnow; - if(syncdif > 391) - { - udelay(syncdif*22); - S9xProcessSound (0); - } - - IPPU.RenderThisFrame = true; - sync_next += 391; //16.7ms - realframe += 1; - } -#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; - } -#endif - } - else - { - sync_last= 0; - sync_next = 0; - - if(skip_rate++ < 10) - IPPU.RenderThisFrame = false; - else - { - skip_rate = 0; - IPPU.RenderThisFrame = true; - } - } } if (CPU.V_Counter == PPU.ScreenHeight + 3) diff --git a/source/nds/entry.cpp b/source/nds/entry.cpp index 5573f478..58adf494 100644 --- a/source/nds/entry.cpp +++ b/source/nds/entry.cpp @@ -343,6 +343,18 @@ void game_disableAudio() S9xSetSoundMute (TRUE); } } + +void game_set_frameskip() +{ + if( game_config.frameskip_value == 0) + { + Settings.SkipFrames = AUTO_FRAMERATE; + } + else + { + Settings.SkipFrames = game_config.frameskip_value - 1 /* 1 -> 0 and so on */; + } +} void init_sfc_setting(void) { @@ -368,7 +380,6 @@ void init_sfc_setting(void) Settings.ShutdownMaster = TRUE; Settings.FrameTimePAL = 20000; Settings.FrameTimeNTSC = 16667; - Settings.FrameTime = Settings.FrameTimeNTSC; Settings.DisableSampleCaching = FALSE; Settings.DisableMasterVolume = FALSE; Settings.Mouse = TRUE; @@ -449,10 +460,12 @@ int load_gamepak(char* file) game_disableAudio(); CPU.Flags = 0; - S9xReset (); // mdelay(50); // Delete this delay if (!Memory.LoadROM (file)) return -1; + S9xReset (); + + Settings.FrameTime = (Settings.PAL ? Settings.FrameTimePAL : Settings.FrameTimeNTSC); Memory.LoadSRAM (S9xGetFilename (".srm")); // mdelay(50); // Delete this delay @@ -611,41 +624,18 @@ int sfc_main (int argc, char **argv) return (0); } +static unsigned int sync_last= 0; +static unsigned int sync_next = 0; +static unsigned int framenum = 0; + +extern "C" u32 game_fast_forward; + +static unsigned int skip_rate= 0; + void S9xSyncSpeed () { -#if 0 -#ifdef _NETPLAY_SUPPORT - if (Settings.NetPlay) - { - // XXX: Send joypad position update to server - // XXX: Wait for heart beat from server - S9xNetPlaySendJoypadUpdate (joypads [0]); - if (!S9xNetPlayCheckForHeartBeat ()) - { - do - { - CHECK_SOUND (); -// S9xProcessEvents (FALSE); - } while (!S9xNetPlayCheckForHeartBeat ()); - IPPU.RenderThisFrame = TRUE; - IPPU.SkippedFrames = 0; - } - else - { - if (IPPU.SkippedFrames < 10) - { - IPPU.SkippedFrames++; - IPPU.RenderThisFrame = FALSE; - } - else - { - IPPU.RenderThisFrame = TRUE; - IPPU.SkippedFrames = 0; - } - } - } - else -#endif + uint32 syncnow; + int32 syncdif; #if 0 if (Settings.SoundSync == 2) @@ -655,30 +645,135 @@ void S9xSyncSpeed () return; } #endif + syncnow = getSysTime(); + + if (game_fast_forward) + { + sync_last = syncnow; + sync_next = syncnow; + if(++skip_rate < 10) + IPPU.RenderThisFrame = false; + else + { + skip_rate = 0; + IPPU.RenderThisFrame = true; + } + } + else if (Settings.SkipFrames == AUTO_FRAMERATE /* && !game_fast_forward */) + { + // frame_time is in getSysTime units: 42.667 microseconds. + uint32 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. + IPPU.RenderThisFrame = TRUE; + 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 (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(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; + framenum = 0; + } + } + else + { + skip_rate = 0; + IPPU.RenderThisFrame = TRUE; + sync_next = syncnow + frame_time; + } + } + else // Early + { + skip_rate = 0; + ds2_setCPUclocklevel(0); + if (syncdif > 0) + udelay(syncdif * 128 / 3 /* times 42 + 2/3 microseconds */); + set_cpu_clock(clock_speed_number); + S9xProcessSound (0); + + IPPU.RenderThisFrame = TRUE; + sync_next += frame_time; + } #if 0 - if (Settings.TurboMode) - { - if(++IPPU.FrameSkip >= Settings.TurboSkipFrames) - { - IPPU.FrameSkip = 0; - IPPU.SkippedFrames = 0; - IPPU.RenderThisFrame = TRUE; - } - else - { - ++IPPU.SkippedFrames; - IPPU.RenderThisFrame = FALSE; - } - return; - } + 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) */ + { + // 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) + { + skip_rate = 0; + IPPU.RenderThisFrame = TRUE; + // Are we early? + syncdif = sync_next - syncnow; + if (syncdif > 0) + { + ds2_setCPUclocklevel(0); + udelay(syncdif * 128 / 3 /* times 42 + 2/3 microseconds */); + set_cpu_clock(clock_speed_number); + S9xProcessSound (0); + // After that little delay, what time is it? + syncnow = getSysTime(); + } + sync_next = syncnow + frame_time * Settings.SkipFrames; + } + else + { + IPPU.RenderThisFrame = FALSE; + } + } #ifdef __sgi /* BS: saves on CPU usage */ sginap(1); #endif +#if 0 /* Check events */ static struct timeval next1 = {0, 0}; diff --git a/source/nds/gui.c b/source/nds/gui.c index dc822bc8..33196abb 100644 --- a/source/nds/gui.c +++ b/source/nds/gui.c @@ -17,8 +17,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "snes9x.h" - #include #include #include @@ -78,7 +78,7 @@ GAME_CONFIG game_config; //save state file map char savestate_map[SAVE_STATE_SLOT_NUM]; -static unsigned int savestate_index; +static u32 savestate_index; #define MAKE_MENU(name, init_function, passive_function, key_function, end_function, \ focus_option, screen_focus) \ @@ -2776,7 +2776,7 @@ u32 menu(u16 *screen) (char*)&msg[MSG_VIDEO_ASPECT_RATIO_3], (char*)&msg[MSG_VIDEO_ASPECT_RATIO_4]}; - char *frameskip_options[] = { (char*)&msg[MSG_FRAMESKIP_0], (char*)&msg[MSG_FRAMESKIP_1] }; + char *frameskip_options[] = { (char*)&msg[MSG_VIDEO_FRAME_SKIPPING_AUTOMATIC], (char*)&msg[MSG_VIDEO_FRAME_SKIPPING_0], (char*)&msg[MSG_VIDEO_FRAME_SKIPPING_1], (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 *on_off_options[] = { (char*)&msg[MSG_GENERAL_OFF], (char*)&msg[MSG_GENERAL_ON] }; @@ -2800,7 +2800,10 @@ u32 menu(u16 *screen) &game_fast_forward, 2, NULL, ACTION_TYPE, 2), /* 03 */ STRING_SELECTION_OPTION(game_disableAudio, NULL, &msg[FMT_AUDIO_SOUND], sound_seletion, - &game_enable_audio, 2, NULL, ACTION_TYPE, 3) + &game_enable_audio, 2, NULL, ACTION_TYPE, 3), + + /* 04 */ STRING_SELECTION_OPTION(game_set_frameskip, NULL, &msg[FMT_VIDEO_FRAME_SKIPPING], frameskip_options, + &game_config.frameskip_value, 12 /* auto (0) and 0..10 (1..11) make 12 option values */, NULL, ACTION_TYPE, 4) }; MAKE_MENU(graphics, NULL, NULL, NULL, NULL, 0, 0); @@ -4004,9 +4007,7 @@ void init_game_config(void) game_config.clock_speed_number = 5; // 396 MHz by default clock_speed_number = 5; game_config.graphic = 3; // By default, have a good-looking aspect ratio - - game_config.gamepad_config_menu = BUTTON_ID_TOUCH; - memcpy(game_config.gamepad_config_map, gamepad_config_map_init, sizeof(gamepad_config_map_init)); + game_config.frameskip_value = 0; // Automatic frame skipping game_config.backward = 0; //time backward disable game_config.backward_time = 2; //time backward granularity 1s @@ -4063,9 +4064,8 @@ void load_game_config_file(void) { fread(&game_config, 1, sizeof(GAME_CONFIG), fp); - memcpy(gamepad_config_map, game_config.gamepad_config_map, sizeof(game_config.gamepad_config_map)); - gamepad_config_menu = game_config.gamepad_config_menu; clock_speed_number = game_config.clock_speed_number; + Settings.SkipFrames = (game_config.frameskip_value == 0 ? AUTO_FRAMERATE : game_config.frameskip_value - 1 /* 1 -> 0 and so on */); } fclose(fp); @@ -4113,9 +4113,6 @@ int save_game_config_file(void) if(gamepak_name[0] == 0) return -1; - memcpy(game_config.gamepad_config_map, gamepad_config_map, sizeof(game_config.gamepad_config_map)); - game_config.gamepad_config_menu = gamepad_config_menu; - sprintf(game_config_filename, "%s/%s", DEFAULT_CFG_DIR, gamepak_name); pt = strrchr(game_config_filename, '.'); if(NULL == pt) diff --git a/source/nds/gui.h b/source/nds/gui.h index 5a409793..ec0dd1a2 100644 --- a/source/nds/gui.h +++ b/source/nds/gui.h @@ -44,16 +44,63 @@ struct _EMU_CONFIG struct _GAME_CONFIG { - u32 clock_speed_number; - u32 frameskip_type; + u32 clock_speed_number; + u32 Reserved0; u32 frameskip_value; - u32 graphic; - u32 enable_audio; - u32 gamepad_config_menu; + u32 graphic; + u32 enable_audio; + u32 Reserved1; u32 backward; u32 backward_time; - u32 reserve[32]; - u32 gamepad_config_map[MAX_GAMEPAD_MAP]; + u32 Reserved2; + u32 Reserved3; + u32 Reserved4; + u32 Reserved5; + u32 Reserved6; + u32 Reserved7; + u32 Reserved8; + u32 Reserved9; + u32 Reserved10; + u32 Reserved11; + u32 Reserved12; + u32 Reserved13; + u32 Reserved14; + u32 Reserved15; + u32 Reserved16; + u32 Reserved17; + u32 Reserved18; + u32 Reserved19; + u32 Reserved20; + u32 Reserved21; + u32 Reserved22; + u32 Reserved23; + u32 Reserved24; + u32 Reserved25; + u32 Reserved26; + u32 Reserved27; + u32 Reserved28; + u32 Reserved29; + u32 Reserved30; + u32 Reserved31; + u32 Reserved32; + + u32 Reserved33; + u32 Reserved34; + u32 Reserved35; + u32 Reserved36; + u32 Reserved37; + u32 Reserved38; + u32 Reserved39; + u32 Reserved40; + u32 Reserved41; + u32 Reserved42; + u32 Reserved43; + u32 Reserved44; + u32 Reserved45; + u32 Reserved46; + u32 Reserved47; + u32 Reserved48; + u32 Reserved49; }; typedef enum @@ -118,6 +165,7 @@ extern GAME_CONFIG game_config; extern void gui_init(u32 lang_id); extern u32 menu(u16 *original_screen); extern void game_disableAudio(); +extern void game_set_frameskip(); extern void set_cpu_clock(u32 num); #ifdef __cplusplus diff --git a/source/nds/message.h b/source/nds/message.h index ee89acde..09332bc2 100644 --- a/source/nds/message.h +++ b/source/nds/message.h @@ -30,8 +30,7 @@ enum MSG MSG_MAIN_MENU_EXIT, FMT_VIDEO_ASPECT_RATIO, FMT_VIDEO_FAST_FORWARD, - FMT_VIDEO_FRAME_SKIP_AUTOMATIC, - FMT_VIDEO_FRAME_SKIP_MANUAL, + FMT_VIDEO_FRAME_SKIPPING, FMT_AUDIO_SOUND, MSG_SAVED_STATE_CREATE, FMT_SAVED_STATE_LOAD, @@ -56,8 +55,18 @@ enum MSG MSG_VIDEO_ASPECT_RATIO_3, MSG_VIDEO_ASPECT_RATIO_4, - MSG_FRAMESKIP_0, // currently unused - MSG_FRAMESKIP_1, // currently unused + MSG_VIDEO_FRAME_SKIPPING_AUTOMATIC, + MSG_VIDEO_FRAME_SKIPPING_0, + MSG_VIDEO_FRAME_SKIPPING_1, + MSG_VIDEO_FRAME_SKIPPING_2, + MSG_VIDEO_FRAME_SKIPPING_3, + MSG_VIDEO_FRAME_SKIPPING_4, + MSG_VIDEO_FRAME_SKIPPING_5, + MSG_VIDEO_FRAME_SKIPPING_6, + MSG_VIDEO_FRAME_SKIPPING_7, + MSG_VIDEO_FRAME_SKIPPING_8, + MSG_VIDEO_FRAME_SKIPPING_9, + MSG_VIDEO_FRAME_SKIPPING_10, MSG_GENERAL_OFF, MSG_GENERAL_ON, diff --git a/source/snes9x.h b/source/snes9x.h index e9ed479f..e9e29b8b 100644 --- a/source/snes9x.h +++ b/source/snes9x.h @@ -97,9 +97,13 @@ #include "fs_api.h" #include "ds2_malloc.h" +#ifdef __cplusplus extern "C" { +#endif extern int cprintf(const char *fmt, ...); +#ifdef __cplusplus } +#endif #ifdef __WIN32__ #include "..\wsnes9x.h" From c61b48ad1a237fda7c8e886c7c3b0899accbd3e6 Mon Sep 17 00:00:00 2001 From: Nebuleon Fumika Date: Mon, 7 Jan 2013 02:38:03 -0500 Subject: [PATCH 4/9] Add a section about frame skipping in the readme. --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index 661a4808..ff244bab 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,24 @@ To add cheats to the menu in a game, first load the game, then use the Cheats menu's "Load a cheat file" option. The option can be touched using the Touch Screen, but does not activate the menu. So press A. +# Frame skipping + +In the Video & audio menu, the **Frame skipping** option allows you to select +a number of frames to skip between rendered frames. +* Setting this to 0 will show every single frame, but this will slow down the + game considerably, as the DSTWO would only have enough processing power to + emulate **and** render a few frames per second. It has enough power to + emulate all frames and render **some**, though. +* Setting this to 10 will skip 10 frames and render one, but this will + severely desynchronise the audio. You will also find yourself unable to + perform actions during the correct frame with the controller. +* Setting this to - (Keep up with the game) will make the emulator try to + render the game at its correct speed, dropping frames as needed (up to 8). + +It is recommended to start with frame skipping 4 (Show 1 frame every 5) and +go to 3 or 2 if the game doesn't run with major slowdowns with them. If you +don't like the slowdowns, return to frame skipping 4 or -. + # The font The font used by CATSFC is now similar to the Pictochat font. To modify it, From 6681d4ade7f34787417d74799eb1aa179ab8992a Mon Sep 17 00:00:00 2001 From: Nebuleon Fumika Date: Mon, 7 Jan 2013 03:26:53 -0500 Subject: [PATCH 5/9] Fix an off-by-one in the manual frameskip code. It would raise the sound speed by 25% if frame skip 3 (Show 1 in 4) rendered a frame early. Optimise the controller code. I don't expect to allow remapping any time soon, because the DS has buttons for every single SNES controller button and nothing more. --- source/nds/entry.cpp | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/source/nds/entry.cpp b/source/nds/entry.cpp index 58adf494..bff57b2d 100644 --- a/source/nds/entry.cpp +++ b/source/nds/entry.cpp @@ -760,7 +760,7 @@ void S9xSyncSpeed () // After that little delay, what time is it? syncnow = getSysTime(); } - sync_next = syncnow + frame_time * Settings.SkipFrames; + sync_next = syncnow + frame_time * (Settings.SkipFrames + 1); } else { @@ -1049,7 +1049,7 @@ void Init_Timer (void) } - +/* const unsigned int keymap[12] = { 0x80, //KEY_A 0x8000, //KEY_B @@ -1064,6 +1064,7 @@ const unsigned int keymap[12] = { 0x40, //KEY_X 0x4000 //KEY_Y }; +*/ unsigned int S9xReadJoypad (int which1) { @@ -1077,6 +1078,7 @@ unsigned int S9xReadJoypad (int which1) ds2_setSupend(); do { ds2_getrawInput(&inputdata); + mdelay(1); } while (inputdata.key & KEY_LID); ds2_wakeup(); set_cpu_clock(clock_speed_number); @@ -1089,12 +1091,28 @@ unsigned int S9xReadJoypad (int which1) { unsigned int key; unsigned int i; - - key = 0; + + // 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 + // 0x0010 -> 0x0100; 0x0020 -> 0x0200 + // 0x0030 -> 0x0300 + key |= (inputdata.key & (KEY_RIGHT | KEY_LEFT)) << 4; + // 0x0100 -> 0x0010; 0x0200 -> 0x0020; 0x0400 -> 0x0040 + // 0x0700 -> 0x0070 + key |= (inputdata.key & (KEY_R | KEY_L | KEY_X)) >> 4; + // 0x0080 -> 0x0400; 0x0800 -> 0x4000 + // 0x0880 -> 0x4400 + key |= (inputdata.key & (KEY_DOWN | KEY_Y)) << 3; +/* for(i= 0; i < 12; i++) //remap key { key |= (inputdata.key & (1< Date: Mon, 7 Jan 2013 04:38:35 -0500 Subject: [PATCH 6/9] Smooth out the automatic frame skipping such that it doesn't go from 1 FPS to 8 FPS right away and constantly. The equivalent frame skip will be 2, 3, 5 or 8 most of the time, depending on the game, and will vary a bit depending on rendering demands. For example, the frame skipping in Super Mario World is 3 on the map and in graphically simple levels, and 5 in complex levels. --- source/nds/entry.cpp | 53 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/source/nds/entry.cpp b/source/nds/entry.cpp index bff57b2d..63d736fc 100644 --- a/source/nds/entry.cpp +++ b/source/nds/entry.cpp @@ -626,7 +626,7 @@ int sfc_main (int argc, char **argv) static unsigned int sync_last= 0; static unsigned int sync_next = 0; -static unsigned int framenum = 0; +static unsigned int auto_equivalent_skip = 0; extern "C" u32 game_fast_forward; @@ -662,6 +662,54 @@ void S9xSyncSpeed () } else if (Settings.SkipFrames == AUTO_FRAMERATE /* && !game_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 > auto_equivalent_skip) + { + skip_rate = 0; + IPPU.RenderThisFrame = TRUE; + // Are we early? + syncdif = sync_next - syncnow; + if (syncdif > 0) + { + // Are we VERY early? Say, 3 entire frames... + if (syncdif >= frame_time * 3) + auto_equivalent_skip -= 2; + // or one + else if (syncdif >= frame_time) + auto_equivalent_skip--; + ds2_setCPUclocklevel(0); + udelay(syncdif * 128 / 3 /* times 42 + 2/3 microseconds */); + set_cpu_clock(clock_speed_number); + S9xProcessSound (0); + // After that little delay, what time is it? + syncnow = getSysTime(); + } + else + { + // We're late. + // If we're over half a second late, we were + // paused, so do nothing. + if (syncdif <= -11719 /* 500.0 ms late or more */) + sync_next = syncnow + frame_time * (auto_equivalent_skip + 1); + else if (auto_equivalent_skip < 7) + auto_equivalent_skip++; + } + if (auto_equivalent_skip >= 8) + // If we're skipping loads, rebase time to now. + sync_next = syncnow + frame_time * (auto_equivalent_skip + 1); + else + // Otherwise, keep track of partial-frame + // latencies for a bit more. + sync_next += frame_time * (auto_equivalent_skip + 1); + } + else + { + IPPU.RenderThisFrame = FALSE; + } + +#if 0 // frame_time is in getSysTime units: 42.667 microseconds. uint32 frame_time = Settings.PAL ? 468 /* = 20.0 ms */ : 391 /* = 16.67 ms */; if (sync_last > syncnow) // Overflow occurred! (every 50 hrs) @@ -729,7 +777,7 @@ void S9xSyncSpeed () IPPU.RenderThisFrame = TRUE; sync_next += frame_time; } -#if 0 +/* if(++framenum >= 60) { syncdif = syncnow - sync_last; @@ -738,6 +786,7 @@ void S9xSyncSpeed () //printf("T %d %d\n", syncdif*42667/1000, realframe); realframe = 0; } +*/ #endif } else /* if (Settings.SkipFrames != AUTO_FRAMERATE && !game_fast_forward) */ From d9b6322caafcbd355848a32acd8b8c0fa1746264 Mon Sep 17 00:00:00 2001 From: Nebuleon Fumika Date: Mon, 7 Jan 2013 16:21:32 -0500 Subject: [PATCH 7/9] Add a bit that tolerates 1/16 latency per frame in automatic frameskip mode. Remove old automatic frameskip code. --- source/nds/entry.cpp | 82 ++------------------------------------------ 1 file changed, 2 insertions(+), 80 deletions(-) diff --git a/source/nds/entry.cpp b/source/nds/entry.cpp index 63d736fc..d9050caf 100644 --- a/source/nds/entry.cpp +++ b/source/nds/entry.cpp @@ -693,7 +693,8 @@ void S9xSyncSpeed () // paused, so do nothing. if (syncdif <= -11719 /* 500.0 ms late or more */) sync_next = syncnow + frame_time * (auto_equivalent_skip + 1); - else if (auto_equivalent_skip < 7) + // Tolerate 1/16 late without skipping. + else if (syncdif < -(frame_time * auto_equivalent_skip / 16) && auto_equivalent_skip < 7) auto_equivalent_skip++; } if (auto_equivalent_skip >= 8) @@ -709,85 +710,6 @@ void S9xSyncSpeed () IPPU.RenderThisFrame = FALSE; } -#if 0 - // frame_time is in getSysTime units: 42.667 microseconds. - uint32 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. - IPPU.RenderThisFrame = TRUE; - 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 (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(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; - framenum = 0; - } - } - else - { - skip_rate = 0; - IPPU.RenderThisFrame = TRUE; - sync_next = syncnow + frame_time; - } - } - else // Early - { - skip_rate = 0; - ds2_setCPUclocklevel(0); - if (syncdif > 0) - udelay(syncdif * 128 / 3 /* times 42 + 2/3 microseconds */); - set_cpu_clock(clock_speed_number); - S9xProcessSound (0); - - IPPU.RenderThisFrame = TRUE; - sync_next += frame_time; - } -/* - 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) */ { From 6329ef7a3237a06708ed1a29f95b7f89277b142a Mon Sep 17 00:00:00 2001 From: Nebuleon Fumika Date: Mon, 7 Jan 2013 19:40:05 -0500 Subject: [PATCH 8/9] Revert "Add a bit that tolerates 1/16 latency per frame in automatic frameskip mode." This reverts commit d9b6322caafcbd355848a32acd8b8c0fa1746264. --- source/nds/entry.cpp | 82 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 2 deletions(-) diff --git a/source/nds/entry.cpp b/source/nds/entry.cpp index d9050caf..63d736fc 100644 --- a/source/nds/entry.cpp +++ b/source/nds/entry.cpp @@ -693,8 +693,7 @@ void S9xSyncSpeed () // paused, so do nothing. if (syncdif <= -11719 /* 500.0 ms late or more */) sync_next = syncnow + frame_time * (auto_equivalent_skip + 1); - // Tolerate 1/16 late without skipping. - else if (syncdif < -(frame_time * auto_equivalent_skip / 16) && auto_equivalent_skip < 7) + else if (auto_equivalent_skip < 7) auto_equivalent_skip++; } if (auto_equivalent_skip >= 8) @@ -710,6 +709,85 @@ void S9xSyncSpeed () IPPU.RenderThisFrame = FALSE; } +#if 0 + // frame_time is in getSysTime units: 42.667 microseconds. + uint32 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. + IPPU.RenderThisFrame = TRUE; + 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 (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(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; + framenum = 0; + } + } + else + { + skip_rate = 0; + IPPU.RenderThisFrame = TRUE; + sync_next = syncnow + frame_time; + } + } + else // Early + { + skip_rate = 0; + ds2_setCPUclocklevel(0); + if (syncdif > 0) + udelay(syncdif * 128 / 3 /* times 42 + 2/3 microseconds */); + set_cpu_clock(clock_speed_number); + S9xProcessSound (0); + + IPPU.RenderThisFrame = TRUE; + sync_next += frame_time; + } +/* + 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) */ { From a88b70d07c0e4b9f164b1324a422f9e68931dec7 Mon Sep 17 00:00:00 2001 From: Nebuleon Fumika Date: Mon, 7 Jan 2013 20:15:49 -0500 Subject: [PATCH 9/9] Add hard-coded speed hacks for Super Mario All-Stars & World. Adjust the automatic frame skipping code to require auto_equivalent_skip to be greater than 0 before reducing the skip if it's early. Otherwise, we end up with overflow, thus skipping 4 billion frames. --- source/memmap.cpp | 26 +++++++++++++++++++++++--- source/nds/entry.cpp | 4 ++-- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/source/memmap.cpp b/source/memmap.cpp index 364cbecc..dd28366b 100644 --- a/source/memmap.cpp +++ b/source/memmap.cpp @@ -622,12 +622,32 @@ bool8 CMemory::LoadROM (const char *filename) // For now, we just do these two, hardcoded: if (strncmp("YOSHI'S ISLAND", (char *) &ROM[0x7FC0], 14) == 0) { - ROM[0x00F4] = 0x42; - ROM[0x00F5] = 0x3B; + ROM[0x0000F4] = 0x42; ROM[0x0000F5] = 0x3B; } else if (strncmp("SUPER MARIOWORLD", (char *) &ROM[0x7FC0], 16) == 0) { - ROM[0x006D] = 0x42; + ROM[0x00006D] = 0x42; + } + else if (strncmp("ALL_STARS + WORLD", (char *) &ROM[0x7FC0], 17) == 0) + { + ROM[0x0003D0] = 0x42; ROM[0x0003D1] = 0x5B; + ROM[0x018522] = 0x42; ROM[0x018523] = 0x5B; + ROM[0x02C804] = 0x42; ROM[0x02C805] = 0xBA; + ROM[0x0683B5] = 0x42; ROM[0x0683B6] = 0x5B; + ROM[0x0696AC] = 0x42; ROM[0x0696AD] = 0xBA; + ROM[0x089233] = 0xDB; ROM[0x089234] = 0x61; + ROM[0x0895DF] = 0x42; ROM[0x0895E0] = 0x5B; + ROM[0x0A7A9D] = 0x42; ROM[0x0A7A9E] = 0xBA; + ROM[0x1072E7] = 0x42; ROM[0x1072E8] = 0xD9; + ROM[0x107355] = 0x42; ROM[0x107356] = 0x5B; + ROM[0x1073CF] = 0x42; ROM[0x1073D0] = 0x5B; + ROM[0x107443] = 0x42; ROM[0x107444] = 0x5B; + ROM[0x107498] = 0x42; ROM[0x107499] = 0x5B; + ROM[0x107505] = 0x42; ROM[0x107506] = 0x5B; + ROM[0x107539] = 0x42; ROM[0x10753A] = 0x5B; + ROM[0x107563] = 0x42; ROM[0x107564] = 0x5B; + ROM[0x1801D4] = 0x42; ROM[0x1801D5] = 0x10; + ROM[0x18041D] = 0x42; ROM[0x18041E] = 0x79; } #endif diff --git a/source/nds/entry.cpp b/source/nds/entry.cpp index 63d736fc..e272415c 100644 --- a/source/nds/entry.cpp +++ b/source/nds/entry.cpp @@ -674,10 +674,10 @@ void S9xSyncSpeed () if (syncdif > 0) { // Are we VERY early? Say, 3 entire frames... - if (syncdif >= frame_time * 3) + if (syncdif >= frame_time * 3 && auto_equivalent_skip > 1) auto_equivalent_skip -= 2; // or one - else if (syncdif >= frame_time) + else if (syncdif >= frame_time && auto_equivalent_skip > 0) auto_equivalent_skip--; ds2_setCPUclocklevel(0); udelay(syncdif * 128 / 3 /* times 42 + 2/3 microseconds */);