Here, I'll try and go through the steps of how I'd look into this using FCEUX. You mentioned that the memory address $0657 holds some randomly generated value for "shoes". Let's look at that...
Step 1: Go to Tools > RAM Watch and add an entry for 0657 so wee can look at it as the game is running.
Step 2: Start the game and play it until it changes. Here I notice it happens when leaving the "PRESS START" screen.
Step 3: Go back to before it changed (by resetting or with a savestate, etc.) and try to get close to the point where it is about to change and pause the emulator.
Step 4: Go to Debug > Trace Logger, select Log to File and click browse to create a file. Start Logging.
Step 5: Go to Debug > Debugger and add a breakpoint on writes to 0657, this is going to automatically pause when it gets written to.
Step 6: Click Run in the debugger, or otherwise unpause. Execution will proceed slowly (the trace is writing everything that's happening to a file), and it will eventually automatically pause when $0657 is written to. Once it pauses click "Step Into" once just to finish the instruction that actually writes $0657.
Step 7: Stop logging in the trace. Open the file you created and go to the bottom. Here's the last two instructions:
Code:
]A:00 X:28 Y:00 S:F8 P:nVUbdIZC $9928: A5 06 LDA $0006 = #$29
Breakpoint 0 Hit at $992A: $0657:EC-W--
A:29 X:28 Y:00 S:F8 P:nVUbdIzC $992A: 9D 2F 06 STA $062F,X @ $0657 = #$00
So we see here that a value $29 is loaded from $0006 and written to $0657. We want to find out where that value came from...
Step 8: Use a text search tool to search backwards for "0006" from that line until you find a write to $0006.
This ends up taking a little while because there's a long repetitive series of this loop:
Code:
A:00 X:28 Y:88 S:F8 P:NVUbdIzC $9916: B9 2E 06 LDA $062E,Y @ $06B6 = #$00
A:00 X:28 Y:88 S:F8 P:nVUbdIZC $9919: 30 07 BMI $9922
A:00 X:28 Y:88 S:F8 P:nVUbdIZC $991B: B9 2F 06 LDA $062F,Y @ $06B7 = #$00
A:00 X:28 Y:88 S:F8 P:nVUbdIZC $991E: C5 06 CMP $0006 = #$29
A:00 X:28 Y:88 S:F8 P:NVUbdIzc $9920: F0 BF BEQ $98E1
A:00 X:28 Y:88 S:F8 P:NVUbdIzc $9922: C8 INY
A:00 X:28 Y:89 S:F8 P:NVUbdIzc $9923: C8 INY
A:00 X:28 Y:8A S:F8 P:NVUbdIzc $9924: C4 50 CPY $0050 = #$00
A:00 X:28 Y:8A S:F8 P:NVUbdIzC $9926: D0 EE BNE $9916
It seems to look at a list of previously generated values and compare against it. You said this number determines the location of the shoes, right? Well, this is probably trying to make sure it doesn't put 2 objects in the same location.
Step 9: Here we see that $0006 comes from a series of 2 table lookups (i guess some kind of "location" table), but those lookups are determined by a combination of $0012 and $0005. Next to find out how those two values were filled... keep searching backwards!
Code:
A:54 X:28 Y:00 S:F8 P:nVUbdIZc $9900: A5 12 LDA $0012 = #$45
A:45 X:28 Y:00 S:F8 P:nVUbdIzc $9902: 29 03 AND #$03
A:01 X:28 Y:00 S:F8 P:nVUbdIzc $9904: 05 05 ORA $0005 = #$54
A:55 X:28 Y:00 S:F8 P:nVUbdIzc $9906: A8 TAY
A:55 X:28 Y:55 S:F8 P:nVUbdIzc $9907: B9 AB E7 LDA $E7AB,Y @ $E800 = #$33
A:33 X:28 Y:55 S:F8 P:nVUbdIzc $990A: A8 TAY
A:33 X:28 Y:33 S:F8 P:nVUbdIzc $990B: B9 23 E8 LDA $E823,Y @ $E856 = #$29
A:29 X:28 Y:33 S:F8 P:nVUbdIzc $990E: C5 FA CMP $00FA = #$FF
A:29 X:28 Y:33 S:F8 P:nVUbdIzc $9910: F0 CF BEQ $98E1
A:29 X:28 Y:33 S:F8 P:nVUbdIzc $9912: 85 06 STA $0006 = #$15
Step 10: A little futher back we see that $0005 is actually just the value from $0004 but times 4. So now we have to keep searching for $0004 instead. In the same place we also find where $0012 came from, and this looks like our main PRNG step, maybe?
Code:
A:80 X:28 Y:2A S:F8 P:NVUbdIzc $98E6: A5 12 LDA $0012 = #$A1
A:A1 X:28 Y:2A S:F8 P:NVUbdIzc $98E8: 45 13 EOR $0013 = #$87
A:26 X:28 Y:2A S:F8 P:nVUbdIzc $98EA: 65 14 ADC $0014 = #$1F
A:45 X:28 Y:2A S:F8 P:nvUbdIzc $98EC: 85 12 STA $0012 = #$A1
A:45 X:28 Y:2A S:F8 P:nvUbdIzc $98EE: 45 13 EOR $0013 = #$87
A:C2 X:28 Y:2A S:F8 P:NvUbdIzc $98F0: 85 13 STA $0013 = #$87
A:C2 X:28 Y:2A S:F8 P:NvUbdIzc $98F2: E5 02 SBC $0002 = #$68
A:59 X:28 Y:2A S:F8 P:nVUbdIzC $98F4: 85 14 STA $0014 = #$1F
A:59 X:28 Y:2A S:F8 P:nVUbdIzC $98F6: A5 04 LDA $0004 = #$15
A:15 X:28 Y:2A S:F8 P:nVUbdIzC $98F8: 0A ASL
A:2A X:28 Y:2A S:F8 P:nVUbdIzc $98F9: 0A ASL
A:54 X:28 Y:2A S:F8 P:nVUbdIzc $98FA: 85 05 STA $0005 = #$50
$0012-0014 seem to be part of a 24-bit PRNG seed, though now there's also a value from $0002 that seems to be involved. If I keep searching back from here, I just see iterations of this same step for $0012-0014. My trace log doesn't seem to go far enough back to figure out how they were initially set up, but that's fine, we have identified that they were important for setting up $0657 and we can begin a new investigation from here.
Similarly the determination for $0002 is not immediately clear from the trace log I have saved, but it's something we can now dig into.
Step 11: Add $0002, $0012-0014 to the ram watch again. I notice that they're changing all the time. $0002 in particular is increasing by 1 each frame. Putting a breakpoint on writes to it, I notice that it's incremented by 1 each frame, during the NMI routine. So... this appears a frame counter.
So...
Observation 1: the random setup depends on how many frames have elapsed since power-on before you start the game.Step 12: Putting a breakpoint on writes to $0012, I find this routine that runs in a loop many many times each frame during startup:
Code:
00:8539: A2 03 LDX #$03
00:853B: B5 12 LDA $12,X @ $0012 = #$B9
00:853D: F5 11 SBC $11,X @ $0011 = #$80
00:853F: 75 16 ADC $16,X @ $0016 = #$00
00:8541: ED 02 04 SBC $0402 = #$00
00:8544: 4D 23 04 EOR $0423 = #$00
00:8547: 45 02 EOR $0002 = #$08
00:8549: FD F7 03 SBC $03F7,X @ $03F7 = #$00
00:854C: 5D 23 04 EOR $0423,X @ $0423 = #$00
>00:854F: 95 12 STA $12,X @ $0012 = #$B9
00:8551: CA DEX
00:8552: 10 E7 BPL $853B
00:8554: 60 RTS
This just runs being called over and over in an infinite loop until an NMI interrupts it and eventually manipulates the stack to get it out of that infinite loop. This is setting up the 24-bit PRNG seed. It maybe runs 40 times per frame.
Observation 2: this random seed loop is broken only by the NMI, so this will require precise PPU emulation to get correct. If it's even one cycle different, the PRNG seed could be changed.Step 13: That seed generating loop from before also depends on other values from other places, in particular $0016-0019 are where controller inputs are stored (16 = gamepad 0, 17 = gamepad 0 new presses this frame, 18-19 = gamepad 1 similar).
Observation 3: the random seed is influenced by controller input before the start.$0011 also seems to fold into it but it appears to be a constant $80 after startup, so I won't bother looking into it.
SUMMARYSo... I mean we could keep going here, but here's what the situation looks like to me:
- For a consistent PRNG setup, you have to press START on the very first possible frame that you can.
- Input on both gamepads before that start does influence the PRNG seed.
- Power-on RAM values so far do not appear to have any influence.
- Even one cycle difference in the specific timing of the NMI can also alter the seed.
That last one in particular is one that emulators might easily behave differently on, especially reset/warmup behaviour of the PPU. It's also possible that on hardware the variability of
PPU alignment might mean you only have a 1 in 4 chance of getting a consistent alignment on reset/power-up too, even if you could get that frame-perfect START every time.
Does this agree/disagree with your experience with the game?