This TAS completes Super Mario Bros. 2 (USA) in less than half a minute by executing arbitrary code and jumping to the credits. This is performed via an exploit in the game's controller reading routine that allows you to stay in an infinite loop for as long as you want. This is the same exploit that lets
Super Mario Bros 3. be completed in a quarter of a second.
Humorously, this is also the first SMB2U TAS that doesn't need a flashing lights warning.
Game objectives
- Complete Super Mario Bros. 2 (U) by jumping to the credits with arbitrary code execution
- Executes arbitrary code
- Final boss skip glitch
- Heavy glitch abuse
- Major skip glitch
DPCM bug
On the North American release of the NES, if an audio sample is playing through the DPCM channel and the CPU is reading controller inputs at the same time, sometimes the controller inputs will be corrupted. This often results in an erroneous right press. Many games solve this problem by reading controller input multiple times until two sets of inputs match. This is absolutely fine. The most amount of times the controller will be read in casual play is maybe like 3 or 4 times which isn't a problem.
Fortunately, 31 years later, the release of the SubNESHawk core would allows this to be a problem. By manually alternating controller inputs on every controller poll, the CPU can be trapped in the controller reading subroutine. Combined with the Non-Maskable Interrupt, some pretty glitchy behavior can take place.
Jumping to RAM
For Super Mario Bros. 2, alternating controller inputs can either crash the game or not crash the game. But sometimes, if timings are just right, The PC can be jumped to address $FFF5. The PC makes its way across garbage data and CPU vectors until eventually making it to $00 in RAM. Once I found that out, I knew I had to look for a way to manipulate it somehow.
One very important detail is that SMB2U doesn't use IRQs at all. If a BRK ($00) is executed, the PC gets stuck in an infinite BRK loop, flooding the stack with garbage until interrupted by NMI. Unlike the SMB3 ACE TAS which can pull through all of RAM and make its way to the controller inputs, we have to be careful that the PC doesn't run into $00.
8374: 00 BRK
V
FFF0: DF E6 00 DPC $00E6,X
FFF3: 00 BRK
FFF0: DF E6 00 DPC $00E6,X
FFF3: 00 BRK
FFF0: DF E6 00 DPC $00E6,X
FFF3: 00 BRK
You get the idea
The Process
I started by looking for a way to jump to controller input on power-on. Controller inputs are located at $F5-$F8. The problem is that you can't branch from $00 to $F5 because that's too far for a branch instruction. To get to controller inputs, either two separate branches or a JMP/JSR had to happen. $00 and the couple bytes following it are difficult to manipulate from power-on, nor does the title screen modify them in any useful way (as far as I found).
Once I gave up on that, I decided to see if I could manipulate $00-$03 anywhere else. Since those are temporary variables, they always change randomly with little to no pattern, making them hard to manipulate. Only during gameplay did I find a pattern with a few variables that could help an ACE setup.
- address $00 is (most of the time) value $50 (a BVC instruction)
- address $01 is mostly consistent depending on how far you are into the level
- address $07 can be manipulated into any value, but only during the pause menu (incremented every time "PAUSE" blinks)
- address $08 is (most of the time) tied to the player's Y position
My idea was to manipulate RAM into writing BVC $0007 and BCS $002B at $00 and $07 respectively. That would branch into the object X position table, which has many contiguous bytes that can be arbitrarily manipulated.
The credits can be played by jumping to $E95C. This routine sets up the final room where you free the Subcons from the pipe and then play the ending sequence. Since this address is in the last PRG bank, no bank swapping had to be worried about.
Attempt 1
I made a TAS that does successfully jump to $E95C. Actually, since E7 was easier to write into the X position table than E9, I jumped to $E7B1 which then jumps to $E95C. The issue is that while the ending sequence does play, it stops at the end where Mario is supposed to wake up from his dream. He just doesn't breathe or wake up and it's really scary. Also, half the TAS was spent in the pause menu waiting for address $07 to go from $63 to $B0 (BCS) which was BORING.
That fact on top of me not wanting Mario to die in his sleep, I looked for another way to pull it off. I suspected the ending didn't play out because the game was paused, so I looked for another way to manipulate address $07.
While very finicky and random, address $07 CAN be manipulated. I realized this could be done by carrying the tweeter across the waterfall and then throwing it. I think something about the tweeter's y position or velocity could set address $07 to #$B0. My guess is that it's the last object loaded in before the end of the waterfall (other than the falling platform), so it sets up the variables there last, before controllers are read, but I have no idea.
OnehundredthCoin found out that $07 is left by sprites as the Y-position at the top of their hitbox. The tweeter is loaded into sprite slot 0, which is evaluated last, so its hitbox Y-position is left there by the time controllers are read.
The Run
The current run is performed as follows:
- Play as Toad because he runs the fastest
- Carry the first shy guy up until the last tweeter at the water fall (speed boost)
- Ditch the shy guy to save the tweeter
- Go to the part of the level with the three pink shy guys and the pow block ($01 = #$05)
- Save the tweeter to manipulate $07 later
- Unload the middle shy guy at X=$E7 and the top shy guy at X=$4C
- Wait for the bottom shy guy to approach X=$B1
- Throw the tweeter ($07 = #$B0)
- Change toad's Y position by jumping ($08 = #$22)
- Mash A button until the PC jumps to $FFF5
The trace log looks like this:
NMI stuff
...
EC6D: 40 RTI
V
FFF5: 04 01 NOP $01
FFF7: 04 01 NOP $01
FFF9: BE 94 EB LDX $EB94,Y
FFFC: 50 FF BVC $FFFD
FFFD: FF FF FF ISC $FFFF,X
0000: 50 05 BVC $0007
0007: B0 22 BCS $002B
002B: 4C B1 E7 JMP $E7B1
V
E7B1: 4C 5C E9 JMP $E95C
V
E95C: Credits sequence stuff
...
Now that i'm thinking about it, I'm not exactly sure why the RTI goes to $FFF5. But it can I guess. And the credits play out properly and the game wasn't paused and Mario gets to sleep peacefully.
Console verification?
When my homemade TAS replay device plays it, the input alternating doesn't jump the PC to RAM. Not sure why. I even have an official SMB2U Rev A cartridge. But I'd be really happy if someone else could get it to work.
Can this be improved?
I'm very sure I missed something. Maybe you can jump to the credits straight from address $07 and I missed it. Maybe you CAN achieve total control from power-on and I missed it. But I'm happy that one more Mario game has a credits warp (even if it isn't REALLY a Mario game and does require sub frame inputs.)
One thing worth mentioning that I didn't know about when submitting this run is that
Maru discovered an SMB2U credits warp 7 years earlier, and that I was not the first person to pull it off, which does deserve some credit. My run improves this time by 8.9 seconds.
Suggested Screenshots