https://www.os2museum.com/wp/win16-retro-development/ OS/2 Museum OS/2, vintage PC computing, and random musings [os2floppy] Skip to content * Home * About + Wanted List * OS/2 History + OS/2 Beginnings + OS/2 1.0 + OS/2 1.1 + OS/2 1.2 and 1.3 + OS/2 16-bit Server + OS/2 2.0 + OS/2 2.1 and 2.11 + OS/2 Warp + OS/2 Warp, PowerPC Edition + OS/2 Warp 4 + OS/2 Timeline + OS/2 Library o OS/2 1.x SDK o OS/2 1.x Programming o OS/2 2.0 Technical Library + OS/2 Videos, 1987 * DOS History + DOS Beginnings + DOS 1.0 and 1.1 + DOS 2.0 and 2.1 + DOS 3.0, 3.1, and 3.2 + DOS 3.3 + DOS 4.0 + DOS Library * NetWare History + NetWare Timeline + NetWare Library * Windows History + Windows Library * PC UNIX History + Solaris 2.1 for x86 - Undefined Isn't Unpredictable Win16 Retro Development Posted on December 20, 2022 by Michal Necasek Several months ago I had a go at producing a high resolution 256-color driver for Windows 3.1. The effort was successful but is not yet complete. Along the way I re-learned many things I had forgotten, and learned several new ones. This blog entry is based on notes I made during development. [boxv31-word-640x480]Windws 3.1 running Word in a usable resolution Source Code and Development Environment I took the Video 7 (V7) 256-color SuperVGA sample driver from the Windows 3.1 DDK as the starting point. The driver is written entirely in assembler (yay!), consisting dozens of source files, over 1.5MB in total size. This driver was not an ideal starting point, but it was probably the best one available. The first order of business was establishing a development environment. While I could have done everything in a VM, I really wanted to avoid that. Developing a display driver obviously requires many restarts of Windows and inevitably also reboots, so at least two VMs would have been needed for a sane setup. Instead I decided to set everything up on my host system running 64-bit Windows 10. Running the original 16-bit development tools was out, but that was only a minor hurdle. The critical piece was MASM 5.NT.02, a 32-bit version of MASM 5.1 rescued from an old Windows NT SDK. The Windows 3.1 DDK source code is very heavily geared towards MASM 5.1 and converting to another assembler would have been a major effort, likely resulting in many bugs . Fortunately MASM 5.NT.02 works just fine and assembles the source code without trouble. For the rest, I used Open Watcom 1.9 tools: wmake, wlink, and wrc (make utility, linker, and resource compiler). I used a floppy image to get the driver binary from the host system to a VM, a simpler and faster method than any sort of networking. With everything building, the real fun started: Modifying the Video 7 driver to actually work on different "hardware". Trials and Tribulations Fortunately there was not a huge amount of Video 7 specific code in the sample driver. Unfortunately the hardware specific code was sprinkled throughout the code base. My first change was to unify the bank switching code, which is critical for performance. The sample driver had about half a dozen different bank switching routines (no, I don't know why). I replaced them with one, and made sure the bank switching is only done when necessary (i.e. the current bank differs from the requested one). Why not use a linear framebuffer, you ask? From the beginning, I did not want to restrict the driver to 386 Enhanced mode Windows. Using a LFB in Standard mode is difficult; it's easy to reprogram a selector base, but that breaks down when the system runs with paging and the LFB is not mapped. Even worse, in real mode a LFB just can't be used, period. Then came the painstaking work of removing the V7 specific drawing code. There was more of it than I'd expected. The V7 hardware has latches and pattern blit registers that accelerate certain operations. Now, there were pure software fallback drawing paths more or less everywhere, but not at all clearly identified. It took some effort to force all drawing to go through the software path. There was one very nasty bug related to this; the driver assumed that the hardware pattern blit registers take care of pattern rotation. Forcing pure software drawing could lead to a situation where the pattern was not correctly rotated. This caused very visible problems when dragging windows around, as the selection rectangle (a patterned line) was prone to leaving "droppings" behind. I ditched any attempts to use offscreen memory in the driver. While that is usually a performance win on real hardware, it's not in a VM. Due to the bank switching overhead, moving data from system memory is always faster. The drawing code in the V7 driver assumes that a single scanline never crosses a bank boundary; that significantly simplifies the drawing logic because there's no need to potentially switch banks between two adjacent pixels. The original driver used a 1K pitch, allowing maximum horizontal resolution of 1024 pixels. I changed that to 2K, which enables resolutions up to 2048 pixels horizontally. This could potentially be made more flexible in order to conserve video memory, but at least for the time being that didn't seem worth the effort. The mouse cursor drawing code had another nasty bug in it. The V7 driver would more or less always use the hardware cursor and the software fallback was probably very rarely used, if ever. Under some circumstances, the routine to save screen contents under the cursor could be entered with the direction flag set, and ended up copying data in the wrong direction and overwriting innocent memory. A single CLD in the right place fixed that. I also had to contend with the question why the colors in my driver are different from the Windows 3.1 VGA/SVGA drivers. I learned that for whatever reason, the Windows 3.0 and 3.1 8514/A driver (the canonical high-res driver) really used a different color scheme. This was documented in the Windows 3.0 and 3.1 DDKs, although no explanation was provided as to why the colors should be different. Tools Fun The linker (wlink) caused one very interesting problem. By default, wlink enables far call optimization, replacing far calls with near ones. This optimization is almost always safe and a performance win, but not in the case of the Windows display driver. The driver "compiles" drawing routines by copying fragments of code from the code segment on the stack, assembling a selection of them together as needed and modifying constants within the code. Now, wlink optimized the far calls within the code segment, which would have been fine, but when that code got copied on the stack, calls to the code segment really needed to be far. Disabling the far call optimization was trivial once I knew what the problem was. As a side project, I also wrote a quick and dirty wmapsym tool, a functional equivalent of Microsoft's MAPSYM but using Watcom map files as input. This proved extremely useful when debugging the driver. Apropos debugging--the tool for Windows 3.1 driver debugging is WDEB386, a more or less standard Microsoft-ish debugger similar to SYMDEB, the OS/2 kernel debugger, NTSD, and others. I used it with input and output redirection to a serial port; this was routed to a pipe on the host, and PuTTY attached to the pipe. [boxv31-putty-640x405]WDEB386 in PuTTY Going More Retro The display driver functionality in Windows 3.1 Standard and 386 Enhanced does not matter much. The one area where there's a major difference is DOS session support. In 386 Enhanced mode, there's a whole dedicated VxD (VDDVGA) that handles video virtualization. Even when switching to a fullscreen DOS session, the display driver remains active in 386 Enhanced mode, but it is notified via dev_to_foreground and dev_to_background calls that it's going to the background or coming back. In Standard mode, the driver is shut down via the Disable call when switching to a full-screen DOS session, and re-initialized via Enable on the way back. Things started getting even more interesting with Windows 3.0. In Standard and 386 Enhanced mode, the differences from Windows 3.1 are minimal. But Windows 3.0 running in real mode is a different beast. I had to modify the driver to not use any APIs available only in protected mode and decide at runtime (using WinFlags) what to do. It would have been lovely if I had the Video 7 sample driver from the Windows 3.0 DDK. Alas, I never managed to find it. Anyone? Windows 2.x was more work to get going. The basic structure of the driver is the same, there are just fewer GDI calls the driver needs to implement. For the most part, Windows 2.x is extremely similar to Windows 3.0 in real mode. The difference is that the API calls added to support protected mode (such as AllocCSToDSAlias) do not exist in Windows 2.x at all. The drawing code is essentially identical, but the driver initialization and teardown need to be slightly different. In theory it might have been possible to import the Windows 3.x specific routines dynamically, and use a single binary for Windows 2.x and 3.x. In practice that is not workable because the drivers also need a different format of resources (Microsoft significantly changed the resource format between Windows 2.x and 3.0). It was therefore much simpler to create a separate Windows 2.x driver binary and use conditional compilation for using either Windows 3.x or 2.x code paths. A related complication was that I could not find a resource compiler capable of dealing with Windows 2.x resources and running on 32-bit Windows. I resorted to running RC from a Windows 2.x SDK in a DOS VM in order to finalize the 2.x driver binary. Not pretty but fully functional. All in all, it was an interesting retro development trip. And there's more work to be done. This entry was posted in Debugging, Development, Microsoft, Windows. Bookmark the permalink. - Undefined Isn't Unpredictable 9 Responses to Win16 Retro Development 1. [b8fb] Jason Stevens says: December 20, 2022 at 3:49 pm I've been playing with Hack-1.03 and cross building for quite a few compilers with my main one being Microsoft C 6.0a. I use the MS-DOS Player from here: http://takeda-toshiya.my.coocan.jp/msdos/index.html It works like a 'bind' exe, so I wrap CL.EXE, and LIB.EXE. To link, I took the linker Version 5.60.220 from Visual C++ 1.52 on the Visual C++ 2.0 CD-ROM, along with nmake. While I'm not currently using any assembly, I do want to be able to do some invalid opcodes to trigger stuff in an emulator so I'll have to try this old MASM. Since ms-dos player is interpretive it is slow, making the use of makefiles all the more important, but it doesn't seem too bad to me. I had all kinds of weird cli/argument issues with the linker from Microsoft C 5/6 and found the one from VC 1.52 worked far better as a drop in replacement. I've found the Watcom linker less than satisfactory. 2. [8f7d] Michal Necasek says: December 20, 2022 at 4:27 pm I am aware that this is a possibility. Building the driver takes several seconds on a very fast machine using native tools, so using emulation probably will be quite a lot slower. If I can run native code, I very much prefer that. I know the Watcom linker very well so I can make it do what I need. Didn't have any trouble with it really. It can link the Win16 display drivers just fine. I'm sure YMMV. 3. [5791] Radoslaw Sokol says: December 21, 2022 at 11:09 pm "Windows 3.1 running Word in a usable resolution" Well, this is definitely true, but I see it partly as a joke as well In fact, we have to admire how compact the Windows UI was. It had been designed for 640x480 and it showed. In both its initial incarnations (1.x/2.x/3.x and then 9x/NT4/2000) it was meant to fit on a VGA screen, even if it was a tight fit. Any better resolution just let the user fit more on screen. From my past experience with Windows 3.x, I'd say that: * Hercules, EGA and 640x480 were barely usable, and definitely not very useful. You could do some serious work, but it was painful, and there was definitely no way to enjoy multitasking. Just a bunch of full-screen apps and a lot of Alt+Tabbing, and Word reduced to 90% zoom. * 800x600 was "just enough". One could do most of the work quite effectively, and even use non-full-screen apps most of the time, unless they needed more viewing area (CorelDRAW!, Word, programming environments) * 1024x768 was "perfect" - but with small fonts only. And it needed a damn good monitor. Not much of a surprise that the large fonts variant of this mode was more common for some time, which in turn effectively reduced the screen estate to something in between 640x480 and 800x600, but with higher quality text and graphics. (Small/Large Font modes is something that's rarely mentioned in the retro community, by the way) * 1280x1024 was a dream. I think it was reserved to professionals with their huge 19'' and 21'' monitors. CAD, graphics and so on. It was pretty much unobtainable without a good (and expensive) graphics card and a good (and expensive) monitor. * Anything more? Unheard of, as far as Windows 1.x/2.x/3x is concerned, with the exception of some more obscure graphics systems perhaps (full-page A4 displays, for instance). 4. [8f7d] Michal Necasek says: December 22, 2022 at 12:06 pm Yes, I totally agree that 640x480 was not quite enough, while 800x600 was not great yet already usable. 1024x768 was quite good and above that was luxury. Not only did that need an expensive monitor but also a good graphics card. There were always systems that could run Windows in high resolution, but far from common -- it was probably a few thousand dollars extra. I always hated the large fonts thing because it made the screen effectively smaller. I still don't like it with Windows 10 5. [63de] Richard Wells says: December 22, 2022 at 9:59 pm Using small fonts is the sign that one is still young. Aging yields a desire for larger fonts. Windows made it comparatively easy to have the user assign the optimum looking fonts for their preferred resolution. It annoyed me how many designers decided to override the user's choice for designer's choice. 1024x768 was the resolution that would not die being the highest resolution affordable monitors could produce for about a decade. 6. [d18f] Malcolm says: December 23, 2022 at 2:02 am Is it feasible to use MS-DOS player to bind the 2.x RC but leave the rest of the tools as native Win32? In my collection of tools, I don't even see a (Microsoft) 3.x RC that's native Win32 - even when the compilers and linkers are, this piece seems purely DOS. Do others have a PE version of this (and if so, where?) For build performance, does wmake support multi-process builds? I've been maintaining my own make (ymake) which aims to be nmake compatible but retrofitting multi-process execution. Obviously this requires makefiles that specify dependencies correctly and don't have multiple tasks try to update the same shared files, etc. On modern hardware sequential compilation seems needlessly frustrating. (PS. I survived on 640x480 until 2002. It turns out when you don't know what you're missing, anything is usable.) 7. [704a] Yuhong Bao says: December 23, 2022 at 12:39 pm 640x480 at 16 colors? 8. [8f7d] Michal Necasek says: December 23, 2022 at 1:14 pm Yes, I expect it would be feasible to tweak just the Windows 2.x resource compiler. On the other hand, I have to move the driver to a DOS machine and copy it to the corresponding Windows directory, and it's not hard to run RC there. No, the build is not parallel, but it takes about 3 seconds from scratch. A different make tool could be used, but I would have to build the driver from scratch a lot to spend even five minutes on parallel building and have any chance of ever getting that time back. With bigger projects it's a very different story. 9. [177f] Stu says: December 25, 2022 at 12:19 am This is fantastic work. Are you going to look at having the resolution able to change dynamically, aka guest extensions ? Are you going to look at more colours too ? I know DOSEMU2 could really do with a driver this worked with. Leave a Reply Your email address will not be published. Required fields are marked * [ ] [ ] [ ] [ ] [ ] [ ] [ ] Comment * [ ] Name * [ ] Email * [ ] Website [ ] [Post Comment] [ ] [ ] [ ] [ ] [ ] [ ] [ ] D[ ] This site uses Akismet to reduce spam. Learn how your comment data is processed. * Archives + 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 * Categories + 286 + 386 + 3Com + 3Dfx + 486 + 8086/8088 + Adaptec + AGP + AMD + AMD64 + Apple + Archiving + Assembler + ATi + BIOS + Books + Borland + BSD + Bugs + BusLogic + C + C&T + CD-ROM + Cirrus Logic + CompactFlash + Compaq + Compression + Conner + Corrections + CP/M + Creative Labs + Crystal Semi + Cyrix + DDR RAM + Debugging + DEC + Development + Digital Research + Documentation + DOS + DOS Extenders + Dream + E-mu + Editors + EISA + Ensoniq + ESDI + Ethernet + Fakes + Fixes + Floppies + Graphics + Hardware Hacks + I18N + IBM + IDE + Intel + Internet + Keyboard + Kryoflux + Kurzweil + LAN Manager + Legal + Linux + MCA + Microsoft + MIDI + NetWare + Networking + NeXTSTEP + NFS + Novell + NT + OS X + OS/2 + PC architecture + PC hardware + PC history + PC press + PCI + PCMCIA + Pentium + Pentium 4 + Pentium II + Pentium III + Pentium Pro + Plug and Play + PowerPC + Pre-release + PS/2 + QNX + Quantum + Random Thoughts + RDRAM + Roland + Ryzen + S3 + SCO + SCSI + Seagate + Security + Site Management + SMP + Software Hacks + Solaris + Sound + Sound Blaster + Source code + Standards + Storage + Supermicro + TCP/IP + ThinkPad + Trident + UltraSound + Uncategorized + Undocumented + UNIX + UnixWare + USB + VGA + VirtualBox + Virtualization + VLB + Watcom + Wave Blaster + Western Digital + Windows + Windows 95 + Windows XP + Wireless + WordStar + x86 + Xenix + Xeon + Yamaha OS/2 Museum Proudly powered by WordPress.