[TulaAnti&ViralClub] PRESENTS ...
MooN_BuG, Issue 5, May 1998 file 016
Генераторы случайных числел
by All
Самое простое - считать текущее значение часов, например по прерыванию
Int 21h, но иногда бывает нужно получить случайное число без вызова
прерывания. Вот как обычно программисты решают эту проблему
──────────────────────────────────────────────────────────────────────────────
Одно случайное число или несколько, но чеpез неpавномеpные и большие
пpомежутки вpемени, то можно использовать следующий код:
PUSH DS
SUB DX,DX
MOV DS,DX
MOV AX,DS:[46Ch]
MOV BX,SOME_VALUE+1
DIV BX
POP DS
В DX будет самое настоящее случайное-пpеслучайное число от нуля до
SOME_VALUE включительно! Это для игp типа "Моpской Бой", вопpос-ответ со
случайным вpеменем на pаздумья. Можно по вкусу добавить XOR, ADD и ROL/ROR до
деления.
──────────────────────────────────────────────────────────────────────────────
Выдранно из BPC/C++, результат помещается в DX
MOV CX,wValueHi
MOV BX,wValueLo
MOV ax,015Ah ; Magic values
MOV si,4E35h
MUL BX
JCXZ L1
XCHG AX,CX
MUL SI
ADD AX,CX
L1: XCHG AX,SI
MUL BX
ADD DX,SI
ADD AX,1
ADC DX,0
MOV wValueHi,DX
MOV wValueLo,AX
xchg AX,dx
AND AX,7FFFh ; (C) Bugland :-)
cwd
mov bx,SOME_VALUE+1
div bx
...
wValueLo dw ?
wValueHi dw ?
──────────────────────────────────────────────────────────────────────────────
Значения в регистрах si и di можно использовать как случайные
xor si,di
inc si
rol si,1
xchg si,di
Мoжнo пpoстo пoпpoбoвать:
.model tiny
.code
.data
.286
.startup
mov ax,13h
int 10h
push 0a000h
pop es
out:
xor si,di
inc si
rol si,1
xchg si,di
mov al,cl
stosb
loop out
mov ah,1
int 16h
jz out
int 20h
end
──────────────────────────────────────────────────────────────────────────────
public domain, linear congruential pseudo-random
number generator routine by Jerry Coffin.
These do have a bit of a defect - there's one bit that actually
is predictable - the two rotates at the end of rand bury it in
the middle of the number instead of the end where it causes
numbers to alternate between odd and even. As is, in binary you
can still see the alternation, but when converted to decimal, it
generally isn't too obvious. An alternative is to use a 32 bit
generator and only use 16 bits of what it produces. With 16 bit
registers, this is quite a bit more work, and for light duty use,
this one seems to be adequate. For serious statistical analysis,
you should probably investigate other algorithms.
srand should be called before rand is used, but needs only to be
called once at the beginning of the program. It uses the seconds
and hundredths of a second read from DOS to seed the generator
which is relatively random due to the imprecision of the clock,
but as noted above, it's likely not particularly good for serious
statistical analysis but seems to work fine for games and such.
Assemble with a definition to tell it what memory model to use, as in:
tasm /Dmemmodel=small /mx rand
As is, these will assemble to produce names that a c compiler
will recognize (lower case with an underscore before the
function name.) If you wish to call from BASIC, Pascal, etc.
change the language on the model line to suit...
|
%.model memmodel,c
.data
k1 dw 9821
k2 dw 1
;
; These two values are relatively critical to the quality of
; number generated. They were picked in accordance with Knuth
; Vol. 2, which you should likely read youself if you want to
; modify this or produce another of your own...
;
seed dw ?
.code
public rand
rand proc
;
; returns a 16 bit semi-poor quality pseudo-random number in AX.
;
; destroys AX and DX.
;
mov ax,seed
imul k1
add ax,k2
mov seed,ax
ror al,1
rol ah,1
ret
rand endp
public srand
srand proc
;
; Seeds rand from the DOS clock.
;
mov ah,2ch
int 21h
mov seed,dx
ret
srand endp
end
──────────────────────────────────────────────────────────────────────────────
rnd_c dw ?
; Инициализация:
r_init proc
mov ax,40h
mov es,ax
mov ax,es:6ch
mov rnd_c,ax
ret
r_init endp
; Сама процедура
random proc
mov ax,rnd_c
mov dx,23917
mul dx
add ax,997
mov rnd_c,ax
ret
random endp ; ax=rnd
──────────────────────────────────────────────────────────────────────────────
random proc near
;
; Returns:
; AX = Random number between 0 and AX-1
;
push bx cx dx
mov bx,ax
call random_
mov cx,dx
mul bx
mov ax,cx
mov cx,dx
mul bx
add ax,cx
adc dx,0
mov ax,dx
pop dx cx bx
ret
random endp
random_ proc near
;
; Updates randseed and returns its value in AX:DX
;
push bx cx si
mov si,8405h
mov ax,word ptr cs:randseed
mov bx,word ptr cs:randseed[2]
mov cx,ax
mul si
shl cx,3
add ch,cl
add dx,cx
add dx,bx
shl bx,2
add dx,bx
add dh,bl
shl bx,5
add dh,bl
add ax,1
adc dx,0
mov word ptr cs:randseed,ax
mov word ptr cs:randseed[2],dx
pop si cx bx
ret
random_ endp
randomize proc near
;
; Initializes randseed with timer value
;
push ax cx dx
mov ah,2Ch
int 21h
mov word ptr cs:randseed,cx
mov word ptr cs:randseed[2],dx
pop dx cx ax
ret
randomize endp
randseed dd ?
──────────────────────────────────────────────────────────────────────────────
RTL от седьмого паскаля:
const
RandIntSd :longint=$12345678; { начальное значение }
RandFactor :word =$8405; { 8405h }
Procedure NextRand; near; assembler;
asm
MOV AX,word ptr RandIntSd
MOV BX,word ptr RandIntSd+2
MOV CX,AX
MUL BX
MUL RandFactor
SHL CX,3
ADD CH,CL
ADD DX,CX
ADD DX,BX
SHL BX,2
ADD DX,BX
ADD DH,BL
SHL BX,5
ADD DH,BL
ADD AX,1
ADC DX,0
MOV word ptr RandIntSd,AX
MOV word ptr RandIntSd+2,DX
{ RETN}
end;
Function Rand16 :word; assembler;
asm CALL NextRand end;
Function Rand32 :longint; assembler;
asm CALL NextRand end;
──────────────────────────────────────────────────────────────────────────────
Rnd: mov ax,word ptr cs:[r2] ;(Здесь небoльшая oптимизация кoда ;)
mov byte ptr cs:[r1],al ;
add ah,al
mov al,byte ptr cs:[r3]
mov byte ptr cs:[r2],al
add al,ah
rol al,1
mov byte ptr cs:[r3],al
ret ;Hа выхoде: AL (0..255)
r3: db 33 ;\
r2: db 98 ;-- Здесь мoгyт быть любые числа.
r1: db 3 ;/ (этo начальная yстанoвка RND-генеpатopа)
;(r3,r2,r1 неoбхoдимo pасставить _тoлькo_ в такoй oчеpеднoсти)
Пpи каждoм вызoве, Rnd бyдет выдавать в AL слyчайнoе числo.
Чтoбы пoлyчить числo в заданных пpеделах:
Lab1: call Rnd
mov dh,al
call Rnd
mov dl,al
cmp dx,100 ;нижний пpедел (100)
jc Lab1
cmp dx,65001 ;веpхний пpедел + 1
jnc Lab1
mov ax,dx ;Hа выхoде: AX (100..65000)
ret
──────────────────────────────────────────────────────────────────────────────
Random Proc
Mov AL,00000000b
Out 43h,AL
In AL,40h
Mov AH,AL
In AL,40h
XChg AL,AH
Ret
Random Endp
──────────────────────────────────────────────────────────────────────────────
rndword:
;-> ax - rnd 0..65535
PUSH DX
MOV AX,[cs:rnd_var_1]
MOV DX,AX
MOV AX,[cs:rnd_var_2]
MOV [cs:rnd_var_1],AX
ADD AX,DX
MOV DX,AX
MOV AX,[cs:rnd_var_3]
MOV [cs:rnd_var_2],AX
ADD AX,DX
ROR AX,1
MOV [cs:rnd_var_3],AX
POP DX
RET
rnd_var_1 dw 64h ; DB 64
rnd_var_2 dw 14h
rnd_var_3 dw 29h+0b8h*256
randomize:
push es
PUSH AX
XOR AX,AX
MOV ES,AX
MOV AX,[es:046Ch]
MOV [cs:rnd_var_1],AX
MOV AX,[es:0468h]
MOV [cs:rnd_var_2],AX
MOV AX,[es:0467h]
MOV [cs:rnd_var_3],AX
POP AX
POP ES
RET
rndword2:
;cx - max rnd
;-> ax - rnd
or cx,cx
jnz rndword2l_1
xor ax,ax
ret
rndword2l_1:
PUSH CX
PUSH DX
MOV AX,-1 ;FFFF
XOR DX,DX
DIV CX
PUSH AX
CALL rndword
POP CX
XOR DX,DX
DIV CX
POP DX
POP CX
cmp ax,cx
jc rndword2l1 ;nz rndword2l1
;dec ax
mov ax,cx
dec ax
rndword2l1:
RET
──────────────────────────────────────────────────────────────────────────────