1
0
mirror of https://github.com/cc65/cc65.git synced 2024-07-14 19:29:26 +00:00
cc65/libsrc/common/rand.s
Brad Smith 041f981960
rand() use XOR to break up unwanted pair correlation (#1107)
* rand() use XOR to break up unwanted pair correlation

This form of rand() cannot return the same value twice in a row.
Two additonal EOR instructions produce a more even distribution of successive pairs.
see comments on #951

* rand.s document purpose of XOR

* suggested srand() optimization: zero fill unnecessary

* test to validate implementation of rand()

* srand() improving behaviour and adding startup test

* srand() with a tail call to rand() for better initial shuffle

* srand() can fall through to rand() instead of tail call
2020-07-21 23:38:18 +02:00

70 lines
2.1 KiB
ArmAsm

;
; Randum number generator
;
; Written and donated by Sidney Cadot - sidney@ch.twi.tudelft.nl
; 2016-11-07, modified by Brad Smith
; 2019-10-07, modified by Lewis "LRFLEW" Fox
;
; May be distributed with the cc65 runtime using the same license.
;
;
; int rand (void);
; void srand (unsigned seed);
;
; Uses 4-byte state.
; Multiplier must be 1 (mod 4)
; Added value must be 1 (mod 2)
; This guarantees max. period (2**32)
; The quality of entropy in the bits of the seed are poorest in the lowest
; bits, and best in the highest bits.
;
; The high 8 bits are used for the low byte A to provide the best entropy in
; the most commonly used part of the return value.
;
; Finally XOR with the lower 2 bytes is used on the output, which breaks up
; some minor deficient sequential patterns. (#951)
;
; Uses the following LCG values for ax + c (mod m)
; a = $01010101
; c = $B3B3B3B3
; m = $100000000 (32-bit truncation)
;
; The multiplier was carefully chosen such that it can
; be computed with 3 adc instructions, and the increment
; was chosen to have the same value in each byte to allow
; the addition to be performed in conjunction with the
; multiplication, adding only 1 additional adc instruction.
;
.export _rand, _srand
.data
; The seed. When srand() is not called, the C standard says that that rand()
; should behave as if srand() was called with an argument of 1 before.
rand: .dword $B5B5B4B4
.code
_srand: sta rand+0 ; Store the seed
stx rand+1
sta rand+2 ; argument << 16 is convenient fill for MSW
stx rand+3
; fall through to rand() to sufficiently "shuffle" first rand() result
_rand: clc
lda rand+0
adc #$B3
sta rand+0
adc rand+1
sta rand+1
adc rand+2
sta rand+2
eor rand+0
and #$7f ; Suppress sign bit (make it positive)
tax
lda rand+2
adc rand+3
sta rand+3
eor rand+1
rts ; return bit (16-22,24-31) in (X,A)