logo To Foot
© J R Stockton, ≥ 2004-12-13

Borland Pascal Wait and Delay.

No-Frame * Framed Index * Frame This
Links within this site :-

Parts of this apply also to Delphi.

This tends to assume DOS or a Win3/Win9x DOS box; I have no experience of Win 2000, ME, NT.

2001-01-16 : This page, which contains material more relevant to interval and delay, has been split from Borland Pascal Time and Date, which contains material more related to absolute time and date. They should be read together.

Wait Code

While Crt.Delay is convenient when it works, and while corrections and substitutes are available (see below), often millisecond resolution is not necessary and the DOS timer can be used directly.

DOS-based Delay Routines

The immediately following delay routines loop counting CHANGES in the byte at Seg0040:$006C; without HLT, they hog the CPU. PT18 is a pointer, or a ^longint, already set to contain Seg0040:$006C.

   procedure WaitTix(Tix : integer) {MSDOS/DPMI} ;
   var x46C : byte ; PB18 : ^byte absolute PT18 ;
   begin x46C := PB18^ ;
     repeat
       if x46C<>PB18^ then begin x46C := PB18^ ; Dec(Tix) end ;
       until Tix<0 end {WaitTix} ;

Probably OK.

Or

   procedure Wait(C : integer) ;
   var T : byte ; Tkr : byte absolute $40:$6C ;
   begin repeat
     T := Tkr ; repeat until T<>Tkr ; Dec(C) until C<=0 end {Wait} ;

Or (*)

   procedure Wait(C : word) ;
   var T : byte ; Tkr : byte absolute $40:$6C ;
   begin while C>0 do begin T := Tkr ; repeat until T<>Tkr ;
     Dec(C) end end {Wait} ;

To work in approximate milliseconds, first do   C := C div 55 ;.

N.B. change-counting methods may give extended delays if another process causes change steps to be missed.

Someone suggested, in news:  asm mov cx,18*[delayseconds] ; HLT ; loop $-1 end ; I now think that it will delay for that number of interrupts rather than of timer interrupts.

Rudolf Polzer has code like that after (*) above; but inserts, after "repeat", "asm hlt end ;" to release the CPU until an interrupt occurs :-

   procedure dlay(n : integer) ;
   const t : byte = 0 { static! saves some stack space } ;
   var timer : byte absolute $0040:$006C ;
   begin while n > 0 do begin
       t := timer ;
       repeat  asm  hlt  end  until timer<>t ;
       Dec(n) ;
       end end ;

The purpose of HLT is to "give back processor cycles on multitasking OSs, reduce CPU power consumption on notebooks under plain DOS. HLT powers down the processor until an interrupt (for example the timer interrupt) occurs. Then the interrupt routine is called (and increments the timer variable) and then the TP program gets control and compares timer with t. If timer = t, another interrupt (eg serial) has occured." It's reported (LeeJ) that dlay "crashes under win2000/nt"; any ideas?

RP also wrote : "I also used the HLT trick for something else :-

   repeat
     t := timer ;
     asm  hlt  end ;
     until timer = t ;

stops exactly when an interrupt occurs that is NOT the timer interrupt. I used this in a screen saver to catch mouse movements without using the driver itself."

FP has cited, to yield time to other processes (see Ralf Brown's List) :-

  push ax ; mov ax,1680h ; int 2fh ; pop ax

Michael Glover, in news:c.l.p.b, has given the following method, derived from the above and slightly modified by me (to be tested) :-

  function KWAIT(SecsDelay : integer) : integer ;
  var Tptr : ^longint ; Finish : longint ; W : word ; Ch : byte ;
  begin
    KWAIT := -1 {default} ;
    Tptr := Ptr(Seg0040, $006C);
    Finish := Tptr^ + Round(18.2*SecsDelay) ;
    repeat
      asm  push ax ; mov ax,1680h ; int 2fh ; pop ax  end { free CPU } ;
      if KeyPressed then begin
         Ch := Ord(ReadKey) ;
         if Ch=0 then KWAIT := 256+Ord(ReadKey) else KWAIT := Ch ;
         EXIT ;
       end ;
    until Tptr^ > Finish ;
  end {KWAIT} ;

Small Delays

There is a visible register in the CTC (PIT} (Ports $40..$43) normally counting down at 65536×18.2 Hz = 1.193 MHz but possibly two steps at once; this should be usable for small delays in a DOS program. One must read "The Timing FAQ" Sec.7 very carefully before using the CTC; it may be better to commandeer the speaker channel, #2.

The SmallWait code in int_test.pas appears to work.

Very Small Delays

In an article of Sun, 19 Jan 1997 in comp.lang.pascal.borland, Martin Lafferty (robots@enterprise.net) wrote :-

I find the best way to get a small machine independent delay is to do a harmless read from something on the ISA bus. Reading a byte port takes about a microsecond, I reckon. I suspect I have run into trouble mixing dummy loops and port reads, as the pentium can get clever and do the port read before it finishes the loop!

e.g. This can fail, even if BigNumber is really big, because the second read can occur before the loop has completed.

    something := Port[somewhere] ;
    for i := 0 to BigNumber do {hardware requires delay in here}
       Junk := 1 ;
    somethingElse := Port[somewhereelse] ;

I do it like this

    something := Port[somewhere] ;
    for i := 0 to RelativelySmallNumber do {hardware requires delay in here}
       Junk := Port[HarmlessReadonISABus] ;
    somethingElse := Port[somewhereelse] ;

Long Delays

For a long delay, one might count 1 Hz cycles of the UIP bit of the RTC; this should be reasonably accurate.

Another

Untested by me - SuperDelay delay unit for Turbo Pascal 7.0, Allen Cheng :-
"SuperDelay is a pascal unit of Extremely accurate Delay Routines. It reprograms the timer chip to take microsecond resolution, and it is 200-300 times more accurate than the Crt.Delay routine. It'll be more accurate if running on a faster computer."

Windows Code

Untested by JRS as yet.

Adapted from a post and a message from Michael Glover, Surrey, UK, referring to a simple wait loop in TPW :-

>Try Start:=GetTickCount;
>Repeat Until GetTickCount>=Start+Time;

Not a good idea, since the application in question hogs the system. Better to yield. Try the following :-

procedure Delay(Numsec : integer) ;
var EndTime : LongInt ; Message : TMsg ;
begin EndTime := GetTickCount + NumSec*1000 ;
  repeat
    if GetMessage(Message, 0, 0, 0) then begin
      TranslateMessage(Message) ; DispatchMessage(Message) end ;
    until GetTickCount > EndTime ;
  { In Delphi, the repeat loop should simply be :-
    repeat ProcessAppMessages ; until ... ; }
  end {Delay} ;

Ing. Franz Glaser wrote :-

procedure Delay(ms : LongInt) ;
var TickCount : LongInt ; M : TMsg ;
begin
  TickCount := GetTickCount;
  while GetTickCount - TickCount < ms do
    if PeekMessage(M,0,0,0,pm_Remove) then
      begin TranslateMessage(M) ; DispatchMessage(M) end ;
  end ;

I read that the Borland site has, for 32-bit work :-

procedure Delay(ms : longint) ;
var TheTime : LongInt;
begin
  TheTime := GetTickCount + ms ;
  while GetTickCount < TheTime do Application.ProcessMessages ;
end ;

Windows GetTickCount

See in my Delphi Programming, and in Win32 Help.

Sleep

If the version of Windows in use has a Sleep function, then that should be the thing to call.

JCO'C has, for Win16 (a non-preemptive OS), recommended the use of settimer/WM_TIMER.

Delphi

In Delphi, consider Sleep.

Visual Delays

If the delay is for animation purposes, it may be well to sync it to the vertical retrace, at around 70 Hz (mode-dependent).

I have seen something like the following suggested (untested) :-

procedure WaitRetrace ; assembler ;
  asm  mov dx,3DAh { read COLOUR MODE VGA input status register #0 }
  @@loop1: in al,dx ; and  al,8 ; jnz @@loop1 ;
  @@loop2: in al,dx ; and  al,8 ; jz  @@loop2  end ;

Timing

Discussions of timing and delays can be found in Prof. Salmi's TurboPascal FAQ, in Kris Heidenstrom's Timing FAQ, and in the newsgroup comp.lang.pascal.borland. Try a Google or AltaVista search on "Frank Heckenbach", though older material may now be inaccessible. See links on my other pages, including in PC Links Reference.

My INT70AT2.PAS shows the use of the RTC to generate interrupts at a high, programmable rate, without affecting the 18.2 Hz ticker. This could be a useful tool for generating short delays.

For using the RDTSC instruction, which accesses a counter at CPU clock rate (it needs a Pentium or better), see Delphi Programming.

The Crt.Delay Procedure

The standard Borland Crt.Delay procedure uses a count-loop calibrated during program startup, employing the 18.2 Hz DOS clock signal to give a standard interval and using the same loop with this calibration when Delay is running. It cannot be accurate unless the program "owns" the machine (see Misuse of Crt.Delay).

The Crt.Delay method assumes constant CPU speed; I hear that some systems now lower the speed of the CPU if it gets too hot.

Putting "Crt" in any "uses" statement in the program will invoke the calibration, as the initialisation section always calibrates for Delay at start-up, whether or not Delay itself is called.

Therefore, if this calibration fails, it is necessary to do at least one of :-
  (1) avoid the unadjusted Borland Crt unit;
  (2) avoid needing Delay, or adjust Delay parameters;
  (3) replace the unit;
  (4) patch the unit;
  (5) patch the program.

See also Startup Overflow Bugs below (which includes a link for TP7/BP7 RunTime Error 200, and refers to speed errors with earlier versions).

Misuse of Crt.Delay

Note that the method used by Crt.Delay assumes that the machine is, at run-time, dedicated to the main thread of the program. This is an unsafe assumption under Windows, for example; at program start-up Windows may still be busy shuffling things about, so the CPU seems slower at calibrate-time. In a modern PC, there may be other processes stealing CPU time; display, battery-monitor, ...

I'd say that the real cause of such errors was running a program designed to "own" the PC in an environment in which it did not.

Try the following program, which I compiled with TurboPascal 5.0 in c:\TEMP\ in a DOS box under WfWg3.11 :-

uses Crt ;
var T1, T2 : word ;
begin
repeat
  Sound(1000) ; Delay(100) ; Nosound ; Delay(900) ;
  T2 := MemW[$40:$6C] ; Writeln(T2-T1:7) ; T1 := T2 ;
  until Keypressed ;
end.

On its own, that should hoot at 1 Hz and print 18s (with 20% 19s). On my 486DX33 with a 1996 1GB hard disc, when running in a Window, it hoots faster and prints 13±1; however, with the mouse speeding in circles around its pad the program slows to 21. Full-screen, it hoots even faster and prints 9±1. I EXPECT that it would give 18/19 in a Windows-free system.

Another Delay Routine

The following (untested) is independent of Crt, and may give better resolution :-

  procedure AnotherDelay(ms : word) ; assembler ;
  asm  mov ax,1000 ; mul ms ; mov cx,dx ; mov dx,ax ;
    mov ah,$86 ; int $15  end ;

Int 15/86 may be intended for operating system use only - consider consequences.

More on Delays

Moslo will slow down a PC, and may help.

Startup Overflow Bugs

With more recent processors, the delay calibration count in Borland's Crt unit may be large enough to cause errors.

Versions before TP7

Programs compiled with older versions of TP (... TP5, TP5.5, TP6) can miscalibrate, without reporting any error, when the 16-bit counter overflows during startup - AFAIR, this occurred with a 486DX33, but not with a 286-12. I've heard that "the limit was about 486/25 or perhaps 386/40 with cache". There was a TP6 patch (from Borland?); TP7 & BP7 are OK for this bug. Refer to Timo Salmi's Pascal FAQ, article #67. AIUI, Pedt Scragg's Crt unit will fix this problem.

TP3

2001-02-17 : In News (RAS) : "Turbo 3 has a Delay bug similar to the BP7 runtime error 200 on 180mz/200mz Pentium Pro's and above. I had to write a NewDelay function that worked off a timer and discontinue using Delay."

RTE200 with TP7 & BP7

DOS and DPMI (Real and Protected) mode programs, when using the Crt unit supplied by Borland in their BP7/TP7 library file, give, on many CPUs over about 200 MHz, Runtime Error 200 during program startup; many fixes are available. It is known that at least one of these fixes fails at 450-500 MHz.

Pedt Scragg has a trustworthy replacement Crt unit, available via PDCU and at Garbo. Be sure to install it correctly, with TPUMOVER.

Some of the fixes for Runtime Error 200 at program startup make the Crt.Delay procedure give shorter delays.

TPW; BP & BPW

Windows mode programs, with or without WinCrt, are not affected, since Delay is not available.

Other

Home Page
Mail: no HTML
© Dr J R Stockton, near London, UK.
All Rights Reserved.
These pages are tested mainly with Firefox and W3's Tidy.
This site, , is maintained by me.
Head.