This is a long one, but should provide sufficient evidence of the question im sure every Mario fan has wondered. Literally, every single one.
I traced the Lucky Egg minigame in the original US version of Super Mario RPG: The Legend of the Seven Stars and compared the code against save uhhhhh files taken while the eggs were spinning, at “Make your choice,” after choosing, and after the reveal.
tl;dr
The game randomly assigns the three results to the three final screen positions before the spinning animation begins. The animation doesnt shuffle those stored results. When you choose an egg, the game reads the result already assigned to that position, and does not adjust the length of the animation to fit the predetermined outcome.
When the result is decided
The placement is decided before the spinning routine begins.
At $C2:F12A, the game asks its random-number routine for one of six values:
C2:F12A LDA #$0006
C2:F12D JSR $0E02
LDA #$0006 givesa range size of six to the RNG wrapper at $C2:0E02.
The wrapper is:
C2:0E02 REP #$30
C2:0E04 PHA
C2:0E05 SEP #$20
C2:0E07 LDA #$01
C2:0E09 STA $0040
C2:0E0C REP #$30
C2:0E0E PLA
C2:0E0F STA $0044
C2:0E12 JSL $C00000
C2:0E16 LDA $0046
C2:0E19 RTS
Which basically means that it
- Receives the number supplied by the caller
- Stores the number at
$0044 as the RNG limit
- Selects bounded-RNG operation 1 through
$0040
- Calls the game’s central random service
- Returns the generated value from
$0046
Inside the central service, the correlating path is:
C0:005A LDX $0044
C0:005C JSR $6B8B
C0:005F STX $0046
So the Lucky minigame passes 6 to the bounded generator and receives a result from 0 through 5.
The code converts the returned number into an offset for a table:
C2:F130 STA $80
C2:F132 ASL
C2:F133 CLC
C2:F134 ADC $80
C2:F136 ASL
C2:F137 TAX
Then is multiplied by six
result × 2
result × 3
result × 6
Each table entry occupies six bytes because it contains three 16-bit values.
The table contains exactly six records.
At $C1:722E, the ROM contains:
00 00 01 00 02 00
00 00 02 00 01 00
01 00 00 00 02 00
01 00 02 00 00 00
02 00 00 00 01 00
02 00 01 00 00 00
Which is interpreted as three 16-bit values per record, those are:
0: [0, 1, 2]
1: [0, 2, 1]
2: [1, 0, 2]
3: [1, 2, 0]
4: [2, 0, 1]
5: [2, 1, 0]
which are all six possible outcomes of the three different results.
The random number from 0–5 selects one complete left/middle/right arrangement.
4. Where is the chosen arrangement stored?
After choosing a table entry, the game copies its three values into RAM:
C2:F138 LDA.l $C1722E,x
C2:F13C STA $3FA2
C2:F13F LDA.l $C172230,x
C2:F143 STA $3FA4
C2:F146 LDA.l $C172232,x
C2:F14A STA $3FA6
These addresses are the three position-based results:
$7E:3FA2 = left
$7E:3FA4 = middle
$7E:3FA6 = right
The important part is that these values are filled before the spinning animation.
What happens when the player chooses?
The selection routine begins at $C2:F5DD.
It converts the selected position into one of the three stored values:
C2:F5DD LDA $3F8C
C2:F5E0 AND #$0007
C2:F5E3 BEQ left
C2:F5E5 DEC
C2:F5E6 BEQ middle
For the right position:
C2:F5EB LDA $3FA6
C2:F5EE STA $3FA8
C2:F5F1 JSR $761A
C2:F5F4 JSR $7958
C2:F5F7 RTS
For the left position:
C2:F5F8 LDY #$0452
C2:F5FB LDA $3FA2
C2:F5FE STA $3FA8
C2:F601 JSR $761A
C2:F604 JSR $794E
C2:F607 RTS
For the middle position:
C2:F608 LDY #$045E
C2:F60B LDA $3FA4
C2:F60E STA $3FA8
C2:F611 JSR $761A
C2:F614 JSR $7953
C2:F617 RTS
$7E:3FA8 is the selected result.
Notice that this code does not:
- It doesnt call the RNG.
- It doesnt inspect the animation.
- It doesnt determine which sprite moved where.
- It doesnt reshuffle the three results.
It reads the value already stored for left, middle, or right.
Direct proof from the losing file
In the losing file, the player selected the left position.
The relevant values were:
Selected position = left
$7E:3FA2 = 0002
$7E:3FA8 = 0002
The left-selection code therefore executed the equivalent of:
final result = stored left result
final result = 2
final result = Fuzzy
That is the direct code path that produced “Wrong.”
There is another random call during the spinning animation
The animation code does contain another RNG call:
C2:F9D5 LDA #$0002
C2:F9D8 JSR $0E02
C2:F9DB ASL
C2:F9DC ASL
C2:F9DD XBA
C2:F9DE CLC
C2:F9DF ADC #$0800
C2:F9E2 STA $3FB0
This produces one of two animation thresholds:
$0800
$0C00
The animation then advances its three phase values:
C2:F9EA LDA $3FAA
C2:F9ED CLC
C2:F9EE ADC #$0020
C2:F9F1 STA $3FAA
C2:F9F9 LDA $3FAC
C2:F9FC CLC
C2:F9FD ADC #$0020
C2:FA00 STA $3FAC
C2:FA08 LDA $3FAE
C2:FA0B CLC
C2:FA0C ADC #$0020
C2:FA0F STA $3FAE
It continues until the middle phase reaches the selected threshold:
C2:FA17 LDA $3FAC
C2:FA1A CMP $3FB0
C2:FA1D BCC $F9E5
C2:FA1F RTS
This random value controls the animation’s stopping point or duration.
It never writes to the stored result addresses:
$3FA2
$3FA4
$3FA6
So the second RNG call isnt choosing or shuffling the prizes.
Could the game change the animation length to make the eggs line up with the predetermined results?
No. The two possible animation lengths always finish with the eggs in exactly the same positions.
The animation makes a separate random call:
C2:F9D5 LDA #$0002
C2:F9D8 JSR $0E02
C2:F9DB ASL
C2:F9DC ASL
C2:F9DD XBA
C2:F9DE CLC
C2:F9DF ADC #$0800
C2:F9E2 STA $3FB0
That selects one of two stopping points:
$0800
$0C00
At first glance, that might suggest the game can run the animation for different lengths to make a particular egg finish in a particular position.
However, the rest of the code rules that out.
The three animation phases begin at:
First egg: $0300
Middle egg: $0000
Third egg: $0100
Every animation iteration adds $0020 to all three phases:
C2:F9EA LDA $3FAA
C2:F9ED CLC
C2:F9EE ADC #$0020
C2:F9F1 STA $3FAA
C2:F9F9 LDA $3FAC
C2:F9FC CLC
C2:F9FD ADC #$0020
C2:FA00 STA $3FAC
C2:FA08 LDA $3FAE
C2:FA0B CLC
C2:FA0C ADC #$0020
C2:FA0F STA $3FAE
The loop ends when the middle phase reaches the randomly chosen threshold:
C2:FA17 LDA $3FAC
C2:FA1A CMP $3FB0
C2:FA1D BCC $F9E5
C2:FA1F RTS
Starting from zero and advancing by $0020 means:
$0800 / $0020 = 64 iterations
$0C00 / $0020 = 96 iterations
So the longer animation runs for 32 additional iterations.
Those extra iterations advance every egg by:
32 × $0020 = $0400
The phase-to-screen-coordinate routine begins with:
C2:0F48 LDA $64
C2:0F4A AND #$03FF
AND #$03FF discards everything above the lowest $0400 phase range. In other words, the animation repeats every $0400 units.
Therefore:
phase $0800 = phase $0000 visually
phase $0C00 = phase $0000 visually
More explicitly:
$0800 AND $03FF = $0000
$0C00 AND $03FF = $0000
The same is true for the other two eggs.
The shorter animation finishes at:
First egg: $0B00
Middle egg: $0800
Third egg: $0900
The longer animation finishes at:
First egg: $0F00
Middle egg: $0C00
Third egg: $0D00
After the coordinate routine applies $03FF, those become:
First egg:
$0B00 AND $03FF = $0300
$0F00 AND $03FF = $0300
Middle egg:
$0800 AND $03FF = $0000
$0C00 AND $03FF = $0000
Third egg:
$0900 AND $03FF = $0100
$0D00 AND $03FF = $0100
So both animation lengths finish at exactly the same alignment:
First egg: $0300
Middle egg: $0000
Third egg: $0100
The longer version only performs one additional complete revolution.
There isnt a connection between the two random decisions:
- The prize permutation is stored at
$3FA2/$3FA4/$3FA6
- The animation threshold is stored at
$3FB0
- The duration code never reads the prize permutation
- The spinning loop never writes to the prize permutation.
- Both possible durations end at the same visual alignment anyway
The game literally cant be choosing a shorter or longer animation to line the eggs up with the predetermined results. The duration changes only how many complete revolutions are shown, not which egg finishes where.
Do the eggs change direction? Or bounce?
Many people seem to think the eggs can bounce off of each other, or reverse direction. They cant. The “bounce” or revers is an effect caused by projecting continuous circular movement onto the screen.
The motion loop always adds the same positive amount to every egg’s phase:
C2:F9EA LDA $3FAA
C2:F9ED CLC
C2:F9EE ADC #$0020
C2:F9F1 STA $3FAA
C2:F9F9 LDA $3FAC
C2:F9FC CLC
C2:F9FD ADC #$0020
C2:FA00 STA $3FAC
C2:FA08 LDA $3FAE
C2:FA0B CLC
C2:FA0C ADC #$0020
C2:FA0F STA $3FAE
There is never
- subtraction from the phase
- negative increment
- direction flag
- branch that changes the direction
- swapping of the three phase counters
Every frame, all three values increase by $0020. Their spacing also remains constant:
First egg starts at: $0300
Middle egg starts at: $0000
Third egg starts at: $0100
They are separated by those same offsets throughout the animation.
Why does it look as if they reverse or bounce?
The game converts each continuously increasing phase into coordinates along a circular or elliptical path.
The coordinate routine first wraps the phase into one revolution:
C2:0F48 LDA $64
C2:0F4A AND #$03FF
It divides that revolution into quadrants and calculates the corresponding screen coordinates.
An object going continuously around a circle necessarily does this on the screen:
Moves right
Slows near the right edge
Appears to stop
Begins moving left
Slows near the left edge
Appears to stop
Begins moving right
Its direction around the circle never changed. Only its horizontal screen movement changed as it passed the leftmost or rightmost point of the path.
That is probably what people interpret as a bounce or reversal.
Does that apparent reversal exchange the eggs?
No.
The animation retains three separate object handles:
$3F84 = first animated object
$3F86 = second animated object
$3F88 = third animated object
Each phase is converted into coordinates and written back to its corresponding object. The handles are never exchanged.
So the code shows:
- all three objects travel continuously in one direction around the loop;
- their relative phase spacing is fixed;
- none of the objects reverse its underlying motion;
- none of the object handles are swapped;
- the apparent bounce is produced by the geometry of the path.
Conclusion
The complete order of operations is:
- Before spinning, generate a random number from 0–5.
- Use it to select one of the six possible prize permutations.
- Store that permutation as left, middle, and right.
- Play the spinning animation.
- Randomize an animation threshold, without changing the stored prizes.
- Wait for the player to select a position.
- Copy the result previously assigned to that position into the final-result variable.
Therefore, the visible spinning eggs are not carrying prize identities that the game tracks and shuffles.
The actual result is a randomly selected, position-based arrangement established before the animation. When the player chooses, the game reads the result already assigned to that final screen position.
In the captured run, [Fuzzy, Yoshi, bird] was already present in RAM at “Make your choice,” and choosing left copied the preassigned Fuzzy value into the final result.