Table of Contents
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