; =============================================================================
;
; Litos - Random generator
;
; =============================================================================
; General algorithm (new_seed = old_seed*214013 + 2531011) for the random
; generator is taken from interpreted BASIC and is described in Microsoft
; Knowledgebase article Q28150. Other possible coefficients are:
; 214013/2531011, 17405/10395331, 214013/13737667, 214013/10395331
;
; Access to global seed of random generator is not SMP locked, it is a random
; number and therefore data distortion is unimportant.
;
; Random numbers in interval are usually calculated with modulo operation.
; Such random numbera are not evenly spreaded out, their density is lower at
; end of the interval. Therefore in this generator is used repeated generation
; of random numbers until suitable number is found, such numbers are spreaded
; evenly in whole interval.
; =============================================================================
CODE_SECTION 32
; ------------- Macro - shift global random generator with an event
%macro RNDSHIFT 0
inc dword [RandSeed] ; shift global random generator
%endmacro
; ------------- Macro - numeric shift global random generator with an event
; %1 = data to increase random generator
%macro RNDSHIFTADD 1
add dword [RandSeed],%1 ; shift global random generator
%endmacro
; ------------- Macro - next random value (EAX=input/output, destroys EDX)
%macro RANDNEXT 0
mov edx,214013 ; coef1
mul edx ; *= coef1
add eax,2531011 ; += coef2
%endmacro
; ------------- Macro - next global random value (EAX=output, destroys EDX)
%macro RANDGNEXT 0
mov eax,[RandSeed]; EAX <- seed for global generator
RANDNEXT ; next random value
mov [RandSeed],eax; store new global seed
%endmacro
; ------------- Macro - next global double word (EAX=output, destroys EDX)
; NOTES: Random number is taken from 2 words instead directly from
; the seed to avoid prediction of future value of the seed.
%macro RANDGDWORD 0
mov eax,[RandSeed] ; EAX <- seed for global generator
RANDNEXT ; next random value
push eax ; push first random value
RANDNEXT ; next random value
mov [RandSeed],eax ; store new global seed
pop edx ; pop first random value
shr edx,16 ; DX <- second random value
xchg ax,dx ; EAX <- random DWORD
%endmacro
; -----------------------------------------------------------------------------
; Next random value
; -----------------------------------------------------------------------------
; INPUT: EAX = old random seed
; OUTPUT: EAX = new random seed
; -----------------------------------------------------------------------------
RandNext: push edx ; push EDX
RANDNEXT ; next random seed
pop edx ; pop EDX
ret
; -----------------------------------------------------------------------------
; Get random byte (with global seed)
; -----------------------------------------------------------------------------
; OUTPUT: AL = random byte (0 to 255)
; -----------------------------------------------------------------------------
RandByte: push edx ; push EDX
push eax ; push EAX
RANDGNEXT ; next global random seed
shr eax,16 ; EAX <- random word
xchg eax,edx ; EDX <- push random word
pop eax ; pop EAX
mov al,dl ; AL <- random byte
pop edx ; pop EDX
ret
; -----------------------------------------------------------------------------
; Get random word (with global seed)
; -----------------------------------------------------------------------------
; OUTPUT: AX = random word (0 to 65535)
; -----------------------------------------------------------------------------
RandWord: push edx ; push EDX
push eax ; push EAX
RANDGNEXT ; next global random seed
shr eax,16 ; EAX <- random word
xchg eax,edx ; EDX <- push random word
pop eax ; pop EAX
xchg ax,dx ; AX <- random word
pop edx ; pop EDX
ret
; -----------------------------------------------------------------------------
; Get random quadruple word (with global seed)
; -----------------------------------------------------------------------------
; OUTPUT: EDX:EAX = random quadruple word (0 to 0ffffffffffffffffh)
; -----------------------------------------------------------------------------
RandQWord: call RandDWord ; generate first random double word
xchg eax,edx ; EDX <- first random double word
; RandDWord must follow!
; -----------------------------------------------------------------------------
; Get random double word (with global seed)
; -----------------------------------------------------------------------------
; OUTPUT: EAX = random double word (0 to 0ffffffffh)
; -----------------------------------------------------------------------------
RandDWord: push edx ; push EDX
RANDGDWORD ; generate random double word
pop edx ; pop EDX
ret
; -----------------------------------------------------------------------------
; Get random float (9 digits, with global seed)
; -----------------------------------------------------------------------------
; OUTPUT: ST0 = random float (0 to 1, 0 including, 1 except)
; NOTES: Maximal value is 0.99999999976716935, prev 0.99999999953433871.
; -----------------------------------------------------------------------------
RandFloat: push eax ; push EAX
xor eax,eax ; EAX <- 0
push eax ; store HIGH dword of the number
call RandDWord ; get random double word
push eax ; store number to the stack
fild qword [esp] ; ST0 <- random number
fmul dword [RandFltCoef] ; recalc to range 0 to 1
pop eax ; destroy number from the stack
pop eax ; destroy HIGH dword from the stack
pop eax ; pop EAX
ret
; -----------------------------------------------------------------------------
; Get random double float (18 digits, with global seed)
; -----------------------------------------------------------------------------
; OUTPUT: ST0 = random double float (0 to 1)
; NOTES: Due to rounding FPU results, it can reach "1" boundary.
; -----------------------------------------------------------------------------
RandDouble: push eax ; push EAX
call RandDWord ; get first random double word
shr eax,1 ; reset signum bit
push eax ; store HIGH number to the stack
call RandDWord ; get second random double word
push eax ; store LOW number to the stack
fild qword [esp] ; ST0 <- random number
fmul qword [RandDblCoef] ; recalc to range 0 to 1
pop eax ; destroy LOW number from the stack
pop eax ; destroy HIGH number from the stack
pop eax ; pop EAX
ret
; -----------------------------------------------------------------------------
; Get random unsigned byte with max value (with global seed)
; -----------------------------------------------------------------------------
; INPUT: AL = maximal unsigned value (0 to 255)
; OUTPUT: AL = random byte (0 to maximal value)
; -----------------------------------------------------------------------------
; ------------- Check maximal value 0
RandMaxByte: or al,al ; maximal value is 0?
jz RandMaxByte8 ; maximal value is 0
; ------------- Push registers
push ecx ; push ECX
push edx ; push EDX
; ------------- Prepare mask or maximal value
movzx edx,al ; EDX <- maximal value
bsr ecx,edx ; ECX <- bits of maximal value
mov ch,al ; CH <- maximal value
mov dl,B1 ; DL <- maximal value bit
shl edx,cl ; EDX <- maximal value
dec edx ; DL <- mask of result
; ------------- Find required value
RandMaxByte2: call RandByte ; get random byte (-> AL)
and al,dl ; mask result
cmp al,ch ; is this value allowed?
ja RandMaxByte2 ; invalid value, next attempt
; ------------- Pop registers
pop edx ; pop EDX
pop ecx ; pop ECX
RandMaxByte8: ret
; -----------------------------------------------------------------------------
; Get random signed byte with max value (with global seed)
; -----------------------------------------------------------------------------
; INPUT: AL = maximal signed value (-128 to +127)
; OUTPUT: AL = random byte (0 to maximal value)
; -----------------------------------------------------------------------------
RandMaxChar: or al,al ; is it unsugned value?
jns RandMaxByte ; it is unsigned value
neg al ; correct maximal value
call RandMaxByte ; generate random byte
neg al ; correct result
ret
; -----------------------------------------------------------------------------
; Get random unsigned word with max value (with global seed)
; -----------------------------------------------------------------------------
; INPUT: AX = maximal unsigned value (0 to 65535)
; OUTPUT: AX = random word (0 to maximal value)
; -----------------------------------------------------------------------------
; ------------- Check maximal value 0
RandMaxWord: or ax,ax ; maximal value is 0?
jz RandMaxWord8 ; maximal value is 0
; ------------- Push registers
push ecx ; push ECX
push edx ; push EDX
; ------------- Prepare mask or maximal value
bsr cx,ax ; ECX <- bits of maximal value
xor edx,edx ; EDX <- 0
mov dl,B1 ; EDX <- B1, maximal value bit
shl edx,cl ; EDX <- maximal value
dec edx ; DX <- mask of result
xchg ax,cx ; CX <- maximal value
; ------------- Find required value
RandMaxWord2: call RandWord ; get random word (-> AX)
and ax,dx ; mask result
cmp ax,cx ; is this value allowed?
ja RandMaxWord2 ; invalid value, next attempt
; ------------- Pop registers
pop edx ; pop EDX
pop ecx ; pop ECX
RandMaxWord8: ret
; -----------------------------------------------------------------------------
; Get random signed word with max value (with global seed)
; -----------------------------------------------------------------------------
; INPUT: AX = maximal signed value (-32768 to +32767)
; OUTPUT: AX = random word (0 to maximal value)
; -----------------------------------------------------------------------------
RandMaxShort: or ax,ax ; is it unsugned value?
jns RandMaxWord ; it is unsigned value
neg ax ; correct maximal value
call RandMaxWord ; generate random word
neg ax ; correct result
ret
; -----------------------------------------------------------------------------
; Get random unsigned double word with max value (with global seed)
; -----------------------------------------------------------------------------
; INPUT: EAX = maximal unsigned value (0 to 0ffffffffh)
; OUTPUT: EAX = random double word (0 to maximal value)
; -----------------------------------------------------------------------------
; ------------- Check maximal value 0
RandMaxDWord: cmp eax,0ffffh ; only word number required?
jbe RandMaxWord ; generate word random number
or eax,eax ; maximal value is 0?
jz RandMaxDWord8 ; maximal value is 0
; ------------- Push registers
push ecx ; push ECX
push edx ; push EDX
; ------------- Prepare mask or maximal value
bsr ecx,eax ; ECX <- bits of maximal value
xor edx,edx ; EDX <- 0
mov dl,B1 ; EDX <- B1, maximal value bit
shl edx,cl ; EDX <- maximal value
dec edx ; EDX <- mask of result
jnz RandMaxDWord2 ; mask is OK
dec edx ; EDX <- -1, maximal mask
RandMaxDWord2: xchg eax,ecx ; ECX <- maximal value
; ------------- Find required value
RandMaxDWord4: call RandDWord ; get random double word (-> EAX)
and eax,edx ; mask result
cmp eax,ecx ; is this value allowed?
ja RandMaxDWord4 ; invalid value, next attempt
; ------------- Pop registers
pop edx ; pop EDX
pop ecx ; pop ECX
RandMaxDWord8: ret
; -----------------------------------------------------------------------------
; Get random signed double word with max value (with global seed)
; -----------------------------------------------------------------------------
; INPUT: EAX = maximal signed value (-80000000h to +7fffffffh)
; OUTPUT: EAX = random double word (0 to maximal value)
; -----------------------------------------------------------------------------
RandMaxLong: or eax,eax ; is it unsugned value?
jns RandMaxDWord ; it is unsigned value
neg eax ; correct maximal value
call RandMaxDWord ; generate random double word
neg eax ; correct result
ret
; -----------------------------------------------------------------------------
; Get random unsigned quadruple word with max value (with global seed)
; -----------------------------------------------------------------------------
; INPUT: EDX:EAX = maximal unsigned value (0 to 0ffffffffffffffffh)
; OUTPUT: EDX:EAX = random quadruple word (0 to maximal value)
; -----------------------------------------------------------------------------
; ------------- Check maximal value 0
RandMaxQWord: or edx,edx ; is maximal value QWORD?
jz RandMaxDWord ; only DWORD required
; ------------- Push registers
push ebx ; push EBX
push ecx ; push ECX
push esi ; push ESI
; ------------- Prepare mask or maximal value
bsr ecx,edx ; ECX <- bits of maximal value HIGH
xor ebx,ebx ; EBX <- 0
mov bl,B1 ; EBX <- B1, maximal value bit
shl ebx,cl ; EBX <- maximal value HIGH
dec ebx ; EBX <- mask of result HIGH
jnz RandMaxQWord4 ; mask is OK
dec ebx ; EBX <- -1, maximal mask HIGH
RandMaxQWord4: xchg eax,ecx ; ECX <- maximal value LOW
mov esi,edx ; ESI <- maximal value HIGH
; ------------- Find required value
RandMaxQWord6: call RandQWord ; get random quadruple word (->EDX:EAX)
and edx,ebx ; mask result HIGH
cmp edx,esi ; check value HIGH
jb RandMaxQWord8 ; value is OK
ja RandMaxQWord6 ; invalid value, next attempt
cmp eax,ecx ; check value LOW
ja RandMaxQWord6 ; invalid value, next attempt
; ------------- Pop registers
RandMaxQWord8: pop esi ; pop ESI
pop ecx ; pop ECX
pop ebx ; pop EBX
ret
; -----------------------------------------------------------------------------
; Get random signed quadruple word with max value (with global seed)
; -----------------------------------------------------------------------------
; INPUT: EDX:EAX = maximal signed value
; OUTPUT: EDX:EAX = random quadruple word (0 to maximal value)
; -----------------------------------------------------------------------------
RandMaxInt64: or edx,edx ; is it unsugned value?
jns RandMaxQWord ; it is unsigned value
neg eax ; correct maximal value LOW
adc edx,byte 0 ; carry
neg edx ; correct maximal value HIGH
call RandMaxQWord ; generate random double word
neg eax ; correct result LOW
adc edx,byte 0 ; carry
neg edx ; correct result HIGH
ret
; -----------------------------------------------------------------------------
; Get random float with max value (9 digits, with global seed)
; -----------------------------------------------------------------------------
; INPUT: ST0 = maximal value (can be signed)
; OUTPUT: ST0 = random float (0 to max value, except)
; -----------------------------------------------------------------------------
RandMaxFloat: call RandFloat ; generate random float
fmulp st1,st0 ; multiple with max. value
ret
; -----------------------------------------------------------------------------
; Get random double float with max value (18 digits, with global seed)
; -----------------------------------------------------------------------------
; INPUT: ST0 = maximal value (can be signed)
; OUTPUT: ST0 = random double float (0 to max value)
; NOTES: Due to rounding FPU results, it can reach max value.
; -----------------------------------------------------------------------------
RandMaxDouble: call RandDouble ; generate random double float
fmulp st1,st0 ; multiple with max. value
ret
; -----------------------------------------------------------------------------
; Get random byte in interval (with global seed)
; -----------------------------------------------------------------------------
; INPUT: AL = maximal value (signed or unsigned)
; AH = minimal value (signed or unsigned)
; OUTPUT: AL = random byte (min to max)
; -----------------------------------------------------------------------------
RandIntByte: sub al,ah ; AL <- new maximal value
call RandMaxByte ; generate random byte
add al,ah ; AL <- add minimal value
ret
; -----------------------------------------------------------------------------
; Get random word in interval (with global seed)
; -----------------------------------------------------------------------------
; INPUT: AX = maximal value (signed or unsigned)
; DX = minimal value (signed or unsigned)
; OUTPUT: AX = random word (min to max)
; -----------------------------------------------------------------------------
RandIntWord: sub ax,dx ; AX <- new maximal value
call RandMaxWord ; generate random word
add ax,dx ; AX <- add minimal value
ret
; -----------------------------------------------------------------------------
; Get random double word in interval (with global seed)
; -----------------------------------------------------------------------------
; INPUT: EAX = maximal value (signed or unsigned)
; EDX = minimal value (signed or unsigned)
; OUTPUT: EAX = random double word (min to max)
; -----------------------------------------------------------------------------
RandIntDWord: sub eax,edx ; EAX <- new maximal value
call RandMaxDWord ; generate random double word
add eax,edx ; EAX <- add minimal value
ret
; -----------------------------------------------------------------------------
; Get random quadruple word in interval (with global seed)
; -----------------------------------------------------------------------------
; INPUT: EDX:EAX = maximal value (signed or unsigned)
; EBX:ECX = minimal value (signed or unsigned)
; OUTPUT: EDX:EAX = random quadruple word (min to max)
; -----------------------------------------------------------------------------
RandIntQWord: sub eax,ecx ; EAX <- new maximal value LOW
sbb edx,ebx ; EDX <- new maximal value HIGH
call RandMaxQWord ; generate random quadruple word
add eax,ecx ; EAX <- add minimal value LOW
adc edx,ebx ; EDX <- add minimal value HIGH
ret
; -----------------------------------------------------------------------------
; Get random float in interval (9 digits, with global seed)
; -----------------------------------------------------------------------------
; INPUT: ST0 = maximal value (can be signed)
; ST1 = minimal value (can be signed)
; OUTPUT: ST0 = random float (min to max)
; -----------------------------------------------------------------------------
RandIntFloat: fsub st0,st1 ; subtract minimal value
call RandMaxFloat ; generate random float
faddp st1,st0 ; random value
ret
; -----------------------------------------------------------------------------
; Get random double float in interval (9 digits, with global seed)
; -----------------------------------------------------------------------------
; INPUT: ST0 = maximal value (can be signed)
; ST1 = minimal value (can be signed)
; OUTPUT: ST0 = random double float (min to max)
; -----------------------------------------------------------------------------
RandIntDouble: fsub st0,st1 ; subtract minimal value
call RandMaxDouble ; generate random double float
faddp st1,st0 ; random value
ret
; -----------------------------------------------------------------------------
; Generate Pseudo-Gaussian random number (with global seed)
; -----------------------------------------------------------------------------
; INPUT: AL = level (0 to 255, 0=linear)
; OUTPUT: ST0 = random double float (0 to 1)
; NOTES: This random generator is not true Gaussian generator. It
; generates random numbers with distribution which is near to
; the Gaussian distribution. This is very simple algorithm:
; it simple adds given number of random numbers. In results
; there is minimal probability of values near 0 and 1 and
; maximal probability of values near 0.5. Level value determines
; steepness of the probability curve.
; -----------------------------------------------------------------------------
; Level 0: Level 1: Level 2: Level 10: X
; | | | | XXX
; | | x | xXXXx | xXXXx
; | | xXXXx | XXXXXXXXX | XXXXX
; |XXXXXXXXXXX | xXXXXXXXx | XXXXXXXXXXX | xXXXXXx
; |XXXXXXXXXXX |xXXXXXXXXXXXx | xxXXXXXXXXXXXXXXXxx | xxXXXXXXXXXxx
; +----------- +------------- +--------------------- +----------------------
; -----------------------------------------------------------------------------
; ------------- Push registers
RandGauss: push ecx ; push ECX
; ------------- Prepare level
movzx ecx,al ; ECX <- required level
inc ecx ; ECX <- number of random numbers
push ecx ; push ECX, count of numbers
; ------------- Add random numbers
fldz ; load zero to ST0
RandGauss2: call RandDouble ; generate random double float number
faddp st1,st0 ; add number to accumulator
loop RandGauss2 ; next value
; ------------- Divide accumulator with count of numbers
fidiv dword [esp] ; divide with count of numbers
pop ecx ; destroy ECX from the stack
; ------------- Pop registers
pop ecx ; pop ECX
ret
; -----------------------------------------------------------------------------
; Data
; -----------------------------------------------------------------------------
DATA_SECTION
; ------------- Global seed for random generator
; Global seed is initialized in INIT\INIT.ASM and incremented with events.
align 4, db 0
RandSeed: dd 5623489 ; seed for global random generator
; ------------- Float coefficient 1/100000000h
align 4, db 0
RandFltCoef: dd 0.00000000023283064365386962890625
; ------------- Double float coefficient 1/8000000000000000h
align 8, db 0
RandDblCoef: dq 1.0842021724855044340074528008699e-19
; -----------------------------------------------------------------------------
; Uninitialized data
; -----------------------------------------------------------------------------
BSS_SECTION
|