=====Generating pseudo-random numbers=====
//by Richard Russell, August 2006//\\ \\ BBC BASIC supplies pseudo-random numbers in three formats:\\ \\
* **RND** supplies a pseudo-random 32-bit integer
* **RND(N)** supplies a pseudo-random positive integer in the range 1 to N
* **RND(1)** supplies a pseudo-random floating-point number in the range 0.0 to 1.0
\\ This article contains routines to provide similar facilities for your assembly-language programs. They are short and very fast, yet the pseudo-random numbers they supply are of an equivalent quality to those supplied by RND.\\ \\
==== RND ====
\\ The subroutine below provides a direct equivalent to RND. Each time it is called it returns a pseudo-random 32-bit integer in the eax register:\\ \\
.seed
dd 0
db 1
;
; Rnd - return a pseudo-random 32-bit integer
; Inputs - None
; Outputs - eax = result
; Destroys - eax, cl, edx, flags
;
.Rnd
mov eax,[seed]
mov cl,[seed+4]
mov edx,eax
shr cl,1
rcr edx,1
rcl cl,1
shl eax,12
xor edx,eax
mov eax,edx
shr eax,20
xor eax,edx
mov [seed+4],cl
mov [seed],eax
ret
This subroutine will generate the same sequence of numbers every time it is used. You are likely to want to //randomise// the sequence in order to make the results less predictable. You can simply do that using the following code, which uses the time as a seed:\\ \\
.Randomise
call "GetTickCount"
mov [seed],eax
ret
You can test the code from BASIC as follows:
CALL Randomise
FOR I% = 1 TO 20
PRINT ~ USR(Rnd)
NEXT
==== RND(N) ====
\\ The subroutine below provides a direct equivalent to RND(N), where N is a positive integer greater than 1. Each time it is called it returns a pseudo-random integer in the range 1 to N, in the eax register:\\ \\
;
; RndRange - return a pseudo-random integer in the range 1 to N
; Inputs - ebx = N
; Outputs - eax = result
; Destroys - eax, cl, edx, flags
;
.RndRange
call Rnd
xor edx,edx
div ebx
mov eax,edx
inc eax
ret
The parameter N is passed to the subroutine in the ebx register. If you prefer to return a number in the range 0 to N-1 just delete the **inc eax** instruction.\\ \\ You can test the code from BASIC as follows:
CALL Randomise
FOR I% = 1 TO 20
B% = 100
PRINT USR(RndRange)
NEXT
==== RND(1) ====
\\ The subroutine below provides a direct equivalent to RND(1). Each time it is called it returns a pseudo-random 64-bit floating-point value (double) in the range 0.0 to 1.0:\\ \\
;
; RndFloat - return a pseudo-random float in the range 0.0 to 1.0
; Inputs - ebx = memory address of 64-bit float (double)
; Outputs - result stored at [ebx]
; Destroys - eax, ecx, edx, flags
;
.RndFloat
call Rnd
bsr ecx,eax
ror eax,cl
add ecx,991
shld ecx,eax,20
shl eax,20
mov [ebx],eax
mov [ebx+4],ecx
ret
The value is returned in memory at the address passed in the ebx register.\\ \\ You can test the code from BASIC as follows:
CALL Randomise
FOR I% = 1 TO 20
B% = ^R#
CALL RndFloat
PRINT R#
NEXT