BBC BASIC Programmers' Reference

Site Tools

alternative_20pseudo-random_20numbers

This is an old revision of the document!

Alternative pseudo-random numbers

by Jon Ripley, August 2006

BBC BASIC for Windows provides a pseudo-random number generator for use in BASIC programs but has no built-in support for generating pseudo-random numbers from assembly language programs. This article contains code to add support for generating pseudo-random numbers from assembly language that can be added to any program.

Rand

The following routine generates a 32-bit pseudo-random number when called, the pseudo-random number is returned in the eax register:

`        .seed     dd 1 :dd 0`
```        .Rand
mov    eax, &4C957F2D       ; |
mul    dword [seed]         ; |
push   edx                  ; |
mov    edx, [seed]          ; |
imul   ebx, edx, &5851F42D  ; |\
pop    edx                  ; | - seed = seed * &5851F42D4C957F2D
mov    ebx, [seed+4]        ; |
imul   ebx, ebx, &4C957F2D  ; |
add    eax, 1               ; seed = seed + 1
mov    [seed], eax          ; store the new seed
mov    [seed+4], edx
shrd   eax, edx, 21         ; shift right 21 bits
ret                         ; return```

Rand(N)

The following routine generates a pseudo-random number in the range 1 to N:

```        .RandRange
call   Rand                 ; get random number
mov    ebx, [esp+4]         ; read range limit
xor    edx,edx              ; clear edx register
div    ebx                  ; do integer division; eax / ebx
mov    eax,edx              ; read remainder of division
ret    4                    ; restore stack and return```

Call RandRange using code similar to the following:

```      push N%
call RandRange```

The pseudo-random number is returned in eax. Here N% can be a register eax, read from a pointer to a 4 byte block in memory [addr], a BASIC constant N% defined at assemble time, a BASIC variable [^N%] or a numeric constant 1234. If the assembler reports a “Size needed” error add the dword prefix to the pushed parameter.

To change the range of numbers returned to 0 to N remove the 'inc eax' instruction.

Rand(0)

The following routine generates a pseudo-random number in the range 0.0 to 1.0, exclusive of 1.0:

```        .RandFloat
call   Rand                 ; get random number
and    eax, &7FFFFFFF       ; clear top bit
push   eax                  ; push random number on stack
call   Rand                 ; get random number
push   eax                  ; push random number on stack
push   &80000000            ; \ Put &8000000000000000
push   0                    ; / on the stack
fild   qword [esp]          ; load &8000000000000000
fild   qword [esp+8]        ; load random number
fdivrp st1,st0              ; divide random number by &8000000000000000
fabs                        ; convert result to absolute value
mov    eax,[esp+20]         ; read location to store result
fstp   qword [eax]          ; store result
add    esp,16               ; free local variables
ret    4                    ; restore stack and return```

To call this routine use code similar to the following:

```      push ^N#
call RandFloat```

The result is stored in the memory pointed to by the parameter. Here ^N# is a pointer to a 64-bit floating point BASIC variable but can be a pointer to an 8 byte block of memory [addr]. If the assembler reports a “Size needed” error add the dword prefix to the pushed parameter.

Seeding the pseudo-random number generator

The following routine is called to seed the pseudo-random number generator with a 64-bit seed:

```        .RandSeed
mov    eax, [esp+4]         ; load new seed
mov    edx, [esp+8]
mov    dword [seed], eax    ; store new seed
mov    dword [seed+4], edx
ret    8                    ; restore stack and return```

To seed the random number generator use the following code:

```      push hN
push lN
call RandSeed```

Here we pass a 64-bit integer value to seed the pseudo-random number, hN is the top 32-bits of the seed and lN is the bottom 32-bits of the seed. By default the seed is set to the value of TIME when the code was assembled. If the assembler reports a “Size needed” error add the dword prefix to the pushed parameter.

Calling the pseudo-random number generator from BASIC

These routines may be called from BASIC, instead of using the RND function, if you really want to:

```      result% = USR Rand :REM Return a pseudo-random number in result%
SYS RandFloat, ^N# :REM Return a pseudo-random float in N#
SYS RandRange, N TO result% :REM Return a range limited pseudo-random number in result%
SYS RandSeed, TIME, TIME :REM Seed the pseudo-random generator using TIME```

References

The multiplier 6364136223846793005 was obtained from Knuth, D.E., "The Art of Computer Programming," Vol 2, Seminumerical Algorithms, Third edition, Addison-Wesley, 1998, p. 106 (line 26) & p. 108.