如何延迟装配

时间:2017-05-14 13:22:31

标签: assembly

我正在尝试创建一个生成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

1 个答案:

答案 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,它有讨论中间方法的答案,这个方法相对简单。