RNG

RndVal1_A = $0037
RndVal1_B = $0038
RndVal1_C = $0039
RndVal2 = $0302
RndCount1 = $0027
RndCount2 = $00F7
Execution of RndFunc1: The current 24-bit[1] random seed RndVal1 is left-shifted by 1. The new entering bit is 0 if the 3rd and 4th bits from the left are the same, and 1 otherwise.
This is an event of what is better known as a linear feedback shift register.
Execution of RndFunc2: Execute RndFunc1 5 times and add 35 to RndVal2 (mod 256).
Randomization occurs every frame (except those which are lagging or playing sound effects). Which randomization occurs on a frame depends on which stage the game is at.
Note: All divisions and moduli operate on unsigned 8-bit integers.
Before the game:
    1. Execute RndFunc1 once.
    2. Reset RndCount2.
    3. Ready the Chance deck array.
    4. Execute RndFunc1 three times.
    5. Take last 8 bits of RndVal1, XOR with RndCount2, integer divide by 16, and use this value as an offset to the deck array.
    6. Check deck array. If already occupied, increment RndCount2. Otherwise occupy offset, and try next card.
    7. Go back to step 4 unless the deck is full (no more cards). In that case go back to step 3, set RndCount2 to 16, and repeat with the Community Chest deck array. When both decks are done, stop.
During the game:
    1. Execute RndFunc1 once, then RndFunc2 once, then RndFunc1 five times.
    2. Take last 8 bits of RndVal1, XOR with RndVal2, mod 6, and increment for Die 2.
    3. Execute RndFunc1 five times.
    4. Take last 8 bits of RndVal1, mod 6, and increment for Die 1.
Everything marked (Acmlm) was found by Acmlm. Everything else was found by FractalFusion.
Some additional comments were added to the code.
  • Random functions:
RndVal1_A = $0037
RndVal1_B = $0038
RndVal1_C = $0039
RndVal2   = $0302

;(Acmlm)
;****************************************
;* Shift RndVal1 left by 1 bit          *
;* New bit entering RndVal1_C = ?       *
;* Load random from RndVal1_C           *
;****************************************

$C13E                        ;          RndVal1          -    A     - C
RndFunc1: LDA RndVal1_A      ;01111000 11111111 00110110 - 01111000 - 1
          ASL                ;                           - 11110000 - 0
          EOR RndVal1_A      ;                           - 10001000 - 0
          ASL                ;                           - 00010000 - 1
          ASL                ;                           - 00100000 - 0
          ASL                ;                           - 01000000 - 0 <-- entering bit
          ROL RndVal1_C      ;01111000 11111111 01101100 - 01000000 - 0
          ROL RndVal1_B      ;01111000 11111110 01101100 - 01000000 - 1
          ROL RndVal1_A      ;11110001 11111110 01101100 - 01000000 - 0
          LDA RndVal1_C      ;                           - 01101100 - 0
          RTS


;****************************************
;* Increment RndVal2 by 35 ($23)        *
;* Run RndFunc1 5 times                 *
;****************************************

$C56D
RndFunc2: LDX #$05

loop:     LDA RndVal2
          CLC
          ADC #$07
          STA RndVal2
          JSR RndFunc1
          DEX
          BNE loop

          RTS
  • Random analysis:
(Acmlm)
37 38 39                            0302
-
70 19 EE = 011100000001100111101110 ¦ 00  Before starting (random 1)
E0 33 DC = 111000000011001111011100 ¦ 00  $0037-0039 << 1
C0 67 B9 = 110000000110011110111001 ¦ 00  $0302      + 00
80 CF 72 = 100000001100111101110010 ¦ 00
01 9E E4 = 000000011001111011100100 ¦ 00
03 3D C8 = 000000110011110111001000 ¦ 00
06 7B 90 = 000001100111101110010000 ¦ 00
0C F7 20 = 000011001111011100100000 ¦ 00
19 EE 40 = 000110011110111001000000 ¦ 00
33 DC 81 = 001100111101110010000001 ¦ 00
67 B9 02 = 011001111011100100000010 ¦ 00

80 CF 72 = 100000001100111101110010 ¦ 00  Before rolling (random 2)
CF 72 05 = 110011110111001000000101 ¦ 23  $0037-0039 << 8, $0027 is reset when this activates
72 05 46 = 111000100000010101000110 ¦ 46  $0302      +$23
05 46 58 = 000001010100011001011000 ¦ 69

3F 2B A1 = 001111110010101110100001 ¦ D2  While rolling (random 3)
84 17 CE = 100001000001011111001110 ¦ 3B  $0037-0039 <<18,16,16,16,18,16,16,16,... depending on $0027
CE 30 E1 = 110011100011000011100001 ¦ A4  $0302      +$69
E1 49 44 = 111000010100100101000100 ¦ 0D
44 8F 6F = 010001001000111101101111 ¦ 76
BC D9 1B = 101111001101100100011011 ¦ DF

                                          After rolling (random 4)
                                          $0037-0039 << 6, 16 when getting dice values
                                          $0302      +$23
  • before game:
;frame 7: 3E 2A D2

;RAM(27)++ starts 3 frames after first press of A and occurs on every frame

; *****
; executes first
; *****

...
$C053 JSR RndFunc1 ; executes always on every frame
$C056 JSR $C2DE
...

; *****
; executes when entering name
; starts on accept input
; stops one frame after OK
; *****

;RAM($27)++ (somewhere)

...
$94D6 LDA $27
$94D8 AND #$1F ;mod 32
$94DA TAX
Iterate:   ; iterates RAM($27)%32+1 times.
$94DB JSR RndFunc1
$94DE DEX
$94DF BPL Iterate
...

; *****
; set up cards
; *****

...
$84A2 JSR Chance ; if jump, chance first, otherwise comm chest
$84A5 LDX #$10

Chance:
$84A7 STX $F6 ; 0 if chance, 16 if comm chest
$84A9 STX $F7
$84AB LDY #$F
$84AD LDA #$FF

Init:          ;sets chance/comm chest entries to $FF
$84AF STA $472,X
$84B2 INX
$84B3 DEY
$84B4 BPL Init
$84B6 LDY #$F

TryInsertCard:
$84B8 JSR RndFunc1
$84BB JSR RndFunc1
$84BE JSR RndFunc1
$84C1 EOR $F7
$84C3 LSR    ;
$84C4 LSR    ;
$84C5 LSR    ;
$84C6 LSR    ; idiv by 16, reduce to <16
$84C7 ORA $F6    ; +0 if chance, +16 if comm chest
$84C9 TAX   ;deck offset
$84CA LDA $472,X
$84CD BMI InsertCard ;vacant spot
$84CF INC $F7
$84D1 JMP TryInsertCard

InsertCard:
$84D4 TYA
$84D5 STA $472,X
$84D8 DEY      ;Next Card
$84D9 BPL TryInsertCard
$84DB RTS      ;Done
...
  • during game:
; (included again)
; *****
; executes first
; *****

...
$C053 JSR RndFunc1 ; executes always on every frame
$C056 JSR $C2DE
...

; *****
; executes when on overhead board view
; *****

...
$8110 JSR $8585
$8113 JSR RndFunc2     ;executes on all but random 1
$8116 JSR $8585
$8119 LDA $26 ;0=not rolling 1=rolling
$811B BEQ $8120 ;not sure why they double-check; see below (RandomWhenRolling)
$811D JMP RandomWhenRolling
...

; *****
; executes only on random 2
; *****

...
$FA1B JSR RndFunc1    ;executes only on random 2
$FA1E AND #$1F ;mod 32
$FA20 CMP #$A     ;mod 10
$FA22 BCC $FA28
$FA24 SBC #$A
$FA26 BCS $FA20
$FA28 STA $5D
$FA2A JSR RndFunc1    ;executes only on random 2
$FA2D AND #$D0
...


;(Acmlm)
;****************************************
;* Randomness variation when rolling    *
;****************************************

RandomWhenRolling:
$816E:    LDA $0026
          CMP #$01           ;equal when rolling
          BNE NotRolling

          JSR RndFunc2
          JSR RndFunc2
          INC $0027
          LDA $0027
          AND #$03           ;mod 4
          BNE Skip           ;... if RAM($27)%4==0 (i.e. every 4 frames) ...

          JSR RndFunc1       ;do this ...
          STA $00FA
          AND #$42
          BNE $8190
          LDA #$02
          JSR $CB9A

$8190:    JSR RndFunc1       ;... and this
          AND #$12
          BEQ $819C          
          LDA #$01
          STA $0504

Skip:
$819C:    ...

NotRolling:
          JSR $8585


;(Acmlm)
;****************************************
;* Determine (and display) the dice     *
;* Values are randomized between them   *
;****************************************

$81EB:    LDX #$01
NextDice: STX $00FB
          ...

$8214:    JSR RndFunc1       ;Randomize and load random value
          JSR RndFunc1
          JSR RndFunc1
          JSR RndFunc1
          JSR RndFunc1

          CPX #$01           ;If second dice, XOR with RndVal2
          BNE Mod6
          EOR RndVal2

Mod6:     CMP #$06           ;modulo 6
          BCC Save
          SBC #$06
          BCS Mod6

Save:     ADC #$01           ;0-5 -> 1-6
          STA $0300,X        ;store dice

          ...
$825E:    LDX $00FB
          DEX
          BPL NextDice
(RNG = Random Number Generator)

[1] The RndVal1 RNG is actually 22-bit, not 24-bit, because the first two bits are superfluous.
Some trivia:

GameResources/NES/Monopoly last edited by Randomno on 7/3/2023 1:40 PM
Page History Latest diff List referrers View Source