User Tools

Site Tools


alternative_20pseudo-random_20numbers

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
        add    edx, ebx             ; |/
        mov    ebx, [seed+4]        ; |
        imul   ebx, ebx, &4C957F2D  ; |
        add    edx, ebx             ; |
        add    eax, 1               ; seed = seed + 1
        adc    edx, 0
        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
        inc    eax                  ; add one
        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.

This website uses cookies. By using the website, you agree with storing cookies on your computer. Also you acknowledge that you have read and understand our Privacy Policy. If you do not agree leave the website.More information about cookies
alternative_20pseudo-random_20numbers.txt · Last modified: 2024/01/05 00:22 by 127.0.0.1