The story of the mysterious WINA20.386 file


matushorvath was curious about the WINA20.386 file that came with some versions of MS-DOS.

The WINA20.386 file predates my involvement, but I was able to find some information on the Internet that explained what it was for. And it's right there in KB article Q68655: Windows 3.0 Enhanced Mode Requires WINA20.386:

Windows 3.0 Enhanced Mode Requires WINA20.386

Windows 3.0 enhanced mode uses a modular architecture based on what are called virtual device drivers, or VxDs. VxDs allow pieces of Windows to be replaced to add additional functionality. WINA20.386 is such a VxD. (VxDs could be called "structured" patches for Windows.)

Windows 3.0 enhanced mode considers the state of the A20 line to be the same in all MS-DOS virtual machines (VMs). When MS-DOS is loaded in the high memory area (HMA), this can cause the machine to stop responding (hang) because of MS-DOS controlling the A20 line. If one VM is running inside the MS-DOS kernel (in the HMA) and Windows task switches to another VM in which MS-DOS turns off A20, the machine hangs when switching back to the VM that is currently attempting to execute code in the HMA.

WINA20.386 changes the way Windows 3.0 enhanced mode handles the A20 line so that Windows treats the A20 status as local to each VM, instead of global to all VMs. This corrects the problem.

(At the time I wrote this, a certain popular Web search engine kicks up as the top hit for the exact phrase "Windows 3.0 Enhanced Mode Requires WINA20.386" a spam site that copies KB articles in order to drive traffic. Meanwhile, the actual KB article doesn't show up in the search results. Fortunately, Bing got it right.)

That explanation is clearly written for a technical audience with deep knowledge of MS-DOS, Windows, and the High Memory Area. matushorvath suggested that "a more detailed explanation could be interesting." I don't know if it's interesting; to me, it's actually quite boring. But here goes.

The A20 line is a signal on the address bus that specifies the contents of bit 20 of the linear address of memory being accessed. If you aren't familiar with the significance of the A20 line, this Wikipedia article provides the necessary background.

The High Memory Area is a 64KB-sized block of memory (really, 64KB minus 16 bytes) that becomes accessible when the CPU is in 8086 mode but the A20 line is enabled. To free up conventional memory, large portions of MS-DOS relocate themselves into the HMA. When a program calls into MS-DOS, it really calls into a stub which enables the A20 line, calls the real function in the HMA, and then disables the A20 line before returning to the program. (The value of the HMA was discovered by my colleague who also discovered the fastest way to get out of virtual-8086 mode.)

The issue is that by default, Windows treats all MS-DOS device drivers and MS-DOS itself as global. A change in one virtual machine affects all virtual machines. This is done for compatibility reasons; after all, those old 16-bit device drivers assume that they are running on a single-tasking operating system. If you were to run a separate copy of each driver in each virtual machine, each copy would try to talk to the same physical device, and bad things would happen because each copy assumed it was the only code that communicated with that device.

Suppose MS-DOS device drivers were treated as local to each virtual machine. Suppose you had a device driver that controlled a traffic signal, and as we all know, one of the cardinal rules of traffic signals is that you never show green in both directions. The device driver has two variables: NorthSouthColor and EastWestColor, and initially both are set to Red. The copy of the device driver running in the first virtual machine decides to let traffic flow in the north/south direction, and it executes code like this:

if (EastWestColor != Red) {
 SetEastWestColor(Red);
}
SetNorthSouthColor(Green);

Since both variables are initially set to Red, this code sets the north/south lights to green.

Meanwhile, the copy of the device driver in the second virtual machine wants to let traffic flow in the east/west direction:

if (NorthSouthColor != Red) {
 SetNorthSouthColor(Red);
}
SetEastWestColor(Green);

Since we have a separate copy of the device driver in each virtual machine, the changes made in the first virtual machine do not affect the values in the second virtual machine. The second virtual machine sees that both variables are set to Red, so it merely sets the east/west color to green.

On the other hand, both of these device drivers are unwittingly controlling the same physical traffic light, and it just got told to set the lights in both directions to Green.

Oops.

Okay, so Windows defaults drivers to global. That way, you don't run into the double-bookkeeping problem. But this causes problems for the code which manages the A20 line:

Consider a system with two virtual machines. The first one calls into MS-DOS. The MS-DOS dispatcher enables the A20 line and calls the real function, but before the function returns, the virtual machine gets pre-empted. The second virtual machine now runs, and it too calls into MS-DOS. The MS-DOS dispatcher in the second virtual machine enables the A20 line and calls into the real function, and after the function returns, the second virtual machine disables the A20 line and returns to its caller. The second virtual machine now gets pre-empted, and the first virtual machine resumes execution. Oops: It tries to resume execution in the HMA, but the HMA is no longer there because the second virtual machine disabled the A20 line!

The WINA20.386 driver teaches Windows that the state of the A20 should be treated as a per-virtual-machine state rather than a global state. With this new information, the above scenario does not run into a problem because the changes to the A20 line made by one virtual machine have no effect on the A20 line in another virtual machine.

matushorvath goes on to add, "I would be very interested in how Windows 3.0 found and loaded this file. It seems to me there must have been some magic happening, e.g. DOS somehow forcing the driver to be loaded by Windows."

Yup, that's what happened, and there's nothing secret about it. When Windows starts up, it broadcasts an interrupt. TSRs and device drivers can listen for this interrupt and respond by specifying that Windows should load a custom driver or request that certain ranges of data should be treated as per-virtual-machine state rather than global state (known to the Windows virtual machine manager as instance data). MS-DOS itself listens for this interrupt, and when Windows sends out the "Does anybody have any special requests?" broadcast, MS-DOS responds, "Yeah, please load this WINA20.386 driver."

So there you have it, the story of WINA20.386. Interesting or boring?

Comments (35)
  1. TKW says:

    Interesting!  Thanks for doing the digging for us.

  2. VoicesInMyHead says:

    Yes, very interesting.  Being a developer from the MS-DOS days, I love to hear pieces of history like this.  While the platform may not be very relevant anymore, the lessons learned and techniques used are very relevant.  Quite honestly, developers of today could learn a few things from these history lessons.  Stories like this often spark a more granular level of thinking in the minds of modern developers who are used to having the details taken care of for them.

  3. jcs says:

    Back in the old days, we used to speculate that the first person to figure out the purpose of this file would win a 20MHz 386 computer from Microsoft.

    Of course, this anecdote dates me, because back then we really did want to win a 20MHz 386…

  4. kinokijuf says:

    The certain popular Web search engine now has this very blog post as the first hit. The KB article is second.

    [Note that the variant of the KB article it picked is precisely the one I linked to in the article (rather than say this one or this one), so I wouldn't be too quick to say that it found it without any help from me. -Raymond]
  5. TKW says:

    Clearly, they know a good source when they see it!

  6. John Muller says:

    Seems like Raymond is the E. F. Hutton of the internet.

  7. Gabe says:

    I remember how the A20 line was a source of problems, but I never understood exactly what was going on. Now, over 20 years later, I can understand it thanks to Wikipedia!

    Apparently when the 80286 was introduced they added additional address lines (previous CPUs had lines A0-A19), but didn't provide a backwards-compatibility mode to simulate the old behavior of wrapping around at 1MB. In order to avoid breaking software that relied on that wrap-around, IBM installed what was effectively a hardware AND gate between the CPU's A20 line and RAM. One input to that AND gate was A20 and the other one was controlled by the on-board Intel 8042 that was originally there to manage the keyboard interface, but had an otherwise unused I/O pin.

    When internal caches got added to chips (Intel's i486 in 1989), the cache needed to know the state of the A20 enable line, so they had to add a pin to the CPU (called A20M#, or Address bit 20 Mask). This pin lasted 20 years before finally being retired in 2008 with the Core i7. Note that this is only required for running the bootloader, because nowadays virtual memory is used to create the wrap-around effect.

  8. Alan says:

    I certainly found it an interesting look into an odd quirk of the past. Thanks for writing it up despite your uncertainty about it's interestingness.

  9. Raphael says:

    The certain popular search engine apparently completely removed the spam link from it's results. Bing still seems to put it in second place (just above this very blog post).

  10. Leo Davidson says:

    Is Bing better at filtering out spam sites or are its algorithms just less targeted by them them in the first place?

    (It could be both, of course.)

  11. JenK says:

    Hm. The last two paras of that KB article seem REALLY familiar. I think I edited/cleaned up that article at least once….

  12. dave says:

    Quite honestly, developers of today could learn a few things from these history lessons.

    The lesson I take from it is "trying to be compatible is not always the best thing".

    I can't think of any good reason why code needs to explicitly require 1048575 + 1 == 0.  So, treat that as a bug, fix the offensive code, don't hook a spare keyboard gate up to the address lines of the processor (!), don't waste time writing code to virtualize this foolishness, …

    Yeah, yeah, it's attitudes like mine which explain why I'm not a software billionaire…

  13. Adam Rosenfield says:

    @dave: How would you expect to fix a bug in a bootloader written 20 years ago for which the source code has been long since lost?  [I'm not saying this is the case, but it certainly could be]

  14. jon says:

    @Leo Davidson: Or Bing gives priority to Microsoft sites.. but no.. no.. that would never happen, surely.

  15. AC says:

    Or Bing gives priority to Microsoft sites

    Well, I'd would probably be very sensible for both Bing and Google to give priority to Microsoft sites over spam-aggregators. That's the spirit of page rank.

  16. chentiangemalc says:

    while totally useless knowledge today it does bring back memories…. :)

  17. Ralph Vader says:

    shame on M1cr0$0f+ for trying to monopolize knowledge bases. before you know it they'll be making us all pay to think

  18. Ivan K says:

    History, shmistory (line stolen from The Far Side).

    And off-topic / for what it's worth: the first non-advert link when I entered 'printer help' in Google (in Australia) went to a Microsoft support page, while the first non-advert link in Bing went to a generic help site for pc's & macs. (Although many users who enter queries like that would probably just click the topmost ad links.)

  19. J says:

    @dave: Awesome work, you've just made a processor that a whole lot existing code doesn't work on. Who are you going to sell it to? Are you going to pay developers so that you have software that actually works on the processor when you launch it? Are you going to advertise it as "mostly-compatible"? Are the extra outlays and lost revenue more or less costly than a cheap compatibility hack?

  20. I wonder who the guilty parties were for this.  There must be an interesting back story at IBM…  What widely-used piece(s) of software originally needed this A20 hack?  Why did IBM originally have to do this for the 286?

    I can see it now… an engineer testing an early 286 motherboard…

    1.  Hmmm, software XYZ isn't running.

    2.  <extensive debug session; realizes that software XYZ expects wrap-around behavior>

    3.  Stupid programmers, did they really think we'd never have more than 1 MB RAM?

    4.  Nobody will buy this computer if it won't run software XYZ, flagship killer app of the IBM PC platform!

    5.  Hmmm…. original software assumed A20 was always 0, what if we use an AND gate to force A20 to 0 unless the user knows how to use > 1 MB RAM?

    6.  <gets razor knife, cuts A20 trace on the prototype PCB; also located an unused AND gate on the board>

    7.  <hooks up A20 line on RAM to output of AND gate with piece of wire>

    8.  <hooks up AND gate input to CPU A20 line with another piece of hookup wire>

    9.  Uh oh, how will I control this gate?  I need an unused I/O!

    10. Hey, nobody seems to be using this output on the keyboard controller….

    I guess the rest is history…  This thing has last-second hack written all over it.

    [Two words: Copy protection. Here are a few more words: Anti-debugging. Games. (I wouldn't be surprised if there were BIOSes that pulled this trick. Set your DS to F800 and you have access not only to the ROM BIOS but also the BIOS data area with a single segment!) -Raymond]
  21. Jeremy says:

    Another interesting piece of A20 lore:  it helped original Xbox users "jailbreak" their device!  That console was more or less a PC compatible machine, powered by an Intel Pentium III.  It booted from a "secret" ROM at address FFFFFFF0.  Hardware hackers pulled down the A20 pin on the CPU to force it to boot from FFEFFFF0 instead, a location in user-overwritable flash memory.

    web.archive.org/…/17_Mistakes_Microsoft_Made_in_the_Xbox_Security_System

  22. 640k says:

    i7 cannot load dos in hma? what a piece of junk.

  23. Raphael says:

    i7 cannot load dos in hma? what a piece of junk.

    Huh? Why not? To do that you only need a enabled A20 line. Doesn't matter if it was on in the first place.

    Now running the original IBM PC BIOS on an i7, that would be a problem (not least of all because it wouldn't support the chip in the first place).

  24. Anonymous Coward says:

    >certain popular Web search engine

    *sigh* Just name it. In any case, if it was Google, the KB article is the first hit now. Of course, someone might have reported the spammer to Google, or maybe this blog has a high pagerank that bumped the KB article up. We'll probably never know.

  25. yuhong2 says:

    Besides, i7s still have A20M etc via Virtual Legacy Wires.

  26. yuhong2 says:

    [Two words: Copy protection. Here are a few more words: Anti-debugging. Games. (I wouldn't be surprised if there were BIOSes that pulled this trick. Set your DS to F800 and you have access not only to the ROM BIOS but also the BIOS data area with a single segment!) -Raymond]

    Don't forget CALL 5 (helps in CP/M translation).

  27. xpclient says:

    WP states "Support for the A20 gate was removed in the Nehalem microarchitecture." :(

  28. Danny says:

    Boring. God, I'm glad we got rid of all that real/protected mode switching and all the crap that comes out of "use real mode"/"use protected mode to have more memory" junk.

  29. Ditching all the legacy stuff in your CPU is nice and easy. Then you just have the task of persuading customers to buy your new architecture and software developers to develop it, like Intel did so effectively with the Itanium systems we all use now, wiping out those silly backwards-compatible 64-bit chips from AMD … whoops.

    There is a market for nice clean efficient CPU designs: anywhere you don't need to worry about existing code bases, essentially. Does my current washing machine use the same control chip as the previous one? Who cares – if the manufacturer wanted to switch from, say, Z80 to 6502, that's up to them. If my new laptop couldn't run existing x86 code, though? It would be most of the way to paperweight status already. Ask anyone with an iPhone or Android phone to think about tablets: buy a different platform, re-purchase all their apps and start again, or keep using all the familiar apps they've already got and paid for?

    It can be a pain sometimes – but if you couldn't be sure that shiny new PC would run Windows XP happily, or that Windows 7 would run Office 2003, your favourite old shoot-em-up, that accounts package you rely on and the free PDF generator you've got set up to do stuff just the way you like, you won't be at all happy. Sure, you could upgrade Office – but then some of those macros you use for client invoices break…

  30. John Elliott says:

    "What widely-used piece(s) of software originally needed this A20 hack?"

    As Yuhong Bao briefly mentioned above, MS-DOS — for the CALL 5 entry point. For CP/M-80 compatibility, the offset at PSP bytes 6 and 7 needs to be as high as possible (DOS seems to use 0xFEF0). But it's also treated as the offset of an entry point within MS-DOS. MS-DOS, certainly in the early versions, was less than 0xFEF0 bytes long, so the only way for 0xFEF0 to be the offset of an address in MS-DOS was to use the wrap. (In fact the entry point chosen was right at the start of memory, 0000:00C0, so the PSP gets set up with the address F01D:FEF0).

    Incidentally, CP/M-86 didn't do this, because it didn't provide the CALL 5 entry point.

  31. Douglas says:

    Interesting! Thanks Raymond. I really enjoyed this trip down memory lane.

  32. Jim says:

    I think my answer is that I found it interesting because I never had to deal with any of this, so to me it's an insight into a complex but long-abandoned world. If I had once needed to deal with it, I'm sure it would just a reminder of hours wasted learning arcane details of what should've been easy.

  33. Gabe says:

    Douglas: Was the "memory lane" pun intentional or not?

  34. Worf says:

    @jas88: Ditching all the legacy stuff in your CPU is nice and easy. Then you just have the task of persuading customers to buy your new architecture and software developers to develop it, like Intel did so effectively with the Itanium systems we all use now, wiping out those silly backwards-compatible 64-bit chips from AMD … whoops.

    You do realize that amd64 isn't fully backwards compatible with x86, right? amd64 is a completely different architecture to x86, with an ia32 compatibility mode.

    It's best to treat the amd64 CPU as two different CPUs – a regular x86 with full real mode through protected and all the quirks in-between, with a 64-bit processor with 32-but x86 compatibliity mode on the side. Because once you're in 64-bit mode, your only option is to run 32-bit code or 64-bit code. If you have 16-bit code, it ain't gonna work in 64-bit mode. You'll have to run a 32-bit OS to still use it.

    Actually run into this problem from time to time – and no, VMs will not work for obvious reasons. You have to use something like DOSbox to run your 16-bit app. Or if it's Windows, hope you have qemu working with Windows 95 or something.

  35. hagenp says:

    @dave: How would you expect to fix a bug in a bootloader written 20 years ago for which the source code has been long since lost?

    Disassemble it.

Comments are closed.