diff --git a/dm/games/0400486024DEC000/game.tsv b/dm/games/0400486024DEC000/game.tsv index e0c8ec0cdc..5cf5214ac2 100644 --- a/dm/games/0400486024DEC000/game.tsv +++ b/dm/games/0400486024DEC000/game.tsv @@ -40,6 +40,7 @@ shader_note Ocarina of Time shaders # The game steps its time by a frame ratio, its base rate / its frame rate, whatever the frame took: frames longer than a vsync played in slow motion. The ratio takes the host's # frame step instead (MATCH_FRAME_STEP in patches.py, found by its function on any build that keeps it): 1 vsync at 60 fps, 2 when 60 does not hold, so game time = real time fps_target 60 +present_snap on # ---- Graphics ---- # agl wraps its clears in render conditions on Donkey Kong Bananza and Splatoon Raiders; the same library here diff --git a/dm/games/0400486024DEC000/patches.py b/dm/games/0400486024DEC000/patches.py index cade577a2e..e268f3f993 100644 --- a/dm/games/0400486024DEC000/patches.py +++ b/dm/games/0400486024DEC000/patches.py @@ -4,8 +4,8 @@ # The game's frame ratio. Once a frame the function at main+0x6e6be0 (v131072) divides its base rate (60.0, or 30.0 while bit 0 of [x21+0x24f] is set) by the int frame rate at # [x21+0x2c8] (fdiv s8,s1,s0 at +0xe4) and hands the result to the time object (main+0x3b4e300: ratio at +0x10, 1 / ratio at +0x14, round(60 / ratio) at +0x1c), which the game -# steps by; the frame's own time never enters it, so a frame that takes two vsyncs at rate 60 plays at half speed. The stub gives the ratio the host's step instead: step × base / 60 -# (the division's value when the host keeps frames 60 / rate vsyncs), and counts the vsyncs stepped. Until the host has decided a step, and at fps_target 0, the division runs. +# steps by; the frame's own time never enters it, so a frame that takes two vsyncs at rate 60 plays at half speed. The stub gives the ratio the host's step instead: (float)step +# (the ratio when the host keeps frames 60 / rate vsyncs), and counts the vsyncs stepped. Until the host has decided a step, and at fps_target 0, the division runs. # s0 and x16 / x17 are free there (s0 is written again on every path before it is read; the function uses neither x16 nor x17); s1 keeps the base rate. # Lichii (a GPL emulator; only the fact was taken) found the division (its notes: main+0x6E6CC4, 60 / [x21+0x2C8]) def frame_step(ctx): @@ -14,23 +14,20 @@ def frame_step(ctx): fs_site(site, '2818201e') # fdiv s8,s1,s0 start = here(); emit(ADRP(16, here(), FS_STATE_OFF)) for w in (0xb9400211, # ldr w17,[x16] target fps (0 = off) - 0x340001b1, # cbz w17,orig (+13) + 0x34000151, # cbz w17,orig (+10) 0xb9402211, # ldr w17,[x16,#0x20] step in vsyncs - 0x34000171, # cbz w17,orig (+11) not decided yet + 0x34000111, # cbz w17,orig (+8) not decided yet 0x1e230220, # ucvtf s0,w17 0xf9401a10, # ldr x16,[x16,#0x30] 0x8b110210): # add x16,x16,x17 emit(w) emit(ADRP(17, here(), FS_STATE_OFF)) for w in (0xf9001a30, # str x16,[x17,#0x30] vsyncs stepped - 0x1e210808, # fmul s8,s0,s1 step × base rate - 0x52a84e10, # mov w16,#0x42700000 60.0 - 0x1e270200, # fmov s0,w16 - 0x1e201908): # fdiv s8,s8,s0 / 60 + 0x1e204008): # fmov s8,s0 step as ratio emit(w) emit(B(here(), site + 4)) emit(0x1e201828); emit(B(here(), site + 4)) # orig: fdiv s8,s1,s0; back - fs_put(site, [B(site, start)], 'frame ratio: the host\'s step × base rate / 60 instead of base rate / the game\'s frame rate (state page target fps; 0 = the game\'s ratio)') + fs_put(site, [B(site, start)], 'frame ratio: the host\'s step instead of base rate / the game\'s frame rate (state page target fps; 0 = the game\'s ratio)') dynamic_resolution(ctx) # The game's dynamic resolution (game__gfx__DynamicResolutionParam: LoadThreshold, ReduceCounterMax, IncreaseCounterMax and a list of levels, each a ResolutionReduceScale and a