;/***************************************************************************
; *                                                                         *
; * 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	"global.s"

	.area	_BSS
.randhi::			; Storage for last random number (or seed)
	.ds	0x01
.randlo::
	.ds	0x01

	.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::
_randw::
	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
	;;   None
	;;
	;; Registers used:
	;;   A, HL (need not be saved) and DE (return register)
	;;
_initrand::
	LDA	HL,2(SP)
.initrand::
	LD	A, (HL+)
	LD	(.randlo), A
	LD	A, (HL)
	LD	(.randhi), A
	RET
