Mail Archives: djgpp-workers/2002/12/07/00:53:43
This was discussed a while ago and I cannot remember if there was a solution
proposed, except one I worked on. Below is a modified delay.c that works on
XP. It does have some problems in that it is only accuate to approx 27
milliseconds as the TOD tick is updated every 54.9 millisends and I do a
manual rounding check in the code in order to try to make the delay a bit
more accurate.
I have ran it through some tests and it is 1000% better than the exisitng
code, here is an output from a sample test run:-
DJGPP_204 C:\dj204\testing>delay 500 500 500 500 250 250 250 250 1000 1000
Time = 07Dec2002 16:45:03
Wait for 500 milli seconds.
Wait for 500 milli seconds.
Wait for 500 milli seconds.
Wait for 500 milli seconds.
Wait for 250 milli seconds.
Wait for 250 milli seconds.
Wait for 250 milli seconds.
Wait for 250 milli seconds.
Wait for 1000 milli seconds.
Wait for 1000 milli seconds.
Time = 07Dec2002 16:45:08
I have not created a patch for this as this may not the final solution and
the full file is easier to read and it's small.
Does anyone have any other suggestions to try to put forward a solution for
Win 2000 and XP only. I will need to test them to see if they work.
Anyone got any problems with the code below? I have included RB's info so
people don't need to look it up.
/* Copyright (C) 2002 DJ Delorie, see COPYING.DJ for details */
/* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
#include <dos.h>
#include <dpmi.h>
/*--------------------------------------------------
INT 15 - BIOS - WAIT (AT,PS)
AH = 86h
CX:DX = interval in microseconds
Return: CF clear if successful (wait interval elapsed)
CF set on error or AH=83h wait already in progress
AH = status (see #00496)
Note: the resolution of the wait period is 977 microseconds on many systems
because many BIOSes use the 1/1024 second fast interrupt from the AT
real-time clock chip which is available on INT 70; because newer
BIOSes may have much more precise timers available, it is not
possible to use this function accurately for very short delays unless
the precise behavior of the BIOS is known (or found through testing)
SeeAlso: AH=41h,AH=83h,INT 1A/AX=FF01h,INT 70
Note: Does not work on Win 2K or XP!!!!
--------------------------------------------------
INT 1A - TIME - GET SYSTEM TIME
AH = 00h
Return: CX:DX = number of clock ticks since midnight
AL = midnight flag, nonzero if midnight passed since time last read
Notes: there are approximately 18.2 clock ticks per second, 1800B0h per 24
hrs
(except on Tandy 2000, where the clock runs at 20 ticks per second)
IBM and many clone BIOSes set the flag for AL rather than incrementing
it, leading to loss of a day if two consecutive midnights pass
without a request for the time (e.g. if the system is on but idle)
since the midnight flag is cleared, if an application calls this
function after midnight before DOS does, DOS will not receive the
midnight flag and will fail to advance the date
Modern releases of MS-DOS/PC DOS (5.0+???) assume that AL is a day
rollover counter rather than a flag, as expected by older releases.
DOS 5 - 7.10 (Windows 98 SE) provide an undocumented CONFIG.SYS
SWITCHES=/T option to force the old behaviour of the day advancing
code, that is using a flag instead of a counter.
DR DOS 3.31 - DR-DOS 7.03 handle AL as a flag.
SeeAlso: AH=01h,AH=02h,INT 21/AH=2Ch,INT 55"Tandy 2000",INT 4E/AH=02h"TI"
SeeAlso: INT 62/AX=0099h,MEM 0040h:006Ch,MEM 0040h:0070h
--------------------------------------------------*/
#define tick_per_day (24*60*60*10000/182)
void delay(unsigned msec)
{
if(_os_trueversion == 0x532)
{
unsigned long start_tick;
unsigned long end_tick;
__dpmi_regs r;
r.h.ah = 0x00;
__dpmi_int(0x1A, &r);
start_tick = (r.x.cx << 16) + (r.x.dx & 0xffff);
end_tick = (msec*182)/10000 + start_tick;
if ((msec%10000/182) > (5000/182))
{
end_tick++;
}
if (end_tick > tick_per_day)
{
while (r.h.al == 0)
{
r.h.ah = 0x00;
__dpmi_int(0x1A, &r);
}
end_tick = end_tick - tick_per_day;
}
while (((r.x.cx << 16) + (r.x.dx & 0xffff)) <= end_tick)
{
r.h.ah = 0x00;
__dpmi_int(0x1A, &r);
}
}
else
{
__dpmi_regs r;
while (msec)
{
unsigned usec;
unsigned msec_this = msec;
if (msec_this > 4000)
msec_this = 4000;
usec = msec_this * 1000;
r.h.ah = 0x86;
r.x.cx = usec>>16;
r.x.dx = usec & 0xffff;
__dpmi_int(0x15, &r);
msec -= msec_this;
}
}
}
- Raw text -