Spacetime Beam

From A complete guide to Super Metroid speedrunning
Revision as of 20:04, 12 March 2016 by Overfiendvip (talk) (Added one of my old pastebins)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

The Spacetime Beam is the result of an invalid beam selection that occurs when all beams except Wave Beam are selected. It can function without Charge Beam, but it is recommended that it also be equipped for easier utilization.

How It Works

The following is what is known so far about how it works on a technical level:

At 0090:AEDD, there is the following instruction:

$90/AEDD FC 68 0C    JSR ($0C68,x)[$90:AD16] A:912F X:0000 Y:0038 P:eNvmxdizc

This is an "absolute indexed indirect" jump that reads the address at $0C68 + X and jumps to it. $0C68 contains different values depending on what beam is shot, so this code calls different routines depending on what is fired. In the case of this beam, $0C68 contains AD16, so the code then jumps to 0090:AD16.

At that memory location, the following subroutine exists:

$90/ACFC 29 FF 0F    AND #$0FFF              
$90/ACFF 0A          ASL A                   
$90/AD00 A8          TAY                     
$90/AD01 A9 90 00    LDA #$0090              
$90/AD04 EB          XBA                     
$90/AD05 85 01       STA $01    [$00:0001]   
$90/AD07 B9 C9 C3    LDA $C3C9, y
$90/AD0A 85 00       STA $00    [$00:0000]   
$90/AD0C A0 00 00    LDY #$0000              
$90/AD0F A2 00 00    LDX #$0000              
$90/AD12 B7 00       LDA [$00], y
$90/AD14 9F C0 C1 7E STA $7EC1C0, x
$90/AD18 E8          INX                     
$90/AD19 E8          INX                     
$90/AD1A C8          INY                     
$90/AD1B C8          INY                     
$90/AD1C C0 20 00    CPY #$0020              
$90/AD1F 30 F1       BMI $F1    [$AD12]      
$90/AD21 60          RTS

When run as intended, this code will read 16 bytes starting at the address that gets written to $00 and $01 in RAM and then write it back to RAM at $7EC1C0. It does this by increasing the Y register twice for every time it loops and compares it to $20, and as long as Y - $20 is negative, it will continue the loop; however, with this beam, the jump into this instructions occurs right into the middle of the STA instruction at $90/AD14.

This effectively converts that operation into the following:

$90/AD16 C1 7E       CMP ($7E,x)

This effectively does nothing in this case, and the code moves on to INX at $90/AD18.

The one big thing to note here is that this completely bypasses the setup for this loop where registers X and Y get cleared to zero and where $00 and $01 are setup with the source address, and this is what causes the effects of the beam. In this case, X seems to be set to zero, so that means writes will start at $7EC1C0 as intended; however, Y retains the value from whatever it was set to by any previous code that used it.

As a result, when Y contains a huge negative value, the loop will run all the way up until Y gets back to being $20 or above. This causes an overflow to happen where a huge part of RAM after $7EC1C0 gets overwritten. The data that is written is read from ((the address at $00-$02) + Y), and the values in $00-$02 seems to at least be different depending on room, so that also changes which data gets copied along with the fact that the value of Y decides from where the copying will start. Since the value of Y is used from whatever code was run previously, this is what causes different data when shooting different beams, Missiles, Super Missiles, et cetera, since the code to update these beams will leave the Y register at different values.

This is pretty much all I know so far, and some of it might be incorrect. I'll try to update it as I learn more. If there are any questions, feel free to contact me on IRC.

- total

See also

External links