Board index » delphi » TP 7.0 and [GS]etTime

TP 7.0 and [GS]etTime

I'm working on a simple program to estimate the error in the time of the
PC clock based on the measured drift of the clock time, and adjust the
time accordingly. In the first tests I found that SetTime/GetTime behave
unexpectedly (at least for me). I used the following test program (TP
7.0, DOS 6.2):

program test ;
uses DOS ;

var
  Hour , Minute , Second , Sec100 : Word ;
  NHour, NMinute, NSecond, NSec100: Word ;
  NLess, NMore: Integer ;
  I: Integer ;

begin
  NLess:= 0 ;
  NMore:= 0 ;

  for I:= 0 to 9999 do
   begin
    GetTime( Hour, Minute, Second, Sec100 ) ;
    SetTime( Hour, Minute, Second, Sec100 ) ;  { Should be a no-op? }
    GetTime( NHour, NMinute, NSecond, NSec100 ) ;
    if NSecond = Second then
      if NSec100 < Sec100 then
        Inc( NLess )
      else
        Inc( NMore ) ;
   end ;  { of for }

  WriteLn( NLess, ' ', NMore ) ;
end.

In most of the tests, NLess will be about 9500. I would expect NLess to
be zero! As a result of running this program, the clock will be set back
for about 9 minutes.

Can anyone comment on this behaviour of SetTime?

Regards,
  Wim Nelis.

 

Re:TP 7.0 and [GS]etTime


On 26 Mar 1998 08:50:35 GMT, ne...@nlr.nl (nelis w.j.m.) wrote:

Hi Win,

First of all, the 'original' Borland source code for GetTime(), and
SetTime():

PROCEDURE GetTime(VAR Hour,Minute,Second,Sec100:WORD); ASSEMBLER;
{GetTime returns the current time set in the operating system.
 Ranges of the values returned are: Hour 0-23, Minute 0-59,
 Second 0-59 and Sec100 (hundredths of seconds) 0-99.}
ASM
  MOV   AH,2CH
  INT   21H
  XOR   AH,AH
  MOV   AL,DL
  LES   DI,Sec100
  STOSW
  MOV   AL,DH
  LES   DI,Second
  STOSW
  MOV   AL,CL
  LES   DI,Minute
  STOSW
  MOV   AL,CH
  LES   DI,Hour
  STOSW
END; {GetTime}

PROCEDURE SetTime(Hour,Minute,Second,Sec100:WORD); ASSEMBLER;
{SetTime sets the time in the operating system. Valid
 parameter ranges are: Hour 0-23, Minute 0-59, Second 0-59 and
 Sec100 (hundredths of seconds) 0-99. If the time is not
 valid, the function call is ignored.}
ASM
  MOV   CH, BYTE PTR Hour
  MOV   CL, BYTE PTR Minute
  MOV   DH, BYTE PTR Second
  MOV   DL, BYTE PTR Sec100
  MOV   AH,2DH
  INT   21H
END; {SetTime}

As you can see here, your compiler has little to do with the strange
behaviour of your program. All it does is give you access to two BIOS
calls. Let's take a closer look at these BIOS calls, there is nowadays
quite a lot of information available about them.

These bios calls use these two memory locations when the 'time of the
day' is present:

MemW[$0040:$006E]; {most significant word}
MemW[$0040:$006C]; {least significant word}

In short; there is a LONGINT there counting how many interrupts the
Intel 8253 programmable interval timer has generated since midnight
(00:00). The 8253 updates this LONGINT at a rate of about 18.21 Hz.

From this it is clear that the BIOS calls will have to convert the
four register values which represent time into this one LONGINT for
SetTime(), and visa-versa for GetTime().

You can now almost guess what the problem is; the forward conversion
is not exactly the reverse function of the backward conversion. This
is quite tolerable because 1 tick only represent about 55 ms, and
human beings simply aren't able to detect such small differences in
time. In general this could be called 'a rounding off error' in the
BIOS, though I wouldn't call it an error. The main reason for this
behaviour is probably the higher efficiency of the algorithms now
involved.

My advise; don't use GetTime() and SetTime() repeatedly like you did,
they were not designed for that purpose. If you need to access the
timer often you should use the given memory locations and write your
own conversion routines.

If you want to estimated the drift of the 8253 timer you will need to
access something that is independent from the Intel 8248 main clock
generator because the 8253's clock pulse comes from this chip. In each
AT there's a real-time clock (RTC; MC 146818) chip which does indeed
run independently of the 8248. You can find information on how to
access this RTC on the web. The question remains of course where the
drift occurs; in the 8253 or in the MC 146818. It's highly likely that
they both drift.

Peter de Jong
wpdej...@worldonline.nl

(PS: note that the chips I mention probably aren't present in your
computer, but because the PC design is backward compatible you can act
as if they were)

Quote
>I'm working on a simple program to estimate the error in the time of the
>PC clock based on the measured drift of the clock time, and adjust the
>time accordingly. In the first tests I found that SetTime/GetTime behave
>unexpectedly (at least for me). I used the following test program (TP
>7.0, DOS 6.2):

>program test ;
>uses DOS ;

>var
>  Hour , Minute , Second , Sec100 : Word ;
>  NHour, NMinute, NSecond, NSec100: Word ;
>  NLess, NMore: Integer ;
>  I: Integer ;

>begin
>  NLess:= 0 ;
>  NMore:= 0 ;

>  for I:= 0 to 9999 do
>   begin
>    GetTime( Hour, Minute, Second, Sec100 ) ;
>    SetTime( Hour, Minute, Second, Sec100 ) ;  { Should be a no-op? }
>    GetTime( NHour, NMinute, NSecond, NSec100 ) ;
>    if NSecond = Second then
>      if NSec100 < Sec100 then
>        Inc( NLess )
>      else
>        Inc( NMore ) ;
>   end ;  { of for }

>  WriteLn( NLess, ' ', NMore ) ;
>end.

>In most of the tests, NLess will be about 9500. I would expect NLess to
>be zero! As a result of running this program, the clock will be set back
>for about 9 minutes.

>Can anyone comment on this behaviour of SetTime?

>Regards,
>  Wim Nelis.

Re:TP 7.0 and [GS]etTime


JRS:  In article <6fd4sr$rh...@simian.nlr.nl> of Thu, 26 Mar 1998
08:50:35 in comp.lang.pascal.borland, "nelis w.j.m." <ne...@nlr.nl>
wrote:

Quote
>I'm working on a simple program to estimate the error in the time of the
>PC clock based on the measured drift of the clock time, and adjust the
>time accordingly. In the first tests I found that SetTime/GetTime behave
>unexpectedly (at least for me). I used the following test program (TP
>7.0, DOS 6.2):

>program test ;
>uses DOS ;

>var
>  Hour , Minute , Second , Sec100 : Word ;
>  NHour, NMinute, NSecond, NSec100: Word ;
>  NLess, NMore: Integer ;
>  I: Integer ;

>begin
>  NLess:= 0 ;
>  NMore:= 0 ;

>  for I:= 0 to 9999 do
>   begin
>    GetTime( Hour, Minute, Second, Sec100 ) ;
>    SetTime( Hour, Minute, Second, Sec100 ) ;  { Should be a no-op? }
>    GetTime( NHour, NMinute, NSecond, NSec100 ) ;
>    if NSecond = Second then
>      if NSec100 < Sec100 then
>        Inc( NLess )
>      else
>        Inc( NMore ) ;
>   end ;  { of for }

>  WriteLn( NLess, ' ', NMore ) ;
>end.

>In most of the tests, NLess will be about 9500. I would expect NLess to
>be zero! As a result of running this program, the clock will be set back
>for about 9 minutes.

>Can anyone comment on this behaviour of SetTime?

You do have the problem of the excluded middle; there ought to be an
NEqual in between NLess and NMore.

But ISTM that you have not realised that the time routines are merely a
window on an 18.2Hz clock (longint $40:$6C), and so are subject to
rounding errors.  Moreover, as the clock is of poor accuracy, I expect
that Borland or Microsoft went to no trouble to optimise the rounding.

(( test ... BP7 De{*word*81} shows that GetTime just calls Int21/2C, so the
   rounding is Microsoft's. ))

You should anyway expect NLess>0 (slightly), because a tick occurring
between Get & Set will be nullified.

My  http://www.merlyn.demon.co.uk/pas-time.htm  may help you understand.

--
John Stockton, Surrey, UK.    j...@merlyn.demon.co.uk    Turnpike v1.12    MIME.
  Web <URL: http://www.merlyn.demon.co.uk/> - FAQqish topics, acronyms & links.
  Correct 4-line sig separator is as above, a line comprising "-- " (SoRFC1036)
  Do not Mail News to me.    Before a reply, quote with ">" or "> " (SoRFC1036)

Re:TP 7.0 and [GS]etTime


On Fri, 27 Mar 1998 12:36:55 +0000, Dr John Stockton

Quote
<j...@merlyn.demon.co.uk> wrote:
>JRS:  In article <6fd4sr$rh...@simian.nlr.nl> of Thu, 26 Mar 1998
>08:50:35 in comp.lang.pascal.borland, "nelis w.j.m." <ne...@nlr.nl>
>wrote:
>>I'm working on a simple program to estimate the error in the time of the
>>PC clock based on the measured drift of the clock time, and adjust the
>>time accordingly. In the first tests I found that SetTime/GetTime behave
>>unexpectedly (at least for me). I used the following test program (TP
>>7.0, DOS 6.2):

>>program test ;
>>uses DOS ;

>>var
>>  Hour , Minute , Second , Sec100 : Word ;
>>  NHour, NMinute, NSecond, NSec100: Word ;
>>  NLess, NMore: Integer ;
>>  I: Integer ;

>>begin
>>  NLess:= 0 ;
>>  NMore:= 0 ;

>>  for I:= 0 to 9999 do
>>   begin
>>    GetTime( Hour, Minute, Second, Sec100 ) ;
>>    SetTime( Hour, Minute, Second, Sec100 ) ;  { Should be a no-op? }
>>    GetTime( NHour, NMinute, NSecond, NSec100 ) ;
>>    if NSecond = Second then
>>      if NSec100 < Sec100 then
>>        Inc( NLess )
>>      else
>>        Inc( NMore ) ;
>>   end ;  { of for }

>>  WriteLn( NLess, ' ', NMore ) ;
>>end.

>>In most of the tests, NLess will be about 9500. I would expect NLess to
>>be zero! As a result of running this program, the clock will be set back
>>for about 9 minutes.

>>Can anyone comment on this behaviour of SetTime?

>You do have the problem of the excluded middle; there ought to be an
>NEqual in between NLess and NMore.

>But ISTM that you have not realised that the time routines are merely a
>window on an 18.2Hz clock (longint $40:$6C), and so are subject to
>rounding errors.  Moreover, as the clock is of poor accuracy, I expect
>that Borland or Microsoft went to no trouble to optimise the rounding.

>(( test ... BP7 De{*word*81} shows that GetTime just calls Int21/2C, so the
>   rounding is Microsoft's. ))

>You should anyway expect NLess>0 (slightly), because a tick occurring
>between Get & Set will be nullified.

I think there is more to it. A simple test (delete the settime
statement temporarily and add a variable NEql for the Nsec100 = Sec100
case, shows the following results on my system (P120)
NLess: 0% (as expected, the clok should not run backwards)
NMore: 1%
MEql: 99%

NMore is only incremented if the clokctick occurs somewhere between
the first and second GetTime (Yes, code is executed between the
execution of the GetTime instructions, if only the epilogue of the
first and the prologue of the latter).

Second test: Insert the SetTime statement again, and run the program a
couple of times.
Results:
NLess: 5% (That's funny...)
NMore: 2%
NEql: 93%

Debugging shows that Inc (NEql) only occurs when NSec100 = Sec100 = 0
and Inc (NMore) always with NSec100 = 5 and Sec100 = 0.

Conclusion: SetTime ignores the value of sec100, and just resets the
value for hundredths of seconds. I would call that 'Not optimising
rounding' indeed.

This would also explain why the PC clock was set back. Effectively the
only thing this program does is stop the time (more or less).

IMO, the best thing to do is: ignore the Sec100 value.

Regards,
Klaas

------
This sigfile intentionally left blank

Re:TP 7.0 and [GS]etTime


JRS:  In article <351eb385.3936...@news.compuserve.com> of Sun, 29
Mar 1998 21:25:33 in comp.lang.pascal.borland, Klaas de Jong

Quote
<106401.1...@compuserve.com> wrote:
> ...
>IMO, the best thing to do is: ignore the Sec100 value.

By the way, as I recall, it may well be that the battery-backed
calendar clock, which runs permanently, is not so bad, but the MSDOS
clock at $40:$6C (loaded from the calendar clock at boot time) runs
at an inaccurate rate.

If you only need to correct the DOS clock while the PC is running,
and can accept the calendar one, then there's no need to use
GetTime/SetTime, but just access the longint at $40:$6C (atomically,
or with checks).  When I had a system which read Network time at
boot, but with delays, so that it always set itself a few seconds
slow, I wrote code to increment the value by a few * 18.2.

--
John Stockton, Surrey, UK.    j...@merlyn.demon.co.uk    Turnpike v1.12    MIME.
  Web <URL: http://www.merlyn.demon.co.uk/> - FAQqish topics, acronyms & links.
  Correct 4-line sig separator is as above, a line comprising "-- " (SoRFC1036)
  Do not Mail News to me.    Before a reply, quote with ">" or "> " (SoRFC1036)

Other Threads