Archives
- November 2025
- October 2025
- September 2025
- August 2025
- July 2025
- June 2025
- May 2025
- April 2025
- March 2025
- February 2025
- January 2025
- December 2024
- November 2024
- October 2024
- September 2024
- August 2024
- July 2024
- June 2024
- May 2024
- April 2024
- March 2024
- February 2024
- January 2024
- October 2023
- September 2023
- August 2023
- July 2023
- June 2023
- May 2023
- April 2023
- March 2023
- January 2023
- December 2022
- November 2022
- October 2022
- September 2022
- July 2022
- June 2022
- May 2022
- April 2022
- March 2022
- February 2022
- January 2022
- December 2021
- November 2021
- October 2021
- September 2021
- August 2021
- July 2021
- June 2021
- May 2021
- April 2021
- March 2021
- February 2021
- January 2021
- December 2020
- November 2020
- October 2020
- September 2020
- August 2020
- July 2020
- June 2020
- May 2020
- April 2020
- March 2020
- February 2020
- January 2020
- December 2019
- November 2019
- October 2019
- September 2019
- August 2019
- July 2019
- June 2019
- May 2019
- April 2019
- March 2019
- February 2019
- January 2019
- December 2018
- November 2018
- October 2018
- August 2018
- July 2018
- June 2018
- May 2018
- April 2018
- March 2018
- February 2018
- January 2018
- December 2017
- November 2017
- October 2017
- August 2017
- July 2017
- June 2017
- May 2017
- April 2017
- March 2017
- February 2017
- January 2017
- December 2016
- November 2016
- October 2016
- September 2016
- August 2016
- July 2016
- June 2016
- May 2016
- April 2016
- March 2016
- February 2016
- January 2016
- December 2015
- November 2015
- October 2015
- September 2015
- August 2015
- July 2015
- June 2015
- May 2015
- April 2015
- March 2015
- February 2015
- January 2015
- December 2014
- November 2014
- October 2014
- September 2014
- August 2014
- July 2014
- June 2014
- May 2014
- April 2014
- March 2014
- February 2014
- January 2014
- December 2013
- November 2013
- October 2013
- September 2013
- August 2013
- July 2013
- June 2013
- May 2013
- April 2013
- March 2013
- February 2013
- January 2013
- December 2012
- November 2012
- October 2012
- September 2012
- August 2012
- July 2012
- June 2012
- May 2012
- April 2012
- March 2012
- February 2012
- January 2012
- December 2011
- November 2011
- October 2011
- September 2011
- August 2011
- July 2011
- June 2011
- May 2011
- April 2011
- March 2011
- January 2011
- November 2010
- October 2010
- August 2010
- July 2010
Windows 3.x VDDVGA
While working on my Windows 3.x display driver, I ran into a vexing problem. In Windows 3.1 running in Enhanced 386 mode, I could start a DOS session and switch it to a window. But an attempt to set a mode in the DOS window (e.g. MODE CO80) would destroy the Windows desktop, preventing further drawing from happening properly. It was possible to recover by using Alt+Enter to switch the DOS window to full screen again and then returning to the desktop, but obviously that wasn’t going to cut it.
Oddly enough, this problem did not exist in Windows 3.0. And in fact it also didn’t exist in Windows 3.1 if I used the Windows 3.0 compatible VDDVGA30.386 VxD shipped with Windows 3.1 (plus the corresponding VGA30.3GR grabber).
There was clearly some difference between the VGA VDD (Virtual Display Driver) in Windows 3.0 and 3.1. The downside of the VDD is that its operation is not particularly well explained in the Windows DDK documentation. The upside is that the source code of VDDVGA.386 (plus several other VDD variants) was shipped with the Windows 3.1 DDK.
First I tried to find out what was even happening. Comparing bad/good VGA register state, I soon enough discovered that the sequencer registers contents changed, switching from chained to planar mode. This would not matter if the driver used the linear framebuffer to access video memory, but for good reasons it uses banking and accesses video memory through the A0000h aperture.
But how could that even happen? The VDD is meant to virtualize VGA registers and not let DOS applications touch the real hardware. Something had to be very wrong.
I also suspected that the problem was likely caused by my driver doing something wrong, or perhaps not doing something necessary to correctly set up the VDD. The Video 7 sample driver that I based my code on was intended to work with its own custom VDD, not with VDDVGA; judging from the source code in the Windows 3.1 DDK, I suspect that V7VDD.386 was effectively forked from the Windows 3.0 VGAVDD and at most slightly updated for Windows 3.1. That might also explain why my driver worked with VDDVGA30.386 but not with the newer VDDVGA for Windows 3.1 (VDDVGA.386 is normally built into WIN386.EXE and does not exist as a separate file, although a standalone VDDVGA.386 can be used).
After poking through the VDDVGA source code for a while, I realized that it almost certainly wasn’t register access from a DOS session leaking through. It was the VDD itself!
And I also found that the missing link was a small section of code that was explained as “Call VDD to specify latch address” in the Windows 3.1 VGA driver. It is protected-mode service entry point 0Ch in VGAVDD, and it’s called VDDsetaddresses in the VGA display driver (VGA.ASM) but DspDrvr_Addresses in the VDD (VMDAVGA.INC).
The Windows 3.1 DDK does not appear to document the DspDrvr_Addresses function. Although due to the inconsistent naming, it’s difficult to be entirely certain.
At the same time, I tried to approach the problem from a different angle. The Windows 3.1 DDK does document a set of INT 2Fh calls, some of them with promising descriptions, such as “Save Video Register State (Interrupt 2Fh Function 4005h)” and the corresponding “Restore Video Register State (Interrupt 2Fh Function 4006h)”.
But there I hit the opposite problem. Even though the DDK documents those functions, and the VGA display driver implements 4005h/4006h callbacks, I could not find any code in the VDD calling those functions! And the debugger showed no sign that anyone else is calling them, either.
Note: It is possible that the save/restore registers INT 2Fh callbacks were specified for OS/2. Indeed the OS/2 2.1 DDK defines INT2F_SYSSAVEREGS (0x4005) and INT2F_SYSRESTOREREGS (0x4006) in the virtual video device driver source code… but again there is no sign of those being used in the code.
There is also “Enable VM-Assisted Save/Restore (Interrupt 2Fh Function 4000h)” and “Disable VM-Assisted Save/Restore (Interrupt 2Fh Function 4007h)”. The VGA and Video 7 call these functions and name them STOP_IO_TRAP and START_IO_TRAP. And VGAVDD.386 really implements these in VDD_Int_2F (the INT 2Fh intercept in VGAVDD.386). Interestingly, STOP_IO_TRAP corresponds to “VM knows how to restore the screen” logic, and START_IO_TRAP naturally corresponds to “VM doesn’t know how to restore the screen”.
But how does that make any sense? Why would the hardware access from the Windows display driver ever be trapped?
Why Oh Why?
Although I could not find any explanation in the DDK documentation, eventually I realized what the reason had to be: Windows/386 (aka Win386).
Windows/386 was essentially an add-on for Windows 2.x, adding the ability to pre-emptively multitask DOS sessions. Only, in the Windows 2.x days, Windows itself was effectively one of those DOS sessions.
That is, Windows 2.x display drivers had (almost) no clue about Win386. That only came with Windows 3.0. Therefore the Win386 VDD had to manage Windows itself as just another DOS session, save and restore all EGA/VGA registers, and also manage video memory contents. In fact in the “normal” Windows 2.x Adaptation Guide, there is almost no mention of Win386 (there was a separate development kit for Win386 which covered virtual device drivers).
I/O trapping was especially important on EGA adapters which did not have readable registers. As a consequence, it was impossible to read the current EGA hardware state; the only way was to shadow the state of EGA registers as they were written.
Windows 2.x display drivers did implement one interesting piece of functionality, switching Windows to/from the background. This was not at all intended for Win386 but rather for OS/2 (that is, OS/2 1.x, at least initially). The switching was implemented in the display driver by hooking INT 2Fh and watching for focus switch notifications.
In Windows 3.0, Enhanced 386 mode implemented the previously OS/2-only INT 2Fh callbacks that indicated switching out of and back to the Windows desktop. On the way out, the display driver could restore some kind of sane VGA state, and on the way back to the desktop it could re-establish the necessary hardware register state. In addition, the display driver could force a redraw of the entire screen, which avoided the need to save any video memory (which was good, because the video memory could be relatively big).
Unfortunately I don’t have the Windows 3.0 DDK (and no one else seems to, either) so I can’t look at the 3.0 VDDVGA source code. But it’s clear that whereas Windows 2.x display drivers knew very little about Win386, Windows 3.0 drivers typically have some level of cooperation with the VDD through the INT 2Fh interface.
Windows 3.1 VDDs
In Windows 3.1, Microsoft added a whole new level of complexity to VDDs. Namely, video memory can be paged. Microsoft article KB80901 states the following:
In Windows version 3.1, the standard virtual display device (VDD) for VGA is modified to demand page video memory. Thus, you can run graphical MS-DOS-based applications in a window or in the background on VGA systems. This VDD must track video memory usage, so it is not compatible with any of the super VGA display drivers that must access more than 256 kilobytes (K) of video memory. To run these display drivers, a user must use either the VDD provided by the display adapter manufacturer or the VDDVGA30.386, which is included with Windows version 3.1. Demand paging of video memory may break TSRs that worked with Windows version 3.0. The difference is that the VDD virtualizes access to video memory; in Windows version 3.0, the display driver had full reign over memory.
I am not entirely certain why Microsoft did that. It seems to add a lot of complexity in return for not a lot.
The Windows 3.1 VDDVGA.386 introduced a new concept of ‘CRTC VM’ and ‘MemC VM’, that is, the VM that owns the graphics card’s CRT controller (what is displayed on the screen) and the VM that owns the graphics card’s memory controller, i.e. what is read from and written to video memory.
In the typical case, the CRTC VM is also the MemC VM; that can be the Windows desktop (aka System VM) or a full-screen DOS box. Things get interesting for windowed DOS boxes. The desktop remains the CRTC owner because the desktop is what needs to be displayed. But a DOS box can temporarily become a MemC VM, directly accessing video memory.
Needless to say, this gets quite complicated. VDDVGA.386 needs to save the old MemC VM state, merge the new MemC VM state with it and update the hardware registers, let the DOS box execute, and then restore the original MemC VM state before the System VM can do any drawing to the Windows desktop.
As far as I can tell, of the drivers shipped with Windows 3.1 only VDDVGA.386 has this complexity. None of the other VDDs, including the Video 7 specific V7VDD.386, implement this logic. As mentioned above, I strongly suspect that the Video 7 VDD in the Windows 3.1 DDK (source code in VDDV7VGA directory) is actually very close to the Windows 3.0 VDDVGA.386, and thus to the Windows 3.1 VDDVGA30.386.
It’s a Tie
Needless to say, the register saving/restoring logic in VDDVGA.386 is quite fiddly and difficult to debug. In the end I have not been able to find out why register changes “leak through” to the System VM (i.e. Windows desktop). I found out where in the code that happens, but not why, or how to prevent it.
What I did find is that the DspDrvr_Addresses function does not at all do what the comments suggest. The function is supposedly used “to specify latch address” in video memory. Closer examination of the Windows 3.1 VGA display driver showed that while it does define a byte for the latches, and sends its address to the VDD, the display driver does nothing with that byte.
But even more interesting is that VDDVGA.386 does not use the latch byte either. Instead, VDDVGA.386 assumes that the latch byte lives somewhere very close to the end of the video memory used by the display driver, and expects that any following pages can be used by the VDD. (That logic likely comes from the Windows 2.x EGA/VGA drivers.)
A corollary is that passing 0FFFFh as the latch byte address to the VDD (something that SVGA256.DRV does) tells VDDVGA.386 that there is no video memory to share. In that situation, VDDVGA.386 does not try any hair-raising schemes to modify the VGA register state behind the display driver’s back.
It’s not perfect either. The system does survive MODE CO80 in a windowed DOS box without trouble, but starting (in a window) a DOS application which uses multiple pages of video memory triggers an interesting warning:
The warning appears to be harmless. Once it’s dismissed, the application works fine. The warning also only pops up the first time the application is started (in the same windowed DOS box). It’s not ideal, but it’s something I can live with.
I consider this fighting VDDVGA.386 to a draw. I am not impressed with the Windows 3.1 DDK documentation—it omits certain things while documenting other things that appear to be fictional. That said, the actual DDK source code saves the day, at least in the video area, because it is possible to see more or less all of the code involved.
And the Windows 3.0 DDK would be really nice to have.
64 Responses to Windows 3.x VDDVGA
there is VDDVGA source in Win3.1 (PR2) DDK
https://archive.org/details/ms-win30-prerelease2-ddk
Yes, but that doesn’t really help much, since there is VDDVGA source in the final 3.1 DDK as well. The PR2 source code is a little different but not a lot.
On the other hand the VDDV7VGA source code is more or less identical between the PR2 and final DDK release. The Video 7 display driver is slightly different but only slightly.
Can we have binaries yet? ;-; I’ve got this old panasonic toughbook that has two working batteries and it’d be really neat to run windows in SXGA+ (or even just 1280×1024) on it
I could give you binaries… but I can promise that they won’t work on any real laptop, and I have no interest in working on that.
this just showed up on betawiki
password is `infected’
Windows_3.0_ddk_prerel_19900501.zip
Off topic, re comments, RSS and whatnot:
For some weird reason it seemed like the bug where URLs for comments was fixed. It started working for the blog post “The future that newer was”, i.e. the latest comments has an url in the RSS feed to …/comment-page-2/…
However this post has RSS feed for comments where the URL points to …/comment-page-1/… even though the actual comments are on page 2.
Maybe what actually displays comments and what generate the RSS URLs are set to a different amount of comments per page? I haven’t looked into each comment URL but that would be a plausible explanation of the bug.
Nice work!
Should put this on github – and post to vogons.
I’m sure someone there will have the skills to help “finish” it especially with all your documentation.
I wouldn’t mind integrating it into my “slipstreamed” 3.11 vmware template here: https://www.vogons.org/viewtopic.php?f=8&t=99992
Or you could, if you want.
You might have already seen it but in case you haven’t, the Windows 3.0 Pre-Release DDK was uploaded to Bitsavers by Al Kossow from the Computer History Museum:
https://forum.vcfed.org/index.php?threads/looking-for-windows-3-0-ddk.61299/#post-1376430
In case someone would like to take a peek at the code without unpacking the images:
https://github.com/neozeed/Windows_3.0_ddk_prerel_19900501/
Yes, seen that. It is a sort of pre-release but sort of not, and may not be really different from the officially released version.
FYI, someone just released a VESA Windows 3.1x video driver that supports higher colors and resolutions than Microsoft’s generic driver. Wonder if it can be back ported to 3.0, 2.x, and 1.x easily.
https://github.com/PluMGMK/vbesvga.drv
You’d have to ask the author… it depends on how much the driver assumes 386 Enhanced mode and/or a 386 CPU. If it can run as pure 16-bit code including as real mode, it should not be rocket science to support Windows 3.0 and 2.x. Windows 1.x is more work because the driver model changed somewhat, but architecturally it’s not different from Windows 2.x.
Don’t know if you’ve seen this. Source to the SGI IrisVision Windows 3.1x driver is over on Ardent Tool.
https://www.ardent-tool.com/video/Iris.html
Good that there are other examples of accelerated drivers out there.
I’m trying to build a MIDI player for Windows 3.1 using Microsoft Visual C++ 1.52c, and I’ve run into a brick wall: I can’t locate the essential Win16 import libraries (user.lib, gdi.lib, kernel.lib, etc.) required to link against the Windows 3.1 API.
Even though I’ve installed both the Windows 3.1 SDK and Visual C++ 1.52c, those libraries are missing. I’ve checked every version I could find online — including MSDN ISOs, WinWorldPC, and Archive.org — but they all seem to lack these crucial .LIB files for Win16.
These files are needed for basic Windows programming. For example, linking against USER.LIB is required for common functions like CreateWindow, MessageBox, DialogBoxParam, etc.
My MIDI player is a 16-bit GUI program, styled like Winamp, and I’m trying to compile it on original hardware using VC++ 1.52c and Windows for Workgroups 3.11. The program builds fine until link time, when it fails due to missing Windows libraries.
Thank you,
Josh
That is a fascinating question. What gave you the idea that you should be looking for these libraries?
As far as I can tell, USER/KERNEL/GDI and other “core” imports were always part of some combined library. C/S/M/LLIBW.LIB, LIBW.LIB (Microsoft), WINDOWS.LIB (Watcom), IMPORT.LIB (Borland) or something like that.
In MSVC++ 1.52c, I believe LIBW.LIB is what you’re looking for.
This site uses Akismet to reduce spam. Learn how your comment data is processed.