;/***************************************************************************
; *                                                                         *
; * Module  : rand.s                                                        *
; *                                                                         *
; * Purpose : A rand() generator using the linear congruential method       *
; *                                                                         *
; * Version : 1.01, January 7 1998                                          *
; *             Added _initrand to set seed without recompiling             *
; *           1, January 6 1998                                             *
; *                                                                         *
; * Author  : Luc Van den Borre ( Homepage : NOC.BASE.ORG )                 *
; *                                                                         * 
; **************************************************************************/

        ;; Why use an algorithm for generating random numbers? 
        ;;        
        ;; - Given a certain seed value, the same sequence of random numbers is generated
        ;;   every time. This is a good thing when debugging (reproducible). On the other
        ;;   hand, you've got 2^16 seed values, each of which will produce a sequence of
        ;;   numbers that stays different for any of the other sequences for 'an appreciable
        ;;   time.' (I can't say how long exactly.)
        ;;
        ;; - The linear congruential method is one of the 'best' random number generators
        ;;   around. However, this implementation uses a 16 bit accumulator, while at least 
        ;;   32 bits are needed for a generator that passes all the statistical tests.
        ;;   Still, I'm relatively confident that this is random enough for even the most
        ;;   demanding game.
        ;;
        ;;   Compare this to getting random values from one of the hardware registers
        ;;   (not reproducible, might not have all values). An array might be the best bet 
        ;;   if you don't need a lot of values (or have lots of memory spare), 
        ;;   or if you want values to be within a certain range. 
        ;;   And both would be faster than this. Also, this definitely isn't the fastest algorithm 
        ;;   I know, and certainly for games less strict algorithms might be appropriate (shift and xor ?). 
        ;;   It's your choice - but if you're doing Monte Carlo physics simulations on the
        ;;   GameBoy, this is a safe bet!
        

        .include        "..\..\lib\global.s"
        .include        "rndglob.s"

        .area   _BSS
.randhi:                        ; Storage for last random number (or seed)
        .ds     0x1
.randlo:
        .ds     0x1
.randarr:
        .ds     55
.raxj:
        .ds     0x1
.raxk:
        .ds     0x1

        .area   _CODE

        ;; Random number generator using the linear congruential method
        ;;  X(n+1) = (a*X(n)+c) mod m
        ;; with a = 17, m = 16 and c = $5c93 (arbitrarily)
        ;; The seed value is also chosen arbitrarily as $a27e
        ;; Ref : D. E. Knuth, "The Art of Computer Programming" , Volume 2
        ;;
        ;; Exit conditions
        ;;   DE = Random number [0,2^16-1]
        ;;
        ;; Registers used: 
        ;;   A, HL (need not be saved) and DE (return register)
        ;;

_rand::
        LD      A, (.randlo)
        LD      L, A
        LD      E, A        ; save randlo
        LD      A, (.randhi)
        LD      D, A        ; save randhi

        SLA     L           ; * 16
        RLA
        SLA     L
        RLA
        SLA     L
        RLA
        SLA     L
        RLA
        LD      H, A        ; save randhi*16

        LD      A, E        ; old randlo
        ADD     A, L        ; add randlo*16
        LD      L, A        ; save randlo*17

        LD      A, H        ; randhi*16
        ADC     A, D        ; add old randhi
        LD      H, A        ; save randhi*17

        LD      A, L        ; randlo*17
        ADD     A, #0x93
        LD      (.randlo), A
        LD      D, A        ; return register
        LD      A, H        ; randhi*17
        ADC     A, #0x5c
        LD      (.randhi), A
        LD      E, A        ; return register

        ;   Note D is the low byte, E the high byte. This is intentional because
        ;   the high byte can be slightly 'more random' than the low byte, and I presume
        ;   most will cast the return value to a UBYTE. As if someone will use this, tha!
        RET


        ;; This sets the seed value. Call it whenever you like
        ;;
        ;; Exit conditions
        ;;   DE = 0x0
        ;;
        ;; Registers used: 
        ;;   A, HL (need not be saved) and DE (return register)
        ;;
_initrand::
        LDA     HL,2(SP)
        LD      A, (HL+)
        LD      (.randlo), A       
        LD      A, (HL)
        LD      (.randhi), A
        LD      DE, #0x0       
        RET
