9. Timing issues in dosemu

This section written by Alberto Vignani <vignani@mbox.vol.it> , Aug 10, 1997

9.1. The 64-bit timers

The idea for the 64-bit timers came, of course, from using the pentium cycle counter, and has been extended in dosemu to the whole timing system.

All timer variables are now defined of the type hitimer_t (an unsigned long long, see include/types.h).

Timers in dosemu can have one of the following resolutions:

        MICROSECOND, for general-purpose timing
        TICK (838ns), for PIT/PIC emulation

You can get the current time with the two functions

        GETusTIME    for 1-usec resolution
        GETtickTIME  for 838ns resolution

The actual timer used (kernel or pentium TSC) is configured at runtime (it defaults to on for 586 and higher CPUs, but can be overridden both by the -[2345] command-line switch or by the "rdtsc on|off" statement in the config file).

The DOS time is now kept in the global variable pic_sys_time. See the DANG notes about emu-i386/cputime.c for details.

9.2. DOS 'view of time' and time stretching

The time stretcher implements DOS 'view of time', as opposed to the system time. It would be very easy to just give DOS its time, by incrementing it only when the dosemu process is active. To do that, it is enough to get SIGPROF from the kernel and (with some adjustments for sleeps) calculate the CPU% used by dosemu. The real tricky part comes when we try to get this stuff back in sync with real time. We must 'stretch' the time, slowing it down and speeding it up, so that the DOS time is always trying to catch the real one.

To enable the time stretcher start dosemu with the command-line option -t. If your CPU is not a 586 or higher, dosemu will exit with an error message. Not that this algorithm doesn't run on a 486 - it was tested and was quite successful - but the use of gettimeofday() makes it a bit too heavy for such machines. Hence, it has been restricted to the pentium-class CPUs only, which can use the CPU timer to reduce use of kernel resources.

9.3. Non-periodic timer modes in PIT

Normally, PIT timer 0 runs in a periodic mode (mode 2 or 3), it counts down to 0 then it issues an int8 and reinitializes itself. But many demos and games use it in one of the non-periodic (NP) modes (0 or 1): the timer counts down to 0, issues the interrupt and then stops. In NP modes, software intervention is required to keep the timer running. The NP modes were not implemented by dosemu-0.66, and this is the reason why some programs failed to run or required strange hacks in the dosemu code. To be fair, most of these games ran also in dosemu-0.66 without any specific hack, but looking at the debug log always made me call for some type of divine intervention :-)

The most common situation is: the DOS program first calibrates itself by calculating the time between two vertical screen retraces, then programs the timer 0 in a NP mode with a time slightly less than this interval; the int8 routine looks at the screen retrace to sync itself and then restarts the timer. You can understand how this kind of setup is very tricky to emulate in a multitasking environment, and sometimes requires using the emuretrace feature. But, at the end, many demos that do not run under Win95 actually run under dosemu.

9.4. Fast timing

By "fast timing", I define a timer 0 period less than half of the Linux "jiffie" time (10ms). This is empirically determined - programs that use a timer period greater than 5ms usually run well with the 'standard' timing of dosemu-0.66.

There will always be a speed limit (the PIT can be programmed up to 500kHz), but this is surely higher now. As an example, if you run M$ Flight Simulator 0.5 with PC speaker enabled, as soon as a sound starts the PIT is set for an interrupt rate of 15kHz (and the int8 routine is really nasty, in that it _keeps_count_ of the events); dosemu-0.66 just crashes, while now the sound is not perfect but reasonabily near to the real thing (but do not try to activate the debug log, or it will take forever!)

Fast timing was not easy to achieve, it needs many tricks esp. to avoid interrupt bursts in PIC; it is still NOT completely working on slow machines (386,486) - but it will maybe never work for all cases.

9.5. PIC/PIT synchronization and interrupt delay

Another tricky issue... There are actually two timing systems for int8, the one connected to the interrupt in PIC, the other to port 0x43 access in PIT. Things are not yet perfectly clean, but at least now the two timings are kept in sync and can help one another.

One of the new features in PIC is the correct emulation of interrupt delay when for any reason the interrupt gets masked for some time; as the time for int8 is delayed, it loses its sync, so the PIT value will be useful for recalculating the correct int8 time. (This case was the source for many int bursts and stack overflows in dosemu-0.66).

The contrary happens when the PIT is in a NP mode; any time the timer is restarted, the PIT must be reset too. And so on.

9.6. The RTC emulation

There is a totally new emulation of the CMOS Real Time Clock, complete with alarm interrupt. A process ticks at exactly 1sec, always in real (=system) time; it is normally implemented with a thread, but can be run from inside the SIGALRM call when threads are not used.

Also, int0x1a has been mostly rewritten to keep in sync with the new CMOS and time-stretching features.

9.7. General warnings

Do not try to use games or programs with hi-freq timers while running heavy tasks in the background. I tried to make dosemu quite robust in such respects, so it will probably NOT crash, but, apart being unplayable, the game could behave in unpredictable ways.