2017-05-14 110 views
0

我想创建一个程序,生成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

这是一个X-Y问题。你的问题是你没有得到好随机生成的数字,所以你认为解决方案是插入一个延迟。不是这样。解决方案是使用更好的随机数发生器。更重要的是,只用“一次*”的时间“种下”RNG,而不是每次循环。 –

+0

@CodyGray你能给我一些代码吗? –

+0

http://stackoverflow.com/questions/35583343/generating-random-numbers-in-assembly – vitsoft

回答

0

这是一个经典X-Y problem。你的问题是你没有得到好随机生成的数字,所以你认为解决方案是插入一个延迟。这不是一个好的解决方案。

首先,它不会真的解决您的问题。系统时钟不是随机数发生器。关于时间没有任何“随机”。时间唯一有用的是作为随机数发生器的“种子”。 “种子”只是初始值。当您的应用程序首次启动时,您只能使用它一次,以随机数生成器启动。每次你需要一个随机数时,你都不会使用这个时间,所以这与实际时间无关。

插入延迟不是一个好的解决方案的第二个原因是因为没有可靠的方法来插入延迟。程序员在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,它有答案讨论中等方法,这是相对简单的。

+0

我应该在RNG_Seed中放什么? –