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:

code format="asm" .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 code 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:

code format="bb4w" .Randomise call "GetTickCount" mov [seed],eax ret code You can test the code from BASIC as follows:

code format="bb4w" CALL Randomise FOR I% = 1 TO 20 PRINT ~ USR(Rnd) NEXT code

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:

code format="asm" ;     ; 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 code 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:

code format="bb4w" CALL Randomise FOR I% = 1 TO 20 B% = 100 PRINT USR(RndRange) NEXT code

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:

code format="asm" ;       ; 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 code The value is returned in memory at the address passed in the ebx register.

You can test the code from BASIC as follows:

code format="bb4w" CALL Randomise FOR I% = 1 TO 20 B% = ^R# CALL RndFloat PRINT R#     NEXT code