Advanced guide to Time Keeping in dosemu


Time in the IBM-PC / DOS  system

It helps to understand the emulation options in dosemu if you have an idea of how time is maintained in the IBM-PC BIOS and the DOS operating system. If you just want quick advice and the appropriate choice, try the simple guide page (which we suggest you read anyway), or skip ahead to the table of emulation options.
It should be stressed that time in the DOS PC is not very elegant, or sensible, but it is done now and so there is not a lot we can do about that. The BIOS system starts from EPROM when the PC is booted, and once running it attempts to load the operating system (DOS) from floppy first, then from hard disk. Once DOS is loaded, your program can start to find out the current time...


At boot up, DOS checks for the existence of the autoexec.bat file, and if it finds one it assumes the PC is new enough (!) to have the Real Time Clock (RTC), also referred to as the CMOS clock. This was added to the original PC to provide a battery backed-up time source so you did not need to enter the date and time each reboot. If autoexec.bat is not found, you are promoted for the date and time.
From this point onwards, DOS keeps a day counter internally (days from 1980, hence you can't run software prior to this date even if you want to!) and gets the 'time of day' from a BIOS maintained counter that increments roughly 18.2 times per second. Each time the BIOS counter overflows to a new day, a flag is set and this is used to increment the DOS day counter.
It is due to this simple flag that a PC running DOS that sits for more than 24 hours without a time inquiry can miss days!
Most end applications get date and time information from DOS using the software interrupts calls INT-21 AH=2Ah and INT-21 AH=2Ch as recommended, but that is not the only option possible. Some use the INT-1A time calls, or look directly to the BIOS memory area, to get comparable information on the time.


As a final note, remember this page discusses the normal DOS system, you can replace the DOS clock device driver to modify operation, for example using the ATRTC driver to read the RTC instead of the BIOS counter.

The Real Time Clock

Although the RTC is normally accessible by reading and writing directly to the ports at I/O address 0x70 & 0x71, the recommended method is to ask the BIOS system for the date and time using the software interrupts INT-1A AH=04h and INT-1A AH=02h. According to the documentation they should return with the carry flag clear if successful, but this is not always true in my experience as I have seen correct data and CF set.
As well as keeping the date and time during power off, the CMOS RTC also has alarm capabilities and stores other configuration data that can be configured using the BIOS options during a typical PC's early boot stage (e.g. disk drive types, etc). In real PC hardware, the time keeping of the RTC is often much better than the BIOS counter, and we have used this as a way of improving the general DOS time keeping in the past by reading the RTC periodically and setting DOS to match it.
In dosemu the RTC is emulated with a 1 second alarm call (SIGALRM), which is not necessarily on the UTC second transitions. However, this is not such an error in emulation method as you might imagine, as the original RTC hardware did not support synchronization to the UTC second transition when it was set, basically it just counted at 1Hz and all you could do was set the current count to the wanted date/time. In the PIT mode and LINUX mode the RTC is synchronized to the host time (which is on the UTC second transitions) by any INT-1A AH=04h/02h calls.
Of course, if you have the necessary I/O permission settings (and running with root privileges, of course) you can read the host PC's hardware RTC directly with the 0x70/71 calls, but that is not a recommended method.

The BIOS Maintained Counter

The primary time keeping system in the traditional DOS PC is the count at location [segment:offset] 0x0040:0x006C which is incremented by the hardware interrupt INT-08 generated by Timer-0 of the Intel 8254 Programmable Interval Timer (PIT). This also calls the INT-1C software interrupt (the recommended way of hooking user code to the periodic timer) and performs the BIOS time-out for the floppy disk motor.
The default is to have this counting 65535 from the nominal clock rate of  14.31818MHz/12 = 1.193182MHz, so an interrupt is generated every 54.9254ms and there should be 'approximately' 1573040 (0x1800B0) per day. This is illustrated here in the following diagram:
Diagram showing hardware, BIOS and DOS time interrupts
A further complication of the PIT generated interrupt clock is the possibility that something could modify the Timer-0 rate and change the tick count incrementing behavior and calling frequency of the INT-1C vector from the INT-08 vector. This is done sometimes by mouse drivers and TSR programs that want finer time resolution than the ~55ms of the BIOS initialized values.
I say "approximately" in connection with the counts per day figure because the accuracy of this clock is not very high, probably +/-100ppm at best, so you could expect a variability of something like +/-157 each day (+/-8 seconds, or possibly much worse).
DOS normally reads this BIOS counter by using the software interrupt INT-1A AH=00h, this returns the count (for conversion in to hours, minutes, seconds, and hundredths of a second), and reads and resets the midnight transition flag which is used to indicate that a day (or more!) has passed since the last read. Hence if you use the INT-1A AH=00 call separately from DOS, you run the risk of DOS missing the day transition event and not updating the date correctly.
Although the DOS time call INT-21 AH=2Ch has 1/100 of a second returned in the DL register, the underlying resolution is actually poorer at ~5.5 * 1/100 since that is the underlying tick rate of the system.
As a general rule, counting interrupts is not a good way of keeping time, even if the crystal oscillator and count values were exact, as it is common to have the occasional interrupt missed due to other things happening on the computer.

Emulation Options in dosemu

There are 3 modes of time emulation currently supported in dosemu, and they offer various solutions to the conflicting needs of close emulation of the inelegant PC system and the desire to have accurate time keeping, matching that of the LINUX host (which could be maintained by NTP or similar for high long term accuracy):
Mode
BIOS
PIT
LINUX
Emulation of INT-1A AH=00 counter read?
As for 'real' PC, returns memory maintained at 0040:006C
Returns a tick count computed from the PIT clock.
Returns a tick count computed from the current host (local) time.
Emulation of BIOS 0040:006C counter?
As for 'real' PC, maintained by the INT-08 interrupt.
As for BIOS, but synced to the INT-1A call value when made.
As for BIOS, but synced to the INT-1A call value when made.
Emulation of RTC INT-1A AH=04/02h read?
Returns emulated values based on 1 second alarm call sequencing the time & date counter, may not be UTC synchronized. No emulation of "daylight saving" flag.

Returns value computed from host (local) time.
Cannot be 'set'.
Returns value computed from host (local) time.
Cannot be 'set'.
Emulation of RTC I/O 0x70/71 access?
Same system as INT-1A emulation.
Same as BIOS, but synced to host time on INT-1A AH=04/02 calls.
Same as BIOS, but synced to host time on INT-1A AH=04/02 calls.
DOS Time set?
Yes
Yes
No
DOS time accuracy?
Low
Depends on emulation of PIT clock rate, correctness of Timer-0 rate, and any lost interrupts.
Medium
Depends on emulation of PIT clock, but independent of Timer-0 rate and any lost interrupts.
High
Maintains correct synchronization to host time, even if host is adjusted by small amounts.

Most users will want either the BIOS mode (if they need to set the time to something other than the host time, for example, for programs that do not work post-Y2K) or the LINUX mode (if they want accurate time keeping over long periods).

Timezone Use

All of the dosemu time is in 'local' time, as specified by the timezone environment variable when dosemu starts. This is compatible with the original DOS systems really bad decision to work in local time, which leads to all sorts of stupidity in terms of coping with daylight saving changes and file time stamps over large networks, etc, but we are too late to do anything about that now.
As a result, the emulated PC starts up at the 'local' time of the host computer, and in the case of LINUX time it will then match the daylight saving shift and (with a minor oddity for one second) and leap seconds occasionally used to keep UTC in step with UT1 time. In the BIOS and PIT cases, the emulated time just runs at the nominal rate, possibly with the odd dropped interrupt, and so is largely decoupled from the host time adjustments (at least in theory).
You also have to note that programs written with Microsoft 'C' assume the timezone is -8 hours PST, unless the TZ variable is set to tell them otherwise. Another dumb decision. Any other software that makes use of the standard DOS time calls just gets 'local' time, whatever that might be, and it is up to the software to figure out any offset, daylight saving change, etc. For a long time, the daylight saving start/stop dates were decided by parliament in the UK, rather than by a calender algorithm, and equally dumb idea.
If you really need to keep dependable time under DOS, we suggest you run with TZ=GMT0 so you have zero offset and no daylight saving system. Under Windows you have to set the time region to Casablanca & Monrovia to get the same behavior.

Contact

See details on the PSC Home Page.

(c) Paul Crawford, 5th March 2007 (updated 13th April 2013 to remove dead hyperlink).