=====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