;/***************************************************************************
; *                                                                         *
; * 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!

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

	.area	_HOME

	;; 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::				; Banked
_randw::			; Banked
	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 uint8_t. 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
