The other day I wrote a simple DOS program which used the RDTSC instruction in order to obtain precise time measurements (of how long it takes a PS/2 keyboard to send data; more about that some other time). The 16-bit DOS program worked fine in a VM running PC DOS 2000, as well as in NTVDM on Windows running on a system with a Core 2 class CPU. But when I tried running it on an old IBM ThinkPad 760XL laptop with a 166 MHz Pentium MMX processor running PC DOS 2000, it just hung when executing RDTSC. Unless I disabled EMM386, and then it worked fine.
That was a bit of a mystery. The Pentium MMX does obviously support the RDTSC instruction. The only troublesome possibility would be if perhaps someone set the CR4.TSD bit (the Time Stamp Disable bit) which would cause RDTSC (and RDTSCP on newer CPUs which support it) to #GP fault outside of ring 0. But old EMM386 does not even touch CR4, and at any rate the same program with the same EMM386 version worked on a newer CPU. Could the ThinkPad’s BIOS be setting CR4.TSD?
A quick experiment showed that no, the ThinkPad’s BIOS does not set CR4.TSD. But unless CR4.TSD is set, the Intel SDM clearly documents RDTSC to work in V86 mode without any problem! Given that RDTSC only works on registers, it essentially cannot fail.
And yet it did on the Pentium MMX. Okay, clearly something is amiss. What did Intel’s Pentium documentation say? Let’s check the original Pentium Processor Family Developer’s Manual, Volume 3: Architecture and Programming Manual (Intel document order number 241430-004, 1995). RDTSC is documented on page 25-264.
Wow, look at that: Virtual 8086 Mode Exceptions: #GP(0) if instruction execution is attempted! That would certainly explain why RDTSC refuses to work on the Pentium CPU with EMM386. Intel completely fails to document this behavior in the current SDM, which is supposed to cover the Pentium. Good thing the old documentation survived.
So what is going on? Not surprisingly, I’m not the only one who noticed this oddity recently, although in that case the author incorrectly concluded that it’s somehow related to a specific EMM386 version, rather than the CPU model (to be fair, EMM386 could handle RDTSC in V86 mode, but clearly EMM386 versions at least from the mid-1990s don’t).
Fortunately, this problem was noticed in the old days as well, and there’s an explanation for the strange misbehavior. According to unverified but very plausible information, Intel’s design was to have RDTSC working in V86 mode, which really makes perfect sense. But due to a misunderstanding, RDTSC cannot be used in V86 mode on the Pentium. Whatever the real reason, the Pentium documentation does accurately describe the actual behavior.
On the Pentium Pro (and all subsequent Intel CPUs), RDTSC works as originally intended and can be used in V86 mode (subject to CR4.TSD). Since the current SDM evolved from the Pentium Pro/Pentium II documentation, the Pentium behavior was forgotten and left out (it’s not mentioned in the Pentium Pro documentation because users were expected to look at the separate Pentium documentation).
In the original 1993 Pentium documentation, RDTSC was documented in the infamous Appendix H, so it’s difficult to check exactly what the documentation said.
It is notable that the AMD K5/5k86 is documented to behave like the Intel Pentium (RDTSC always faults in V86 mode). The behavior of AMD K6 and K7 CPUs is not entirely clear. All AMD64 processors are documented to behave the same as modern Intel processors, i.e. RDTSC is supported in V86 mode.
Cyrix introduced RDTSC in the 6x86MX processor (1997). Cyrix documentation implies that RDTSC should be usable in V86 mode. IDT likewise makes no mention that RDTSC should be unusable in V86 mode.
Conclusion: The RDTSC instruction cannot be used in V86 mode on a set of older processors which includes the Intel Pentium (but not Pentium Pro and later), AMD K5, and possibly some other processors. On modern CPUs this restriction does not exist.