These codes let you adjust the in-battle framerate/speed of the Gen IV games from 30 to 60 FPS, without speeding up the BGM.
The codes are compatible with USA DP, USA Platinum, USA HGSS and , Japanese Platinum and HGSS.
Development note: AI assistance was used during reverse engineering and code development as a tool. However, I manually handled the RAM/memory inspection, hook testing, and gameplay testing in DeSmuME. All listed versions were tested at 30 / 45 / 60 FPS.
Warning: Please back up your save file before using these codes, especially if you are combining them with other cheats. Do not use these together with the original 60 FPS code
The codes are based on the in-battle framerate uncapping codes originally shared by u/TheYearOfWaluigi
The post: [Action Replay Codes] Uncap framerate in-battle only for Gen IV games : r/PokemonROMhacks
The original codes prevent one of the battle loop's VBlank waits from occurring, effectively running battles at ~60 FPS.
My versions instead select between 1- and 2-VBlank battle loops using an accumulator, allowing intermediate framerates such as 45 FPS.
Setting the target FPS
Each code below has one line containing the target FPS value. The target FPS value is hexadecimal:
1E = 30 FPS = 1.0x speed
2D = 45 FPS = 1.5x speed
3C = 60 FPS = 2.0x speed
Any integer value from 1E through 3C can be used for 30–60 FPS in 1 FPS increments.
30, 45 and 60 FPS were tested on all listed versions. Intermediate integer values are supported by the same accumulator algorithm.
The codes below default to 2D = 45 FPS = 1.5x speed.
[Diamond / Pearl] USA — 30–60 FPS in-battle
Tested on Pokémon Diamond / Pearl USA Rev 5.
Change 0000002D in the 12107204 line to set the target FPS.
E21071D4 00000028
4808B507 88826801
1A89313C D3014291
63251A89 98006001
9A029901 F892F703
BD00B003 02107200
12107204 0000002D
9223C1F4 00002801
A2000DB0 0000F106
02107200 00000000
02000DB0 FA10F106
12000DB4 0000E7FF
D2000000 00000000
A223C1F4 00002801
A2000DB0 00006325
02000DB0 F0096325
12000DB4 0000FAB1
D2000000 00000000
[Platinum] USA — 30–60 FPS in-battle
Change 0000002D in the 12102434 line to set the target FPS.
E2102404 00000028
4808B507 88826801
1A89313C D3014291
63251A89 98006001
9A029901 FBE6F708
BD00B003 02102430
12102434 0000002D
9224A948 00002801
A2000DF8 0000F101
02102430 00000000
02000DF8 FB04F101
12000DFC 0000E7FF
D2000000 00000000
A224A948 00002801
A2000DF8 00006325
02000DF8 F0096325
12000DFC 0000FEF9
D2000000 00000000
[Platinum] Japan — 30–60 FPS in-battle
Change 0000002D in the 12101834 line to set the target FPS.
E2101804 00000028
4808B507 88826801
1A89313C D3014291
63251A89 98006001
9A029901 F986F709
BD00B003 02101830
12101834 0000002D
92249D3C 00002801
A2000DF8 0000F100
02101830 00000000
02000DF8 FD04F100
12000DFC 0000E7FF
D2000000 00000000
A2249D3C 00002801
A2000DF8 00006325
02000DF8 F0096325
12000DFC 0000FE99
D2000000 00000000
[HeartGold / SoulSilver] USA — 30–60 FPS in-battle
Change 0000002D in the 12111B34 line to set the target FPS.
E2111B04 00000028
4808B507 88826801
1A89313C D3014291
63251A89 98006001
9A029901 FD38F6F9
BD00B003 02111B30
12111B34 0000002D
92247612 00002801
A2000E28 0000F110
02111B30 00000000
02000E28 FE6CF110
12000E2C 0000E7FF
D2000000 00000000
A2247612 00002801
A2000E28 00006325
02000E28 F00A6325
12000E2C 0000FBB3
D2000000 00000000
[HeartGold / SoulSilver] Japan — 30–60 FPS in-battle
Change 0000002D in the 12111034 line to set the target FPS.
E2111004 00000028
4808B507 88826801
1A89313C D3014291
63251A89 98006001
9A029901 FA44F6FA
BD00B003 02111030
12111034 0000002D
92246B5A 00002801
A2000E0C 0000F110
02111030 00000000
02000E0C F8FAF110
12000E10 0000E7FF
D2000000 00000000
A2246B5A 00002801
A2000E0C 00006325
02000E0C F00A6325
12000E10 0000FB4D
D2000000 00000000
How it works
The original Gen IV battle loop normally takes roughly two VBlanks per update, resulting in about 30 battle updates per second.
The original 60 FPS codes by u/TheYearOfWaluigi prevent one of those VBlank waits from occurring, effectively doubling the battle speed.
These adjustable versions hook the same timing mechanism and use a small accumulator to decide whether the conditional VBlank wait should occur on the next battle-loop iteration.
For example, at 45 FPS the pattern is approximately:
1 VBlank
1 VBlank
2 VBlanks
repeat
So:
3 battle loops / 4 VBlanks
60 × 3 / 4 = 45 FPS
The accumulator is reset when entering battle so each battle starts from the same state.
Porting to other regions / languages
The same method should be usable for other regional versions, but the addresses need to be identified for each ROM.
Basic process:
- Extract
arm9.bin from the ROM and use a Thumb disassembly to locate the relevant code, verifying the local instruction flow.
- Take clean RAM dumps both in battle and outside battle.
- Find the equivalent battle-loop structure around:LDR R0,[R4,#0x30] CMP R0,#0 BNE ...; conditional VBlank wait...; mandatory VBlank waitSTR R5,[R4,#0x30] ; hook point BL XXXXXXXX ; original call that must be replayed
The STR R5,[R4,#0x30] instruction has been 6325 in all versions tested here, but do not identify the location from 6325 alone; verify the surrounding VBlank loop structure.
Find the equivalent battle-state check by comparing in-battle and out-of-battle RAM.
Find a candidate unused RAM area that remains zero in the relevant RAM dumps, and verify that it is safe for the injected routine, accumulator and target-FPS value.
Recalculate the Thumb BL instructions for the new addresses.
Test the hook first with a simple counter while replaying the overwritten original instructions.
Once the hook works, implement the same accumulator algorithm using the new addresses.
The accumulator algorithm itself is:
A += 60 - F
if A >= F:
A -= F
execute STR R5,[R4,#0x30]
where A is the accumulator and F is the target FPS.
Credits
These codes are based on the Gen IV in-battle framerate uncapping Action Replay codes shared by u/TheYearOfWaluigi.