• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 2404
  • Last Modified:

1 sec. delay loop

Hello Experts ! I need very quick help to my very easy :) question. How can I write a loop to delay appx. 1 second on a moderate Pentium IV PC. I have been searching in the forum and googling a couple of hours but couldn't find an appropriate code, or my Assembler (NASM) is not willing to compile it ! Please help.. Thanx in advance..
  • 6
  • 3
  • 3
  • +5
5 Solutions
What kind of operating system is your target? Can you handle the timer tick interrupt?
;  for approx 1 second

        mov    ax,1000  ; adjust this constant up or down as needed to get closer to 1 second

wta:     mov    cx,65000

wt:    nop
        loop   wt

         dec   ax
         jnz   wta

grg99, do you believe this will work on a P4 (as asked in the original question)? P4s have very nice stuff to optimize this.

Furthermore, this kind of looping is VERY processor-dependent: It may give you a 1 second wait on a P4 1.5 GHz, but will give you less than 450 milliseconds on a P4 3.4 GHz. Even more, on variable-speed CPUs (SpeedStep, LongRun, etc.) used in mobile computers, this loop may take 5 seconds or more because this simple loop enables the CPU to stay in the low-speed mode. [These all presume you have a loop that is not optimized off by the CPU which is clearly not the case with the above code.]

If this code runs in a multi-tasking environment, the delay may also be lengthened.

All in all, I believe that knowing the target OS we can supply him/her with a much better solution.
Keep up with what's happening at Experts Exchange!

Sign up to receive Decoded, a new monthly digest with product updates, feature release info, continuing education opportunities, and more.

yes, I know.

On a DOS thru Windows NT system, from a 16-bit program, he can peek at $40:$6C which is a longint that (mostly) increments about 18.2 times a second.

In Windows, there's API's like GetTickCount that return milliseconds since bootup.

There's always RDTSC, which counts CPU clock cycles, but to get a second you have to know the CPU frequency.

something like:

Million EQU 1000000


       add EAX,CPUCYCLESPERSECOND   ;  target
       adc  EDX,0                                  ; carry
      mov   ESI,EAX
      mov   EDI,EDX

        cmp    EDX,EDI     ; are high 32 bits right yet?
        JNE    LP              ; no

        cmp     EAX,ESI    ; hi correct, how about low
        JB      LP              ; not yet

grg99, this is exactly why I've asked for the OS in my first comment. :-) But he/she seems to forget about his/her question.
If you have to write a loop in Aseembly language, i.e. not in high level language and not using timer, what I would do for convenience is

- on any other PC with same processor frequency, using linux or windows

- write a command line tool doing a loop, and evaluating time using system calls (not interrupted by windowing system)
   (the only difference will be the task switching time, insignificant if this is the only active application)

#include <sys/time.h>
struct timeval tt;

void start() {   gettimeofday(&tt, NULL);  }
unsigned long end() {  struct timeval t;  gettimeofday (&t, NULL);  return (t.tv_sec*1000 + t.tv_usec/1000) - (tt.tv_sec*1000 + tt.tv_usec/1000); }

     int i,z;
     start ();
     for (i=z=0 ; i<10000000 ; i++) z += i;
     printf ("Took %ld ms\n", end());

Affine the 100000000 to get 1000 ms.

- once you are ok, if you used gcc to compile your program do

    gcc -S myprog.c

to get myprog.s (assembly)

E.g. on my pc it gave exactly 1000 ms. (have to ensure stack adressing is ok :)

        movl    $0, -8(%ebp)
        movl    $0, -4(%ebp)
        cmpl    $99999999, -4(%ebp)
        jle     L7
        jmp     L5
        movl    -4(%ebp), %eax
        leal    -8(%ebp), %edx
        addl    %eax, (%edx)
        leal    -4(%ebp), %eax
        incl    (%eax)
        jmp     L4
SADIKAuthor Commented:
Well, thank you for those quick responses.. they were really quicker than I expected.. My OS is W2K Server which will be a stand alone server running only IIS and the small program that will send some sort of signal to the parallel port by 1 sec delays.. I am quite a beginner so if you can help to write a processor free code, it'll help a lot.. I don't know how to use time ticker.. Pls. be clear and concise.. Thanx again..
Must it be in assembly?

Btw, you can call the Sleep system function from assembly. You have to link your call to "_Sleep@4", pass the desired value in ECX (the wait time in microseconds) and call into kernel32.dll. (The same applies to the "sleep" C runtime function within msvcrt.dll)
If you like this idea, we can supply you with the code.

And as another option, if you think you've find a suitable code while Googling, we can help you to get it compiled with NASM.
Then the best way is to call Sleep() as joghurt has suggested.

SADIKAuthor Commented:
Yes joghurt can you write down the code you mentioned ? Please make it clear for me to understand.. My current code is like this :

       PUSH DX
       MOV DX,0378H
       MOV AL,01H
       OUT DX,AL
      CALL Delay1     ;One second delay
       MOV AL,02H
       OUT DX,AL
      CALL Delay1     ;One second delay
       MOV AL,04H
       OUT DX,AL
      CALL Delay1     ;One second delay
       MOV AL,08H
       OUT DX,AL
       POP DX

; This will loop for 1 Sec
Delay1:    ????

P.S. joghurt, do u think NASM is a good compiler? If not can u suggest an other one?
I use a commercial compiler so it's not the question of being better. (But anyways, it's better than NASM, I think.) If you are interested in free compilers only, you can find some out there, such as Go-Asm, or versions of GCC. However, I personally don't have much experience with them, except gcc on Linux. But you can find many questions here on EE asking about the best (free) compiler.
SADIKAuthor Commented:
Thanks joghurt.. So what about my code ??
There's a tutorial about calling WinAPI functions from assembly:
You can also get some help from here:
(They are for MASM but I think you can get the idea from there for NASM).

The function you need to call is "Sleep" and it's in kernel32.dll.
>>   MOV DX,0378H
>>   MOV AL,01H
>>   OUT DX,AL

Have you tried this?  With Windows, the OUT instruction is a protected opcode.  Generally, only device drivers talk directly to the hardware this way.

From that code, it appears that you are sending data to the LPT1 printer port.  It may make more sense to avoid ASM altogether and use C++ code to access Win32 API functions to do that.

-- Dan
If you want a delay, dont use the loops by counting up to some number because delay always changes with speed of the processor.
Use the system time:

mov bx,0
mov ah,2ch
int 21h
mov cl,dh
push cx
mov ah,2ch
int 21h
pop cx
dec dh
cmp dh,cl
jng L1

Ok this routine always delays 1~2 sec.
If you delete the line "dec dh", then it waits 0~1 sec.
Hope you find it usable..
(Bu arada Türkiye'den misin?) :)

Can you not just call the following?

pushl      $1
call      sleep

you can change the amount of time by changing the value you push onto stack...
in windows you'll need to push the number of milliseconds you want e.g.

pushl      $1000
call         sleep
i wrote a program for this just the other day....this uses the system clock so it should be pretty accurate
i used tasm 5.1 (i think) to assemble this, it should be realitivly easy to convert:

.model medium
.386                      ;80386 instruction set
.stack 100h            ;256 bytes
.data                     ;Data segment same as:  segment para public 'data'

timer        dw  ?      ;hold initial time
time         dw  1h     ;time= 1 second
time18      dw  ?       ; this holds the computed seconds which have been translated into ticks
ticks         dw  0h      ; ticks-can be used to time less than a second, normally 18.2 ticks in a second

.code                       ; segment para public 'code'

;--------Time delay procedure-----------

    ;Purpose:  waits specified time in seconds + ticks
    ;IN:          TIME variable with time in seconds
    ;              TICKS variable with ticks (18.2 ticks per second)
    ;OUT:       Nothing

timedelay proc

mov  ax,time                 ;load ax with time in seconds
mov  cx,18                    ;get ready to multiply ax by cx
mul   cx                        ;ax now = time * 18
mov   time18,ax            ;put in appropriatly named variable
cmp   ticks,0h                ;this will add ticks to seconds if there is any
jne    settick                  ;small jump and back if we need to add ticks in
xor    ax,ax                    ;ax=0
int     1ah                      ;TIMER intterupt- gets time elapsed since midnight stores them to  cx:dx
mov   timer,dx               ;put initial time into timer variable (we only need dx cause its a short time measure)
xor     ax,ax                   ;ax=0  just precautions
xor     dx,dx                   ;dx=0
xor     cx,cx                   ;cx=0
int      1ah                     ;TIMER intterupt- gets time elapsed since midnight stores them to  cx:dx
sub     dx,[timer]           ;has the new time in dx and sees it original time-new time = time you want to wait
cmp    dx,[time18]         ;compares total elapsed ticks to your wanted time
jne     timeit                  ;if its not equal then loop back and wait till it is
jmp    endtimedelay       ;if it is equal jump to exit procedure

add     ax,ticks               ;adds ticks to the seconds
mov    time18,ax            ;moves it to appropriate variable
jmp    getinit                 ;jump back to finish timing

ret                               ;pop cs:ip and return to code
endp                            ;end procedure
;-----------------------end of time delay procedure-----------------


mov     time,1h             ;move 1 second to the time you want to wait variable
mov     ticks,0h             ;dont need any ticks (you could use 18 ihere nstead of 1 in the seconds variable (TIME)
call      timedelay          ;call procedure to wait timedelay

mov      ax,4c00h          ; get ready to terminate program (dont need this to run time delay proc, only to exit program)
int        21h                  ; call DOS intterupt- terminate program
end      start                 ; the end
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Featured Post

Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

  • 6
  • 3
  • 3
  • +5
Tackle projects and never again get stuck behind a technical roadblock.
Join Now