As any user of 16-bit Windows knows, Microsoft Windows 3.1x in 386 Enhanced mode supported a coveted feature called 32-bit disk access (sometimes also called FastDisk). The “32-bit” designation was slightly misleading as there was no 32-bit data path to the disk; it just meant that the system could access the disk without leaving 32-bit protected mode.
The 32-bit disk access was implemented in a VxD called WDCTRL, built into the WIN386.EXE component. WDCTRL was nothing more and nothing less than an IDE hardware disk driver, named after the original Western Digital controller (WD-1003) used in the IBM PC/AT. WDCTRL replaced the INT 13h BIOS for disk access.
The advantage of WDCTRL was twofold. It avoided expensive switching between protected-mode and real-mode (that is, V86 mode) contexts, and also avoided the blocking nature of BIOS calls which only return after an operation completes and allow very little else to happen in the meantime. Not using the INT 13h BIOS service also avoided the need to copy disk buffer to/from memory addressable by the BIOS; WDCTRL could directly access memory anywhere in the system.
However, WDCTRL was very picky when determining whether to run, because if anything went wrong, severe data loss was likely to occur. The algorithm for determining whether WDCTRL could be used was quite involved.
Microsoft describes the preconditions in KB Article 119674, which is unfortunately brief to the point of being utterly meaningless. Luckily the source code for the WDCTRL VxD was shipped with the Windows 3.1 DDK, and isn’t particularly difficult to comprehend.
It should be understood that WDCTRL only supported IDE style disks. Any ESDI, SCSI, or other incompatible disk controllers were not supported and needed custom drivers for 32-bit disk access. However, the vast majority of disks on the market were IDE.
The first prerequisite, and after 1994 or so, the most onerous, was that the disk had to have fewer than 1024 cylinders and that the BIOS geometry had to match the disk’s physical geometry. That meant any disk larger than 504MB was out, and many smaller disks were as well if they had more than 1024 cylinders.
Because WDCTRL supported very old controllers/disks which did not return geometry data, it read the Fixed Disk Parameter Table (FDPT) and compared it with the results of INT 13h, function 08h. The assumption was that the had BIOS programmed the FDPT geometry into the controller.
However, there’s a big catch. The data in the FDPT is not quite the same as the data returned by INT 13h, function 08h. The former contains the total number of disk cylinders, heads, and sectors per track. The latter returns maximum cylinder/head/sector numbers that can be used with BIOS calls.
Since sector numbers start from 1, the FDPT and INT 13h values are usually the same. Heads are numbered from 0, so the INT 13h value is one less. So far so good. Now, cylinders are also numbered from 0, so the INT 13h value should be again one less than the FDPT value. However, it’s often two, and sometimes even three less.
This fact is poorly documented in both official and unofficial references. Looking at the IBM PC/AT BIOS listings, it’s obvious that the BIOS INT 13h, function 08h code takes the FDPT cylinder value and subtracts two from it (where one would be led to think that it should just subtract one). The WDCTRL driver added two back to the INT 13h value, and if it didn’t match the FDPT, tried adding one more before declaring a mismatch. The Undocumented PC explains that one cylinder was often reserved for diagnostic purposes. It was a normal cylinder, but diagnostic utilities might overwrite it, therefore it was inadvisable to allocate the cylinder to an OS. On some Zenith systems, two cylinders were reserved, which is why WDCTRL allowed for that.
Many modern BIOSes (e.g. AMI circa 2005) do not reserve an extra cylinder. On such systems, WDCTRL will refuse to work even if all other conditions are fulfilled. If the geometry test failed, WDCTRL would display a cryptic error message such as the following:
Looking at the KB article, the message can be decoded as follows: WDCTRL was able to obtain the FDPT and INT 13h geometry (phase 0), the reported number of heads and sectors (phases 1, 2) matched but the number of cylinders (phase 3) did not.
Note that non-IDE disks typically do not have a FDPT at all and this test would fail very quickly, before even attempting to access any hardware.
Once WDCTRL was satisfied that the BIOS and physical geometry appeared to match, it set out to test the theory. It used the BIOS INT 13h service to read several disk sectors and compared the results with direct hardware access. The controller status registers at the end of each operation were checked to confirm that they match the expectations.
WDCTRL performed the read-compare-check cycle three times. Initially for the first sector on the disk (cylinder 0, head 0, sector 1), next for sector 503, and finally a sector close to the end of the disk.
Why sector 503? The number 503 was chosen because it is a prime number and should thus ensure that the BIOS-reported geometry indeed matched the disk geometry. In addition, sector 503 was close enough to the beginning of the disk that it was highly likely to contain unique data.
The final test read accesses the next-to-last sector on the next-to-last head on a cylinder 7/8 of the way to the end. This was an interesting choice which illustrates much about Microsoft’s preference for the pragmatic, rather than the correct, solution. Apparently Microsoft ran into enough systems where the BIOS would incorrectly report the disk size and the last sector was not in fact accessible. Rather than alerting the user to the problem, Microsoft avoided dealing with it, presumably on the theory that it wasn’t their problem and WDCTRL wasn’t going to make things any worse than they already were.
At the end of each operation, WDCTRL checked the disk controller registers for consistency with the expected behavior. The controller was assumed to update the current cylinder/head/sector to point at the next sequential sector.
That sounds easy enough, but again there’s a catch. Disk controllers used to behave like that, but often don’t anymore. The ATA-2 standard (1996) changed the behavior of the cylinder/head/sector registers. Now they only need to be updated when an operation fails; the ATA-2 text included the following note: “Prior to the development of this standard, this register was updated at the end of every media access command to reflect the current media address.”
The ATA-3 standard (1997) also changed the sector count register behavior to require a specific value in case an operation failed, but not necessarily contain zero at the end of a successful operation. WDCTRL was thus not fully compatible with ATA-2 and later disks, which is not entirely surprising since WDCTRL was initially written by Ralph Lipe (the Windows/386 guru) in 1990, long before the ATA-2 and ATA-3 specifications were published.
When testing the controller, WDCTRL again printed incomprehensible error messages if a check failed. Phases 04h-0Ah, 0Bh-11h, and 12h-18h all performed the same tests but with the three different sectors as described earlier.
Errors in phase 4/B/12 (hex) would occur if the BIOS read failed, which was somewhat unlikely. Phase 5/C/13 errors would indicate that the direct disk controller operation failed, which was again somewhat unlikely provided that an IDE disk was in fact present and operational. However, errors in phases 6/D/14, 7/E/15, or 8/F/16 would occur with newer disks which no longer conform to the obsolete original ATA specification and do not update registers at the end of a successful operation. Errors in phase 9/10/17 were unlikely with a functioning IDE controller. Errors in phase A/11/18 might occur if, despite all precautions, the BIOS and physical disk geometries did not match.
There were several other tests that WDCTRL performed. Those checked the software environment and primarily served to ensure that someone hadn’t taken over INT 13h in an incompatible way. WDCTRL used the DOS multiplex interrupt 2Fh, function 13h to obtain the address of the original BIOS INT 13h vector and verified that it pointed to a ROM. In addition, WDCTRL tried to ensure that an incompatible cache program wasn’t inserted in the INT 13h handler chain.
WDCTRL would also refuse to load if another VxD had taken over the disk controller hardware or provided FastDisk functionality. Luckily the ensuing error messages were much easier to understand.
Disadvantages and alternatives
WDCTRL was of course not without disadvantages. The basic problem with WDCTRL was that it was stuck in the early 1990s. That meant no support for large disks, but also no support for multi-sector reads/writes and no bus master DMA. Within a few years of the Windows 3.1 release, WDCTRL could easily have worse performance than the system’s BIOS.
For Windows 3.1x, Microsoft did not try very hard to improve things. Their preferred solution was for the users to upgrade to Windows 95, which included better disk support. However, many disk manufacturers released their own FastDisk drivers, often adapted versions of WDCTRL. For example Western Digital shipped WDCDRV.386, a WDCTRL replacement which supported large drives, multi-sector operations, and DMA. Such a driver was especially beneficial for systems with a new IDE disk but older BIOS with no support for advanced features.
Another benefit was that it allowed DOS VMs to be paged out to disk, since disk access did not depend on code located inside the VMs.
Thanks for the memories of long forgotten times.. Could you post more often? Maybe you can have something to say on NextStep or BeOs or OS/2 for PowerPC (though I know you’ve covered it previously)
Thanks for the kind words. I have no experience with BeOS, but I’ll likely write something about NeXTSTEP/OpenSTEP in the future. Or about OS/2 🙂
I post as often as I have something interesting to say… sometimes that’s several times a week, sometimes it’s once a month. Inspiration appears strike unpredictably.