Sweet, a memory corrupton glitch.
The "Autobomb" enemy is very buggy, in both PRG0 and PRG1 versions. I wonder why no one noticed it before. Every time it shoots a fireball, it corrupts some byte of zero page!
$AE36:A6 00 LDX $0000 = #$05
$AE38:A9 27 LDA #$27
$AE3A:20 04 90 JSR $9004
$AE3D:A6 00 LDX $0000 = #$97
$AE3F:D6 33 DEC $33,X @ $00CA = #$00
$AE41:D6 33 DEC $33,X @ $00CA = #$FF
The same code translated to Symbolic debugging:
$AE36: LDX Temp
$AE38: LDA HORIZONTAL_FIREBALL_ID
$AE3A: JSR ShootObject
$AE3D: LDX Temp
$AE3F: DEC Ypos[Temp]
$AE41: DEC Ypos[Temp]
The code is typical for many NES and SNES games. The "Temp" variable stores current value of "objects counter" (e.g. if there are 6 objects on screen then the loop should iterate through those values: 0, 1, 2, 3, 4, 5). The iterator (counter) is used to access memory arrays storing objects' properties (X/Y coordinates, direction, ID, timers, etc). Naturally, the value of the counter is supposed to always be within the range [0 - MAX_NUMBER_OF_ENEMIES). In SMB2U the max is 9.
But here's what went wrong. The "ShootObject" function uses "Temp" variable for its own purposes (to calculate Y coordinate of the new object, so that it appears right from the cannon nozzle). So after creating the fireball the counter value becomes corrupted, and any following RAM access (using this counter as a reference) will modify someone else's data!
In theory the value of counter can be anything from 0 to 0xFF, so those two instructions (DEC Ypos[Temp]) may alter any address in the zero page of RAM (decrease the value by 2).
But in practice some values are more likely than others, because of level design. The Autobomb's Y coordinate is usually the same (the enemy moves back and forth horizontally), for example when the enemy is on the high platform its Y = 0x70, thus the counter turns to 0x90, and the code will corrupt address $C3. If the enemy falls to the lower platform, its Y becomes 0x80, and the iterator turns to 0xA0, the code will corrupt address $D3. Same with $E3 and $F3. But all those addresses don't seem to hold any critical data, so this blatant bug was left unnoticed.
The real potential of the bug comes from the very small possibility that the Autobomb shoots in the middle of falling from a platform to platform.
This death glitch appeared because the Autobomb created a fireball while having Y = 0x77, so the code decreased $CA, which stores the higher byte of vertical scrolling position. Basicaly, it made the player teleport 2 screens down and thus instantly fall into a bottomless pit. That's why no death animation was played.
Here's Lua script that helps you monitor the glitch:
Language: lua
function ShowMessage()
addr = ((0x33 + memory.readbyteunsigned(0x00)) % 256);
textpos = (movie.framecount() % 7);
gui.text(1 + textpos, 20 + textpos, "Frame " .. movie.framecount() .. ": corrupting the address 0x" .. string.format("%02X", addr));
end
memory.registerexecute(0xAE3D, ShowMessage);