Roll RNG
2025-03-28
Address FDBA61 contains a table of the numbers 0-FF, this is the RNG table (RNGTable). A pointer to the current RNG index is stored at 7E0026 (RNGIndex), this pointer is incremented every time a random number is generated during Battle.
This is the function that rolls the RNG:
RollRNG:
; Rolls a random value between two numbers.
; The upper limit is stored in the A register prior to calling
; The lower limit is stored in the X register prior to calling
C1AF22 SEP #$10 ; Set 8-bit index registers (X/Y)
C1AF24 STX $25 ; Store lower limit to $7E0025
C1AF26 REP #$10 ; Set 16-bit index registers (X/Y)
C1AF28 LDX $2C ; Load X register from $7E002C
C1AF2A PHX ; Push X register value to stack
C1AF2B SEP #$10 ; Set 8-bit index registers again
C1AF2D LDX $25 ; Load lower limit from $7E0025
C1AF2F CPX #$FF ; Is the lower limit FF?
C1AF31 BNE $C1AF35 ; Branch if no
C1AF33 BRA $C1AF73 ; Else branch to end of function
; This is checking for a special battle event which is determined
; based on $7E2989 having bit being set.
; If the bit is set then the lower limit is returned.
C1AF35 STA $B31E ; Store upper limit to $7EB31E
C1AF38 LDA $2989 ; Load A register from $7E2989
C1AF3B BIT #$20 ; Test bit 5
C1AF3D BEQ $C1AF42 ; Branch if bit 5 is clear
C1AF3F TXA ; Transfer X to A if bit 5 is set
C1AF40 BRA $C1AF73 ; Branch to end
; Check if the upper limit is 0, if so return 0
C1AF42 LDA $B31E ; Load upper limit to A from $7EB31E
C1AF45 CMP #$00 ; Compare with 0
C1AF47 BEQ $C1AF73 ; Branch to end if value is 0
; If the lower limit and upper limit are equal return the upper limit
C1AF49 CMP $25 ; Compare with lower limit
C1AF4B BEQ $C1AF73 ; Branch to end if they're equal
; Load the current RNG value
C1AF4D LDX $RNGIndex ; Load X with RNGIndex
; Check to see if upper_limit < lower_limit, if so return the RNG value
; directly
C1AF4F SEC ; Set carry flag
C1AF50 SBC $25 ; Subtract lower limit
C1AF52 CMP #$FF ; Compare result with $FF
C1AF54 BNE $C1AF5C ; Branch if not equal to $FF
C1AF56 LDA RNGTable,X ; Load value from RNG table at index X
C1AF5A BRA $C1AF73 ; Branch to end
; Store the upper limit and RNG value to $2A and $28 in preparation
; for division subfunction
C1AF5C STA $2A ; Store upper limit - lower_limit to $2A
C1AF5E STZ $2B ; Store zero to $2B
C1AF60 LDA RNGTable,X ; Load A from RNG table using X as index
C1AF64 TAX ; Transfer A to X
C1AF65 STX $28 ; Store X to $28
C1AF67 STZ $29 ; Store zero to $29
; This is a division subfunction, it divides the 2 bytes at $28
; with the two bytes at $2A and stores the remainder at $32 and result
; to $2A
C1AF69 JSR $C92A ; Division subfunction
; A = RNG value % upper limit
; This is because the RNG table is 0-FF but the upper limit can be
; upper than FF.
C1AF6C LDA $32 ; Load remainder from $32 (RNG value % lower limit)
C1AF6E CLC ; Clear carry flag
; Add the lower limit back, increment the RNG index, and restore context
C1AF6F ADC $25 ; Add $25 to A
C1AF71 INC $RNGIndex ; Increment RNGIndex
C1AF73 REP #$10 ; Set 16-bit index registers
C1AF75 PLX ; Pull X from stack
C1AF76 STX $2C ; Store X to $2C (restore context)
C1AF78 RTS ; Return from subroutine
A modern version of this function (ignoring the special event bit) would look something like:
rng_table = [...]
rng_position = 0
def rollRNG(lower_limit, upper_limit):
if lower_limit == 255:
return upper_limit
if upper_limit == 0:
return 0
if lower_limit == upper_limit:
return upper_limit
rng = rng_table[rng_position]
if upper_limit < lower_limit:
return rng
temp = upper_limit - lower_limit
rng_position += 1
rng_position %= 256
rng %= limit
rng += lower_limit
return rng
(forgive my python, I can make it do things but it’s not a language I work in professionally)