SMW No-ShellCode Credits Warp Technical Explanation
This is a technical explanation of how the arbitrary code execution works in the No-ShellCode Credits Warp.
SNES instruction set: https://wiki.superfamicom.org/65816-reference
SMW Ram map: http://www.smwcentral.net/?p=map&type=ram
Performing the item swap with the chargin' chuck causes Yoshi to eat the chuck, which is normally disallowed. As a result, an out-of-bounds function table lookup causes processor execution to jump to $014A13. This is in a region of the address space called open bus, which is not mapped to any device, so all reads will just return the contents of MDR (memory data register), which will contain whatever memory value was last written/read.
Since the data bank of the JMP to $014A13 was the last byte read, the MDR contains $01, so the instruction is ORA ($01,x). The x register is determined by the sprite slot of the chargin' chuck that's being eaten, which is 9. So the indirect lookup uses the address at $01+9=$0A. $0A was most recently set as the high byte of Yoshi's x-coordinate and $0B as the high byte of his y-coordinate. In this case, $0A=$0107. So the instruction will OR the "a" register with the contents of memory address $0107. This RAM address contains $17. OR a with $17. Now the MDR contains $17.
The next instruction is ORA [$17],y. Direct page addresses $17 and $18 are controller registers for axlr----, $17 is the current frame, and $18 is for first-frame presses. By holding down RLX, $17 is $70, and $18 is $00, because none of the presses are first-frame. $19 is $00 because Mario has no powerup. Because y contains $74, the instruction ORs a with the contents of $000070+74=$0000E4. $E4 is the low byte of the x-coordinate for sprite slot 0, which was the koopa that was knocked out of its shell near the beginning of the level. It was despawned at x-coordinate $01FC, so $E4 has a value of $FC, and the MDR is now $FC.
The next instruction looks like it should be JSR ($FCFC,x) (jump to subroutine). JSR interacts strangely with open bus:
* First, the instruction is read as $FC (JSR)
* The low byte of the destination is also read as $FC
* Then, the high byte of the return address is pushed to the stack ($4A)
* Then, the low byte of the return address is pushed to the stack ($19). The MDR is now $19.
* Finally, the high byte of the destination is read ($19)
So the instruction is actually JSR ($19FC,x). x is still 9, so we JSR to the address contained at $1A05. $1A05 is in a part of memory responsible for keeping track of collected coins and used ?-boxes. $1A05 is always $00 in YI2, because there aren't any item boxes or coins in that part of the level. The Yoshi box is tracked by the $02 bit of $1A06, it becomes 1 when you hit it. By duping that Yoshi block to the right, we also set the $01 bit of $1A06, so that $1A06 becomes $03. That means we JSR to $0300, which is the middle of the OAM table. The return address $4A19 is pushed onto the stack, and will need to be cleared later to avoid returning back to open bus.
The OAM table is responsible for drawing tiles to the screen. Different entries of the OAM table are used for certain types of tiles. $0300 is used for drawing Yoshi swallowing an item, and also for Mario when he turns around while carrying an item. Since we turned around while holding a shell while running left, $0300 is set by the x coordinate of the turn-around animation, which was last seen as $80. $0301 is set by the y coordinate of the tile, which is set to $F0 (off-screen) after the animation is done. The next instruction is $80 $F0, or BRA $F0, which branches backwards $10 bytes (after moving forward 2 for executing the instruction). We land at $02F2.
$02F0-$02FF are used for white "splat" tiles whenever you kill or hit an enemy. $02F2 is $7C (the splat tile identifier), which is JMP (addr,x). $02F3 is $20 (the splat tile attributes). $02F4 is the x-coordinate of one of the splat tiles, whenever it was last seen, which is when we kicked the shell on the left side of the screen, and set to $18. So the instruction is JMP ($1820,x). x is still 9, so it's JMP ($1829), we'll end up at the address that's contained by $1829.
$1829 is inside the minor extended sprite y-velocity table. Some minor extended sprites, such as coins don't use this table. But Yoshi egg fragments do. $1829 is the top right fragment, and $182A is the bottom left fragment. We let the bottom left fragment despawn naturally for a value of $42. The top right fragment gets despawned early by the precise screen scroll position at a y-velocity of $1A. The instruction ends up being JMP $421A.
$4218-$421F are the joypad auto-read registers. Each controller takes two bytes, the first byte being AXLR0000 and the second byte being BYETUDLR. $421A-B is port 2 controller 1, $421C-D is port 1 controller 2, and $421E-F is port 2 controller 2. These controllers have buttons held down for values $20 $48 $A0 $82 $80 $AA. These are interpreted as JSR $A048; BRL $ECA0.
JSR $A048 jumps into the middle of a subroutine in ROM. This subroutine adds $10 to Mario's x-coordinate low byte and stores the result into address $0F. In our case, since Mario's x-coordinate low byte was $DF, the value $EF gets put into address $0F. Then it returns back to the joypad auto-read registers at $421B.
BRL $ECA0 branches into the middle of another subroutine in ROM. In the Japanese ROM, this subroutine pulls two values off the stack, first pulling $19 then $4A, which were the return address from the JSR out of open bus earlier. The $4A value that gets pulled is put into the x register. Then the Y register is loaded from $0F (which contains $EF), and the accumulator gets loaded from $EC5B,y. Since Y is $EF, this loads the value at $ED4A, which is $20. Then the subroutine writes the accumulator to $B6,x. Since the x register contains $4A, this writes the value $20 to $0100, which is where Super Mario World stores the game mode. This puts the game partway into the credits.
With the open bus return address pulled off the stack, the subroutine returns control back to the powerup-eating code, and gameplay resumes, now in credits mode.