=====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 [[http://www.google.co.uk/search?q=6364136223846793005|6364136223846793005]] was obtained from Knuth, D.E., [[http://www.amazon.co.uk/gp/product/0201896842/302-0106311-7812866|"The Art of Computer Programming," Vol 2, Seminumerical Algorithms]], Third edition, Addison-Wesley, 1998, p. 106 (line 26) & p. 108.