Developing Retro-GUI for Win3.11 (Delphi 1) and OS/2 (Visual C++)

Retrocomputing with real hardware is a very nice way to understand how modern computers have learned to run and talk. I developed some GUI for OS/2 Warp 3.0 and Windows 3.11 to control some of my microcontroller-projects.

Windows 3.x and the Win16-API

To develop a GUI for a modern computer, you can make use of the popular Qt-framework for C++, Android Studio for Java or dozens of different frameworks for Python. But back in the days there was only one API for Windows: the Win16-API. So it came to my mind to develop a GUI application using the Win16-API - just for fun. As I have a valid license for Embarcadero Delphi, that allows me to use older Borland-compilers for free, I searched for some older versions that supports the 16-bit API of Windows 3. The first - and last - Delphi that supported 16-bit-applications was Borland Delphi 1, released on 14th of February 1995. So I downloaded this version and created a small demo-application: one line of code and it simply worked within seconds:


procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage('Hello World!');
end;

enter image description here

But it tasted a bit like cheating using a compiler published in 1995 to write software for Windows 3.1, first published in 1992. So I then installed Microsoft Visual C++ v1.52 from 1993, which was arguably the most common development platform for Microsoft Windows 3.x. And - oh boy - I immediately felt the spirit of the 90s:

enter image description here

Microsoft first added options to develop a GUI-based application via Drag&Drop components onto a pre-designed window with VisualBasic. In VisualC++ all GUI-elements had to be placed using plain code without WYSIWYG. In the following picture you can see some of the added GUI-components to create a GUI for selecting the port number and speed of the COM-port:


hControl = CreateWindow("COMBOBOX", NULL, WS_VISIBLE | WS_CHILD | CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP, 16, 400, 65, 5*20, hWnd, (HMENU)IDC_COMPORT, hInst, NULL);
SendDlgItemMessage(hWnd, IDC_COMPORT, CB_ADDSTRING, 0, (LPARAM)(LPSTR)"COM1");
SendDlgItemMessage(hWnd, IDC_COMPORT, CB_ADDSTRING, 0, (LPARAM)(LPSTR)"COM2");
SendDlgItemMessage(hWnd, IDC_COMPORT, CB_ADDSTRING, 0, (LPARAM)(LPSTR)"COM3");
SendDlgItemMessage(hWnd, IDC_COMPORT, CB_ADDSTRING, 0, (LPARAM)(LPSTR)"COM4");
SendDlgItemMessage(hWnd, IDC_COMPORT, CB_SETCURSEL, 0, 0);

enter image description here

After a fiew minutes I got used to this style of programming a GUI, but having the rapid-development-environments of the 2020s in mind, it was quite hard to keep going on coding. During coding I quickly realized: searching for functions or properties of the Win16-API using Google will not lead to a solution, as most of the search-queries are redirected to Microsofts Win32 API... not helpful. As the internet was not invented in 1993, programmers had to read books (those things with paper in it - not the "e") to learn a computer language. During my journey into the past, I've read some chapters of the book "Programming Windows 3.1" by Charles Petzold and the "Windows API Volume III - Windows 3.1 Reference Guide" by Borland. But Microsoft did a great job implementing a very helpful local Win16 API-reference into the development-environment: the Windows 3.1 SDK is available locally within the help-system:

enter image description here

After a couple of days getting used to the Win16 API, I was able to program a full-featured application for my Windows for Workgroups 3.11 with communication through the serial-port, several UI-elements for controlling my FPGA-based Audioplayer and general user-feedback:

enter image description here

I was now reasonably satisfied, as I had now found two ways to develop software for my old computers: a more convenient way using Borland Delphi 1 and a more "feel the 90s"-way using Visual C++. Nevertheless, I hadn't quite reached my goal yet: our first computer, which my parents gave us as a Christmas present in 1994, had OS/2 Warp 3 installed in addition to DOS and Windows 3.11. How hard could it be to develop a software for OS/2? Well...

Searching for a native language for OS/2

In 1994, OS/2 offered the so called WinOS/2 environment, where you could start Windows 3.x applications using a special compatibility-layer of Windows 3.11. But these apps are not running natively under OS/2 and applications started using this layer are marked with a bold yellow frame:

enter image description here

So I searched for better options. IBM offered the "Restructured Extended Executor" (REXX) to develop own applications (or better "scripts"). Besides this, GCC could be used to compile native applications. But I was searching for something more convenient to develop a native GUI application. During my search I stumbled accross the Windows Libraries for OS/2. With this collection of libraries it should be possible, to run my Windows applications under OS/2 natively. For this, Microsoft compiled the most important Windows-libraries for OS/2: GDI.DLL, KERNEL.DLL, SYSTEM.DLL, USER.DLL and some more files.

After installing WLO under OS/2 a specially marked Windows-EXE can be run directly under OS/2, while the WLO-libraries are redirecting the windows-system-calls to native OS/2-calls. Looking at the spare documentation of WLO, I only had to set the OS2EXEFlags within the NE-EXE-Header to 0x80. So I opened a HEX-editor, searched for the OS2EXEFlags in the NE-header in my Borland Delphi 1 application and patched the value to 0x80:

enter image description here

But when starting the EXE file, OS/2 taught me something else: "SYS1804: The file TOOLHELP.DLL cannot be found". Looking at the date of the WLO-files I realized my problem: the files are dated by 1991 - this means Windows 3.0! Borland Delphi 1 is using several of the enhanced functions Windows 3.1 is offering, including the TOOLHELP.DLL, which implements several high-level-functions.

Yes, I tried copying this file in several version into the WLO-folder of OS/2, but this crashed my App as the TOOLHELP.DLL is calling some functions in KERNEL.DLL, that are not implemented in the WLO-version. Even my attempts to implement my own TOOLHELP.DLL with suitable function calls based on the work of the ReactOS-team (toolhelp.c) failed. And as Microsoft and IBM split up in 1991, no more-recent versions of WLO v1.0 have been published. So I didn't get any further with Borland Delphi 1.

Apps for both Win 3.11 and OS/2

But next to Borland Delphi 1 from 1995 I had another chance with Visual Studio C++ 1.52. This compiler from 1993 is closer to the date of the WLO-libraries. So I took my C++ Application and patched the specific NE-header-byte to 0x80 - and it simply worked without any additional work. It was nice to see my app using the native style of OS/2:

enter image description here

Even the hardware-communication worked well as the system-calls to the serial-port were redirected to the OS/2-drivers, as well. I realized this, as I was not able to communicate with 115.2kBaud anymore: OS/2's native serial-port driver had a bug with higher baudrates. Also here I found a solution using the alternative SIO2K-driver made by Ray Gwinn.

To make setting the NE-header byte a little more convenient, I developed a small command line tool that sets the appropriate byte-flag from 0x08 to 0x80. In the end I'm now able to create applications using the same compiler and code-base for three different architectures (16-bit, 32-bit and 64-bit) and multiple operating-systems:

  • Windows 3.11 (16-bit)
  • OS/2 Warp 3 (32-bit)
  • Windows 95/98/ME (32-bit)
  • Windows NT/2000/XP (32-bit)
  • Windows Vista/7/8/10/11 (32-bit and 64-bit)

Why 32-bit and 64-bit? Well, 32-bit Windows-versions were able to run 16-bit applications using a virtual compatibility layer: NT Virtual Dos Machine (NTVDM). With the 64-bit versions this support has been dropped by Microsoft, but by installing the OTVDM (an altered version of winevdm, which is a 16-bit Windows emulator) under 64-bit Windows, my Windows 3.11-applications starts without any problems:

enter image description here

In the end I created an updated version of this app developed in a combination of Delphi 1 and Delphi 7 to create a GUI for all Windows starting with version 3.11 to 11 using the same code-base:

enter image description here

What a journey - but it was great!

Comments