我正在尝试创建一个生成4个随机数的程序,将它们插入数组然后打印它们,但问题是它总是插入相同的数字,因为我用时钟生成数字并且它太快了,这是我的代码:
IDEAL
MODEL small
STACK 100h
DATASEG
Clock equ es:6Ch
EndMessage db 'Done',13,10,'$'
divisorTable db 10,1,0
randoms db 4 dup(11)
CODESEG
proc printNumber
push ax
push bx
push dx
mov bx,offset divisorTable
nextDigit:
xor ah,ah ; dx:ax = number
div [byte ptr bx] ; al = quotient, ah = remainder
add al,'0'
call printCharacter ; Display the quotient
mov al,ah ; ah = remainder
add bx,1 ; bx = address of next divisor
cmp [byte ptr bx],0 ; Have all divisors been done?
jne nextDigit
mov ah,2
mov dl,13
int 21h
mov dl,10
int 21h
pop dx
pop bx
pop ax
ret
endp printNumber
proc printCharacter
push ax
push dx
mov ah,2
mov dl, al
int 21h
pop dx
pop ax
ret
endp printCharacter
start:
mov ax, @data
mov ds, ax
; initialize
mov ax, 40h
mov es, ax
mov cx, 4
mov bx, offset randoms
RandLoop:
; generate random number, cx number of times
mov ax, [Clock] ; read timer counter
mov ah, [byte cs:bx] ; read one byte from memory
xor al, ah ; xor memory and counter
and al, 00001111b ; leave result between 0-15
mov [bx],al ;move the random number to the array
inc bx
loop RandLoop
; print exit message
mov cx, 4
mov bx, offset randoms
PrintOptions:
mov dl,[bx]
call printNumber
inc bx
loop PrintOptions
mov dx, offset EndMessage
mov ah, 9h
int 21h
exit:
mov ax, 4c00h
int 21h
END start
我认为问题在于:
RandLoop:
; generate random number, cx number of times
mov ax, [Clock] ; read timer counter
mov ah, [byte cs:bx] ; read one byte from memory
xor al, ah ; xor memory and counter
and al, 00001111b ; leave result between 0-15
mov [bx],al ;move the random number to the array
inc bx
;call printNumber
loop RandLoop
答案 0 :(得分:0)
这是一个经典的X-Y problem。您的问题是您没有获得好的随机生成的数字,因此您认为解决方案是插入延迟。这不是一个好的解决方案。
首先,它无法真正解决您的问题。系统时钟不是随机数发生器。没有什么"随机"关于时间。时间唯一有用的是作为种子"对于随机数发生器。 A"种子"只是起始值。当您的应用程序首次启动时,您只使用它一次,以填充随机数生成器。每次需要随机数时都不会使用时间,因此实际时间并不重要。
插入延迟的第二个原因不是一个好的解决方案,因为没有可靠的方法来插入延迟。程序员过去常常在80年代做过这样的事情,这就是为什么许多旧游戏不能在现代机器上运行的原因:他们有硬编码的时序循环,这些循环对性能做出了经验假设底层处理器。有一种可靠的方法可以通过编程定时器中断来插入延迟,但这在操作系统下不起作用,所以它今天通常都是非启动器。
这里真正的解决方案是使用更好的随机数生成器。每个C标准库都提供一个,但是如果你没有方便的C标准库(或者不想链接到一个),那么在汇编中你自己写一个并不困难。
ANSI C基本上建议了随机数生成器的以下实现:
extern unsigned long seed;
return ((seed = seed * 1103515245 + 12345) >> 16) & 0x7FFF;
这是大多数C标准库传统上使用的。它并不可怕,但它也不是很好。最大的问题是它只返回15位。如果我们在汇编中编写它,我们可以使用由32位整数乘法生成的64位中间函数,并将结果扩展为32位。这导致极快速PRNG实现,能够每秒生成百万的值,并且其随机性"实际上非常稳固。它通过了一个简单的卡方检验,当你使用它来绘制点图案时,事情就会随机变化"。
以下是代码的样子:
RNG_Seed DD ?
; Generates a pseudo-random 32-bit number.
; Parameters: <none>
; Clobbers: EAX, EDX
; Returns: EAX contains the random number
GenerateRandNum PROC
mov eax, DWORD PTR [RNG_Seed] ; get last seed value
mov edx, 1103515245 ; get multiplier
mul edx ; do multiplication
shl edx, 16 ; move into high 16 bits
add eax, 12345 ; do addition
adc edx, 0FFFFh ; carry to high
mov DWORD PTR [RNG_Seed], eax ; store this seed
shr eax, 16 ; discard low 16 bits
and edx, 0FFFF0000h ; mask high 16 bits
or eax, edx ; combine those bits
ret ; and return them
GenerateRandNum ENDP
要使用它,请在应用程序启动时将当前时间存储到RNG_Seed
。从那时起,只需调用GenerateRandNum
过程,它将在EAX
中返回一个32位随机数。请勿再次触摸RNG_Seed
(由GenerateRandNum
内部维护)。
那里有更好的伪随机数发生器,但这个应该足够好了。
当然,在我写完这个答案之后,我注意到你正在使用16位汇编。如果这意味着您指向的是早于386的处理器(例如8088/8086),则无法进行32位乘法。在这种情况下,您可能只需返回C实现,其结果为15位:
RNG_Seed DW ?
; Generates a pseudo-random 15-bit number.
; Parameters: <none>
; Clobbers: AX, DX
; Returns: AX contains the random number
GenerateRandNum PROC
push bx
push cx
push si
push di
; 32-bit multiplication in 16-bit mode (DX:AX * CX:BX == SI:DI)
mov ax, WORD PTR [RNG_Seed]
xor dx, dx
mov cx, 041C6h
mov bx, 04E6Dh
xor di, di
push ax
mul bx
mov si, dx
xchg di, ax
mul bx
add si, ax
pop ax
mul cx
add si, ax
; Do addition
add di, 3039h
adc si, 0
; Save seed
mov WORD PTR [RNG_Seed], di
; Get result and mask bits
mov ax, si
and ah, 07Fh
pop di
pop si
pop cx
pop bx
ret
GenerateRandNum ENDP
您可以在Wikipedia上找到关于伪随机数生成器的更多讨论,包括一些指向替代实现的链接。或者参见the question vitsoft linked,它有讨论中间方法的答案,这个方法相对简单。