2017-05-14 83 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中放什麼? –