TASVideos

Tool-assisted game movies
When human skills are just not enough

Submission #6499: pirohiko's SNES Final Fantasy V "game end glitch" in 07:28.13

Console: Super NES
Game name: Final Fantasy V
Game version: JPN
ROM filename: Final Fantasy V (J).snes
Branch: game end glitch
Emulator: lsnes-rr2 beta23
Movie length: 07:28.13
FrameCount: 26932
Re-record count: 25665
Author's real name: K.N
Author's nickname: pirohiko
Submitter: pirohiko
Submitted at: 2019-08-30 16:57:33
Text last edited at: 2019-08-30 16:57:33
Text last edited by: pirohiko
Download: Download (5446 bytes)
Status: new
Submission instructions
Discuss this submission (also rating / voting)
List all submissions by this submitter
List pages on this site that refer to this submission
View submission text history
Back to the submission list
Author's comments and explanations:

(Link to video)

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

Old Route

Please see the previous submission for things that are not explained

New Route

knbnitkr suggested a new route plan to me.

No-encounter flag

Karnak Castle exit turned out to turn off no-encounter flag $0A53.

Black Chocobo

Crescent Town turns on Black Chocobo flag $0A93 and event tiles flag $0A1B in the forest. The event black chocobo will crash the game if checked.

Detailed route


  • Saved to data 1 at the position of $0AD8 = {X=0x9E, Y=0x86}.
    • 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.
    • To compress the final checksum adjustment time, I did it 4 steps higher than the previous TAS.

  • Saved to data 3 and 2 at position of $0AD8 = {X=0x9E, Y=0x7B}.

  • 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=0x86}.

  • Loaded from data 1.
    • It walked 8 steps to the right and 2 steps down.

  • Resetting while saving to data 2, at position {X=0xB5, Y=0x8A}.
    • The checksum was adjusted by only waiting for 8 frames when saving the data without using the config screen.
    • By subframe reset, the position of data 2 has become {X=0xB5, Y=0x7B}.
    • Unlike the previous TAS, the checksum is correct.

  • Loaded from data 2.
    • It walked upwards.

  • Saved to data 2 at position {X=0xB6, Y=0x69}.
    • Waited 5 frames for the next checksum adjustment.
    • Normal reset.

  • Loaded from data 3.
    • It walked two steps to the right.

  • Resetting while saving to data 2, at position {X=0xA0, Y=0x7B}.
    • Changed his position to the second back for arbitrary code execution.
    • By subframe reset, the position of data 2 has become {X=0xA0, Y=0x69}.

  • Loaded from data 2.
    • 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.

  • Saved to data 2 and 4 at position {X=0x9F, Y=0x6D, ShipX=0xA0, ShipY=0x6D}
    • Turned upward for the next checksum adjustment.

  • Moved to west-northwest by ship.
    • Turned rightward for the next checksum adjustment.

  • Saved to data 3 and resetting while saving to data 2, the position of data 2 has become {X=0x8A, Y=0x62, ShipX=0x8A, ShipY=0x6D}.
    • Data 2 was apparently lost as the checksum was incorrect.

  • Loaded from data 4.
    • It does not move.

  • Resetting while saving to data 2, at position {X=0x9F, Y=0x6D}.
    • By subframe reset, the position of data 2 has become {X=0x9F, Y=0x6D, ShipX=0x8A, ShipY=0x6D}.
    • The checksum of data 2 was still inaccurate.

  • Loaded from data 3.
    • It does not move.

  • Resetting while saving to data 2, at position {X=0x8A, Y=0x62}.
    • The checksum was adjusted mainly by the setting of the custom pad of config.
    • By subframe reset, the position of data 2 has become {X=0x8A, Y=0x6D, ShipX=0x8A, ShipY=0x6D}, same as the ship.
    • This is the best position to go straight down or left without hitting the land.

  • Loaded from data 2.
    • The ship went to Crescent Town and sank as the event progressed.
    • Black Chocobo flag $0A93 and event tiles flag $0A1B were turned on as the event progressed.

  • Resetting while saving to data 1.
    • Overwritten from SRAM $0000 to $05EF (WRAM $0500-$AEF) for the next checksum adjustment.
    • Data 1 was apparently lost as the checksum was incorrect.

  • Loaded from data 2.
    • Went to Karnak Castle to turn off no-encounter flag $0A53.

  • Resetting while saving to data 1.
    • Overwiten from SRAM $0000 to $0591 (WRAM $0500-$A91).
    • The no-encount flag $0A53 and event tile flag $0A1B on data 1 are turned off.
    • As a result of previous adjustments, the final checksum was adjusted without waiting time.

  • Loaded from data 1.
    • Input for arbitrary code execution was made on the load screen.
    • Input for arbitrary code execution was made at the end of the battle.

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.

  26135|........A.......|
  26136|..sSudl.A.L.....|
address 00 01 02 03 04 05
$0100 20 3E A0 3E A0 3E

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.
  26640|....u...A.......|
address 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
$0000 80 08 80 08 80 08 FC 00 01

Checked Black Chocobo input

  26929|B.sSu..rA.L.....|
  26930|B.sSu.lrAXLR....|
address 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
$0100 20 3E A0 3E A0 3E F0 BB F0 BB 50 02 80 01 F0 BB
$0110 A0 B9 00 00

Last 3 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
  26930|B.sSu..rA.L.....|................|................|................|
  26931|B.sSu.lrAXLR....|.Y....l.AXL..1..|.Ys.u.lr.X.R.1..|.Ys..d.rAXL.0.23|
  26932|B....d.rA.LR.1.3|BY..u.lr.X.R01..|BY.S.d.rA....1.3|BY......A.L...2.|
Actual Joypad binary
A9 D6 C9 85 D7 CB
B5 85 85 D5 5C CB A2 C0

Executed codes

  -- NMI occured --
  00cee0 jml $001f00   <001f00> A:008d X:299c Y:11c8 S:1ff5 D:0b00 DB:00 nvmxdIzC V:225 H:  88
  001f00 brk #$00               A:008d X:299c Y:11c8 S:1ff5 D:0b00 DB:00 nvmxdIzC V:225 H: 120
  000000 bra $000a     <00000a> A:008d X:299c Y:11c8 S:1ff1 D:0b00 DB:00 nvmxdIzC V:225 H: 184
  00000a jsr ($0100,x) <002a9c> A:008d X:299c Y:11c8 S:1ff1 D:0b00 DB:00 nvmxdIzC V:225 H: 206
  000101 rol $3ea0,x   <00683c> A:008d X:299c Y:11c8 S:1fef D:0b00 DB:00 nvmxdIzC V:225 H: 264
  000104 ldy #$f03e             A:008d X:299c Y:11c8 S:1fef D:0b00 DB:00 nvmxdIzc V:225 H: 332
  000107 tyx                    A:008d X:299c Y:f03e S:1fef D:0b00 DB:00 NvmxdIzc V:225 H: 356
  000108 beq $00c5     <0000c5> A:008d X:f03e Y:f03e S:1fef D:0b00 DB:00 NvmxdIzc V:225 H: 370
  -- Start automatic polling --
  00010a bvc $010e     <00010e> A:008d X:f03e Y:f03e S:1fef D:0b00 DB:00 NvmxdIzc V:225 H: 386
  00010e beq $00cb     <0000cb> A:008d X:f03e Y:f03e S:1fef D:0b00 DB:00 NvmxdIzc V:225 H: 408
  000110 ldy #$00b9             A:008d X:f03e Y:f03e S:1fef D:0b00 DB:00 NvmxdIzc V:225 H: 424
  000113 brk #$f0               A:008d X:f03e Y:00b9 S:1fef D:0b00 DB:00 nvmxdIzc V:225 H: 448
  000000 bra $000a     <00000a> A:008d X:f03e Y:00b9 S:1feb D:0b00 DB:00 nvmxdIzc V:225 H: 512
  00000a jsr ($0100,x) <00f13e> A:008d X:f03e Y:00b9 S:1feb D:0b00 DB:00 nvmxdIzc V:225 H: 574
  004218 sbc ($77,x)   <00ab89> A:008d X:f03e Y:00b9 S:1fe9 D:0b00 DB:00 nvmxdIzc V:225 H: 636
  00421a lda #$c9d6             A:0cbc X:f03e Y:00b9 S:1fe9 D:0b00 DB:00 nvmxdIzc V:225 H: 686
  00421d sta $d7       <000bd7> A:c9d6 X:f03e Y:00b9 S:1fe9 D:0b00 DB:00 NvmxdIzc V:225 H: 704
  00421f wai                    A:c9d6 X:f03e Y:00b9 S:1fe9 D:0b00 DB:00 NvmxdIzc V:225 H: 732
  -- End automatic polling <85b5 d585 cb5c c0a2> --
  -- NMI occured --
  00cee0 jml $001f00   <001f00> A:c9d6 X:f03e Y:00b9 S:1fe5 D:0b00 DB:00 NvmxdIzc V:225 H:  80
  001f00 brk #$00               A:c9d6 X:f03e Y:00b9 S:1fe5 D:0b00 DB:00 NvmxdIzc V:225 H: 112
  -- Start automatic polling --
  000000 bra $000a     <00000a> A:c9d6 X:f03e Y:00b9 S:1fe1 D:0b00 DB:00 NvmxdIzc V:225 H: 176
  00000a jsr ($0100,x) <00f13e> A:c9d6 X:f03e Y:00b9 S:1fe1 D:0b00 DB:00 NvmxdIzc V:225 H: 198
  004218 lda $85,x     <00fbc3> A:c9d6 X:f03e Y:00b9 S:1fdf D:0b00 DB:00 NvmxdIzc V:225 H: 260
  00421a sta $d5       <000bd5> A:396b X:f03e Y:00b9 S:1fdf D:0b00 DB:00 nvmxdIzc V:225 H: 294
  00421c jml $c0a2cb   <c0a2cb> A:396b X:f03e Y:00b9 S:1fdf D:0b00 DB:00 nvmxdIzc V:225 H: 322

About the code

The event pointer $0BD6-$0BD8 was rewritten to 0xC9D639, and the event engine $C0A2CB was executed by JML(Jump to New Location Long).
The memory address to be corrected is less than the previous TAS.
Rewriting is much less than the previous TAS, because the event pointer is closer to "THE END".

Special Thanks

  knbnitkr, Yu-ki(hs)

Similar submissions (by title and categories where applicable):