Fixing Metroid NES RNG by using FDS RNG

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

Post Reply
drk421
Posts: 329
Joined: Sun Nov 14, 2004 11:24 am
Contact:

Fixing Metroid NES RNG by using FDS RNG

Post by drk421 » Sun Sep 27, 2020 11:21 am

So I decided to see if I could fix the RNG (Random Number Generator) in Metroid NES. It's one of those things I noticed as a kid playing the game. In Norfair the seahorses would sometimes shoot fire, and othertimes would not. Also the hoppers in Ridley or Kraid's lair would either do long jumps or short jumps. Or when you fought Ridley sometimes his fireballs would be short or long. It wasn't until after playing the FDS version of Metroid that I noticed different enemy behaviors, and figured that's how the game was suppose to be played.
After doing some disassembling, I found the differences between the NES/FDS versions. It turns out that the FDS version of Metroid doesn't have it's own RNG, and that it uses the FDS BIOS RNG subroutine. I can only guess that Tohru Narihiro (the person who did the conversion from FDS to MMC1) didn't have access to the FDS RNG? There had to be some reason he didn't use it...

So I figured I'd port it over...

Here's the NES Metroid RNG: (Credit to SnowBro and Dirty McDingus for Metroid disassembly)

Code: Select all

;--------------------------------------------------------
; NES Metroid RNG
;--------------------------------------------------------

RandomNumber1 = $2E
RandomNumber2 = $2F

.org $C000

NESMetroidRNG:
	TXA						; transfer X to A
	PHA						; push A to stack
	LDX #$05				; load 5 into X register
_C004:
	LDA RandomNumber1		; load RandomNumber1 into A
	CLC						; clear carry flag
	ADC #$05				; add 5 to A
	STA RandomNumber1		; RandomNumber1 is increased by #$19 every frame
	LDA RandomNumber2		; RandomNumber2 is increased by #$5F every frame
	CLC						; clear carry flag
	ADC #$13				; add 13 to A
	STA RandomNumber2		; store A in RandomNumber2
	DEX						; decrement X by one
	BNE _C004				; branch to _C004 if result not zero
	PLA						; pull A from stack
	TAX						; transfer A to X
	LDA RandomNumber1		; load RandomNumber1 into A
	RTS

;Hex code: (26 bytes)
;8A 48 A2 05 A5 2E 18 69 05 85 2E A5 2F 18 69 13 85 2F CA D0 EF 68 AA A5 2E 60
The first instruction (TXA $8A), is used for writes for bank switching. When was debugging in the emulator I found writes to $C000 with 8A, so I left the instruciont TXA there.
I read the MMC1 page and it says something about no bus conflicts, but maybe this was not referring to MMC1 register writes at a ROM address? Someone please correct me if I'm wrong.

Anyway, the code about defintely doesn't produce enough "randomness" in it's results, and you can see patterns in the enemy behavior.

So here's the FDS RNG:

Code: Select all

RandomNumber1 = $2E
RandomNumber2 = $2F

.org $C000

;---------------------------------------------------------
; Start Metroid pass in parameters to FDS RNG subroutine
;---------------------------------------------------------
BeginRNGForMetroid:
	LDX #$2E					; X is the memory location where random numbers start
	LDY #$02					; Y is the count of how many random numbers there are from the start index
	
;---------------------------------------------------------
; FDS Random number generator
;---------------------------------------------------------

FDSRNG:
	LDA $00,X					; load previous random number 1 into A
	AND #$02					; AND A with 00000011
	STA $00						; store A in $00 (working memory)
	
	LDA $01,X					; load previous random number 2 into A
	AND #$02					; AND A with 00000011
	EOR $00						; XOR A with random number 1
	
	CLC							; clear carry flag
	BEQ _E9C1					; branch if equal (zero set)
	SEC							; set carry flag

_E9C1:
	ROR $00,X					; rotate right one bit random number 1, the carry is shifted into bit 7
								; and bit 0 is shifted into carry
	INX							; increment X
	DEY 						; decrement Y is the count of bytes for RNG
	BNE _E9C1					; branch if not equal (zero clear)
	RTS


;Resulting hex code, 27 bytes
;A2 2E A0 02 B5 00 29 02 85 00 B5 01 29 02 45 00 18 F0 01 38 76 00 E8 88 D0 FA 60

As you can see the resulting code is one byte too large to fit it in the same place for the existing "RNG". The code also doesn't have $8A at location $C000

So I had to make a couple changes, but it still outputs the same values:

Code: Select all

; vars
tmp = $00
RandomNumber1 = $2E
RandomNumber2 = $2F

.org $C000

;---------------------------------------------------------
; FDS Random number generator fit
;---------------------------------------------------------

Init:
	TXA
	TAX

FDSRNG:
	LDA RandomNumber1			; load previous random number 1 into A
	AND #$02					; AND A with 00000011
	STA tmp						; store A in tmp (working memory)
	
	LDA RandomNumber2			; load previous random number 2 into A
	AND #$02					; AND A with 00000011
	EOR tmp						; XOR A with tmp
	
	CLC							; clear carry flag
	BEQ _E9C1					; branch if equal (zero set)
	SEC							; set carry flag

_E9C1:
	ROR RandomNumber1			; rotate right one bit random number 1, the carry is shifted into bit 7
	ROR RandomNumber2			; and bit 0 is shifted into carry

	NOP
	LDA RandomNumber1
	RTS

; modified FDS RNG to fit in Metroid NES at $C000
; 8A AA A5 2E 29 02 85 00 A5 2F 29 02 45 00 18 F0 01 38 66 2E 66 2F EA A5 2E 60
The above code produces the same results as the FDS RNG (tested). I've also played through the whole game, and it plays just like the FDS RNG does.

User avatar
Quietust
Posts: 1623
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Re: Fixing Metroid NES RNG by using FDS RNG

Post by Quietust » Thu Oct 22, 2020 10:04 am

samlrs wrote:
Thu Oct 22, 2020 8:29 am
Hello drk421

I'm trying to recreate your experiment and having some issues
its seems like I'm missing something because using your method does not work in my case
I'm trying to fix my donkey kong copy
Why are you asking about Donkey Kong when this thread is about Metroid?
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.

NewRisingSun
Posts: 1227
Joined: Thu May 19, 2005 11:30 am

Re: Fixing Metroid NES RNG by using FDS RNG

Post by NewRisingSun » Fri Oct 23, 2020 2:45 am

IPS patch please :)

User avatar
Quietust
Posts: 1623
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Re: Fixing Metroid NES RNG by using FDS RNG

Post by Quietust » Fri Oct 23, 2020 9:46 am

NewRisingSun wrote:
Fri Oct 23, 2020 2:45 am
IPS patch please :)
Took me about 5 minutes to throw one together - see attached.
Attachments
metroid_rng.ips
(35 Bytes) Downloaded 7 times
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.

NewRisingSun
Posts: 1227
Joined: Thu May 19, 2005 11:30 am

Re: Fixing Metroid NES RNG by using FDS RNG

Post by NewRisingSun » Fri Oct 23, 2020 1:12 pm

Thank you for your five minutes, then! :beer:

Post Reply