Aim
Beat the game as quickly as possible by exploiting a glitch in World 8-4 that allows controller inputs to be read as arbitrary code.
Background
This bug was first discovered by LuigiSidekick during a casual playthrough of SMB2, where they accidentally crashed the game in 8-4. The crash occurs due to a logic error in the DuplicateEnemyObj subroutine, which is used by long firebars and Bowser. If all enemy slots are occupied, the object falls out of bounds into memory beyond the object slot flags ($0F to $14 in RAM), storing values at the slot corresponding to the first $00 byte found. The first occurrence results in the object flag being stored at address $15, which is an unused memory location.
However, if this happens a second time, the first slot to contain a green Koopa Troopa (a requirement as they have the ID $00) in the object slot list is overwritten by a glitched object. The enemy ID which has replaced the first green Koopa Troopa will jump to a memory address corresponding to the slot available to the first half of the long firebar or Bowser and execute its contents as instructions.
Method
The game-end glitch method used in this TAS was discovered by Threecreepio and involves loading enemy ID $84 into any object slot other than slot 0. The code that processes enemy ID $84 jumps to $0747 (TimerControl) in RAM, which can be manipulated by taking damage. Initially, it executes a BRK opcode ($00), triggering SMB2’s IRQ handler.
Since this functions as a 2-byte opcode, the program counter moves to $0749 once completed, which is also a BRK opcode. The values at $074A and $074B are determined by the controller inputs of Players 1 and 2, allowing full control over two bytes.
Until damage is taken to manipulate TimerControl, the game must be prevented from crashing by holding B + Select on Controller 2 to create an RTS instruction ($60). Once damage is taken, TimerControl changes to $FE, a 3-byte opcode that moves the program counter to $074A upon execution.
At this point, the payload can be executed. The inputs from Controllers 1 and 2 result in the instruction JMP ($008D), an indirect jump to the address stored in locations $8D and $8E. Addresses $8D and $8E store the X-position of fireballs shot by Mario, which can be freely manipulated. In this case, the stored values are $81 and $AA, forming the address $AA81, which is part of the HandleAxeMetatile subroutine. This subroutine sets OperMode ($0770) to $02, signaling the game to prepare the ending sequence.
Alternate Methods
It is theoretically possible to gain total control using a stop 'n' swap approach. A significant portion of SMB2's stack ($0160-$01E4) is not cleared by the Disk System BIOS, and SMB2 explicitly skips clearing this region when running the InitializeMemory subroutine. This method also relies on an indirect jump but uses only Controller 2 to jump to the address stored in $00 and $01, requiring camera-scroll manipulation. A smaller-scale version of this method, which does not require swapping cartridges, leverages the X-position of the FloateyNumber variables. This was discovered by Simplistic6502 and OnehundredthCoin.
Another alternative method, developed by SBDWolf and threecreepio, involves setting the coin count to 96 ($60) to create an RTS instruction and execute controller inputs as code across multiple frames. This approach requires having a life count other than 3, as this prevents the game from encountering a STP opcode and freezing when executing NumberOfLives. Threecreepio has created a demonstration video showcasing this method.
Are Other SMB1-Engine Games Vulnerable to This Exploit?
Yes. All Night Nippon Super Mario Bros. shares a nearly identical 8-4 level layout with SMB2, allowing this exploit to be used there as well. However, the enemy ID which must be loaded into memory to transfer execution to RAM is enemy ID $83. While SMB1 and Vs. SMB have the same logic error in DuplicateEnemyObj, no level layouts exist that allow a glitched enemy ID to spawn. Additionally, even if a glitched enemy could be spawned in SMB1, the game would likely crash on the cartridge release due to the BRK opcode causing an infinite loop.