简体   繁体   中英

how to make a delay in assembly

I'm trying to create a program that generate 4 random numbers, insert them into array and then print them, but the problem is that it insert always the same numbers because I generate the number with the clock and it's too fast, this is my code:

    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

I think that the problem is here:

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

This is a classic XY problem . Your problem is that you're not getting good randomly-generated numbers, so you think the solution is to insert a delay. That is not a good solution.

First of all, it won't really solve your problem. The system clock is not a random number generator. There is nothing "random" about the time. The only thing that the time is useful for is as a "seed" for a random number generator. A "seed" is just the starting value. You use it only once, when your application first starts up, to prime the random number generator. You don't use the time each time you need a random number, so it doesn't matter what the actual time is.

The second reason why inserting a delay is not a good solution is because there's no reliable way to insert a delay. Programmers used to do things like this back in the '80s, and that's why many old games won't run on modern machines: they had hard-coded timing loops that made empirical assumptions about the performance of the underlying processor. There is one reliable way to insert a delay by programming the timer interrupt, but that doesn't work under operating systems, so it's generally a non-starter today.

The real solution here is to use a better random-number generator. Every C standard library provides one, but if you don't have a C standard library handy (or don't want to link to one), then it isn't that hard to write one yourself in assembly.

ANSI C essentially suggests the following implementation for a random number generator:

extern unsigned long seed;

return ((seed = seed * 1103515245 + 12345) >> 16) & 0x7FFF;

and that's what most C standard libraries have traditionally used. It's not horrible, but it's not great, either. The biggest problem with it is that it returns only 15 bits. If we're writing it in assembly, we can make use of the 64-bit intermediates generated by 32-bit integer multiplication and extend the result to 32 bits instead. This results in an extremely fast PRNG implementation, capable of generation millions of values per second, and its "randomness" is actually pretty solid. It passes a simple chi-square test, and when you use it to plot a dot pattern, things "look random".

Here's what the code would look like:

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

To use it, store the current time into RNG_Seed when your application starts up. From then on, just call the GenerateRandNum procedure, and it will return a 32-bit random number in EAX . Don't touch RNG_Seed again (it is maintained internally by GenerateRandNum ).

There are better pseudo-random-number generators out there, but this one should be more than good enough.

Of course, after I write this answer, I notice that you are using 16-bit assembly. If that implies you're targeting a processor older than the 386 (such as the 8088/8086), you will not be able to do 32-bit multiplication. In that case, you would probably just go back to the C implementation, with its 15-bit result:

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

You can find plenty more discussion about pseudo-random number generators on Wikipedia , including some links to alternative implementations. Or see the question vitsoft linked, which has answers discussing the middle-square method, which is relatively simple.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM