Submission #5899: pirohiko's SNES Final Fantasy V "game end glitch" in 10:42.29

Console Super NES Emulator lsnes-rr2 beta23
Game Version JPN Frame Count 38601
ROM Filename Final Fantasy V (J).snes Frame Rate 60.0988138974405
Branch game end glitch Rerecord Count 14132
Unknown Authors pirohiko
Game Final Fantasy V
Submitted by pirohiko on 4/1/2018 11:18:01 AM

Submission Comments

Game objectives

  • Emulator used: lsnes rr2-beta23
  • Heavy glitch abuse
  • Uses sub-frame resets
  • Aims for fastest time
  • Uses game-breaking glitches
  • Corrups save data
  • Abuses programming errors
  • Manipulates luck
  • Arbitrary Code Execution

Subframe reset

If resetting is done while WRAM is being transferred to SRAM, a mixed SRAM is created. When reset between the X / Y address, it warps horizontally from the SRAM position, but it needs to pass a checksum. It is also possible to mix the state of various event flags.

Checksum

The checksum of FF6 adds the game data to the 16-bit register every 8 bits, but this game adds it to the 16-bit register every 16 bits. In this game, it is relatively easy to adjust checksum separately for even and odd bytes in config. Even number bytes have frame advance time, so even number bytes move more intensely.

Helmet of memory destruction.

This movie abuses subframe reset. Some untouched objects will destroy memory when checked. Memory after $1417 will be destroyed if some untouched objects are checked. This game's V-blank interrupt has a jump address set to $1F00, which is dynamically rewritten when changing game scenes. If this address is destroyed, the program of this game jumps to $000000 by the BRK opcode.

Detailed route

  • Saved to data 1 at the position of $0AD8 = {X=0x9C, Y=0x8A}.
    • In the initial state it is more difficult to reduce than adding an even number of checksums, so I first increased it with config ($0970-).
    • By setting to store the cursor position, cursor movement of the menu screen is shortened.
  • Saved to data 3 and 2 at position of $0AD8 = {X=0x9E, Y=0x7B}.
    • It walked downwards and rightwards.
  • Resetting while saving to data 1, at position of $0AD8 = {X=0xAD, Y=0x90}.
    • I adjusted the checksum by reducing the increased configuration value.
    • It is important to speed up the message of the battle, but if you speed up the battle speed faster you will only accelerate the enemy.
    • By subframe reset, the position of data 1 has become {X=0xAD, Y=0x8A}.
  • Loaded from data 1.
    • It walked eight steps to the right.
  • Resetting while saving to data 2, at position {X=0xB5, Y=0x8A}.
    • Because walking is short, I can adjust the checksum by waiting for some frames, but I changed the character position rather than wait long.
    • By subframe reset, the position of data 2 has become {X=0xB5, Y=0x7B}.
    • Data 2 was apparently lost as the checksum was exceeded.
  • Loaded from data 3.
    • It does not move.
  • Resetting while saving to data 2, at position {X=0x9E, Y=0x7B}.
    • By losing data 2, the overwrite question was omitted.
    • The unjoined Lenna whose position was changed in Data 2 was overwritten by the Bartz joined flag.
    • By overwriting the SRAM $0700-$0708, the checksum is corrected.
  • Loaded from data 2.
    • I waited one frame on the loading screen to manipulate luck.
    • This waiting time is absorbed by later waiting time.
    • It walked upwards.
  • Saved to data 4 at position {X=0xB6, Y=0x69}.
    • Normal reset.
  • Loaded from data 3.
    • It walked two steps to the right.
  • Resetting while saving to data 4, at position {X=0xA0, Y=0x7B}.
    • The checksum was adjusted by only waiting for 2 frames when saving the data without using the config screen.
    • By subframe reset, the position of data 4 has become {X=0xA0, Y=0x69}.
  • Loaded from data 4.
    • In this position of the ocean there is an event related to the ship, so I got it!
    • The double speed flag by the chocobo is kept even on the ship, so it is quadrupled.
    • The memory address of the position where the ship landed is $0AEF = {X=0xA6, Y=0x4B}.
  • Saved to data 1 and 3, and resetting while saving to data 4, at position {X=0xA6, Y=0x4B}.
    • I overlapped the ship and saved it.
    • By subframe reset, the position of data 4 has become {X=0xA6, Y=0x69}.
    • This position is adjacent to the middle entrance of the pirates cave.
    • Data 4 was apparently lost as the checksum was incorrect.
  • Loaded from data 3.
    • Landed in front of the Wind Shrine.
  • Resetting while saving to data 3, at the ship landed position {X=0xB5, Y=0x3D}.
    • By subframe reset, the ship landed position of data 3 has become {X=0xB5, Y=0x4B}.
    • Data 3 was apparently lost as the checksum was incorrect.
  • Loaded from data 1.
    • It does not move.
  • Resetting while saving to data 3, at position {X=0xA6, Y=0x4B}.
    • By subframe reset, the position of data 3 has become {X=0xA6, Y=0x4B}.
    • The checksum of data 3 was still inaccurate.
  • Loaded from data 2.
    • It does not move.
  • Resetting while saving to data 3, at position {X=0xB5, Y=0x7B}.
    • The checksum was adjusted mainly by the setting of the custom pad of config.
    • By subframe reset, the position of data 3 has become {X=0xB5, Y=0x4B}, same as the ship.
  • Loaded from data 3.
    • I visited Carwen to pick up the Ice Rod.
    • Disturbance by the residents was avoided with the previous luck manipulation.
  • North Mountain
    • When they get off the ship, their appearance is changed, but the no-encounter flag by Chocobo is kept.
  • Magissa
    • Luck manipulation can be done easily by input before battle.
    • Because the attack magic can not be avoided, the duplicated man was sacrificed.
    • By pressing LR a few lag frames have been reduced.
    • Ice Rod is strong.
    • Then they returned straight to the save point.
  • Saved to data 3, and resetting while saving to data 4.
    • Data 4 was overwritten until no-encounter flag $0A53.
    • They include the Magissa flag $0A18, and helmet flag $0A67 is not included.
    • The checksum was adjusted using several configuration settings and window colors.
  • Loaded from data 4.
    • I waited 17 frames on the loading screen to manipulate luck.
    • To avoid encounters with enemies, 14 steps were added.
    • The double speed flag was erased by the event of a slow moving ship.
    • The no-encounter flag was erased by the event getting off Chocobo.
  • Resetting while saving to data 3.
    • Data 3 was overwritten until $0A67.
    • Because the rear row can escape faster, I changed it.
    • The checksum was adjusted using several configuration settings.
  • Loaded from data 3.
    • Let's set arbitrary code.

Arbitrary code setup

A direct page is 256 bytes that SNES CPU frequently accesses, and it has a memory address set according to the scene of the game different. In this game, the jump address of the BRK opcode is set to $000000. The direct page is set to $0000 in the battle scene, and pad input is put in $0000-$0005. But after loading the save data the memory will be filled with 0. Therefore to execute arbitrary code it was necessary to fight before checking the helmet. However, because it is disturbed by the "no-encounter flag ($0A53:0x80)", you need to see the event getting off Chocobo.

Closed menu input

When walking, the direct page will be set to $0B00, And when you open the menu screen it will be set to $0100. If you input the pad in two frames at the moment the menu screen closes, it will be put into $0100-$0105 and it will not change until you open it again.
37858|........A.......|
37859|..sSudl.A.L.....|
address000102030405
$0100203EA03EA03E

Battle end input

If you escape when the second character is in order, $000A will be "FC 00 01". Then $0000 was adjusted at the moment the battle screen closed.
38444|....u...A.......|
address000102030405060708090A0B0C0D0E0F
$0000800880088008 FC0001

Checked helmet input

38595|B.sSu..rA.L.....|
38596|B.sSu.lrAXLR....|
address000102030405060708090A0B0C0D0E0F
$0100203EA03EA03EF0BBF0BB50028001F0BB
$0110A0B90000

Last 7 frames input

When Joypad-Registers was read by unjust timing, bit position slipped, so I did the input by which that was considered.
             1-1              1-2              2-1              2-2
38595|B.sSu..rA.L.....|................|................|................|
38596|B.sSu.lrAXLR....|.YsS..lr.XLR.12.|.YsS.dlrAX.R.1..|.Ys..d.rA.L.0.23|
38597|B..S............|................|B...............|B...............|
38598|..s.udlr....012.|...S.d.r........|B..Sud.......1.3|BY..u.lrAX..0.23|
38599|.Y..u...A..R01..|....ud.r..LR0.2.|B..Sud.....R01.3|BY..u.lrAX..0.23|
38600|BY.Su..rA...012.|BY...d.r...R0.2.|BY.Sud.....R01.3|BY..u.lrAX..0.23|
38601|.....dl.A....1.3|....u.lr.X.R01..|....u...AX...12.|........A.L...2.|
Actual Joypad binary
A9EFEC0657CB
0E9F058C0005CBCB
9C281D9C3A1DCBCB
8E481D9C1A05CBCB
85D6C6D85CCBA2C0

Executed codes

00CEE0 5C 00 1F 00 JML $001F00  A:04fc X:1f52 Y:130e S:1ff5 D:0b00 DB:00 nvmxdIzC V:240 H:  98
001F00 00          BRK          
000000 80 08       BRA $000A    
00000A FC 00 01    JSR (0100,x) -- $2052 return 0101
000101 3E A0 3E    ROL 
000104 A0 3E F0    LDY #$F03E   -- Y = F03E
000107 BB          TYX          -- X = Y
000108 F0 BB       BEQ 
00010A 50 02       BVC $010E
00010E F0 BB       BEQ
000110 A0 B9       LDY #$00B9   -- Y = 00B9
000113 00          BRK
000000 80 08       BRA $000A
00000A FC 00 01    JSR (0100,x) -- $F13E return 4218 yey!
004218 C1 FE       CMP
00421A A9 EF EC    LDA #$ECEF   -- A = ECEF
00421C 06 57       INC $0B57    -- Event talk flag on
00421F CB          WAI
00CEE0 -> 001F00 -> 000000 -> 00000A -> 004218
004218 0E 9F 05    ASL $059F    -- GALUF ID 42 -> KLIRE ID 84
00421B 8C 00 05    STY $0500    -- BUTZ  ID 00 -> LENNA ID B9
00421E CB          WAI
004218 9C 28 1D    STZ $1D28    -- APU data fix $1D28 = 0000
00421B 9C 38 1D    STZ $1D3A    -- APU data fix $1D3A = 0000
00421E CB          WAI

004218 8E 48 1D    STX $1D49    -- APU data fix $1D49 = F03E
00421B 9C 1A 05    STZ $051A    -- LENNA Status = 0000
00421E CB          WAI

004218 85 D6       STA $0BD6    
00421A C6 D8       DEC $0BD8    -- Event Pointer = C8ECEF
00421C 5C CB A2 C0 JML $C0A2C0  -- Event Script engine

likelihood

Dead zombie and chemist glitch could also do Arbitrary Code Execution likewise, but I thought the second world was far.

Special Thanks

Yu-ki(hs), Sumurai Goroh, Yona2san

Masterjun: This looks interesting. Judging.
Masterjun: An important part to note for this run is the fact that it executes controller registers while they are being updated. This is known to not be emulated in a correct way on emulator. This gives this run a high chance of not working on console at all. However, leading the game code to the controller registers is done perfectly fine, so while these exact input sequences probably won't work on console, it's likely there are indeed some inputs which work. The run wouldn't look very different, so we can safely say that this isn't a problem.
Important to note is if there was a submission that is a bit longer, but with a more accurate way of beating the game in terms of emulator accuracy, then it can count as superior and obsolete this movie.
No other problems with this run and the viewer feedback has been quite good as well.
Accepting to Moons.
fsvgm777: Processing.

Last Edited by fsvgm777 on 4/28/2018 6:38 PM
Page History Latest diff