Win3mu Part 5 — Windows 3 Executable Files
--
This is Part 5 in a series of articles about building Win3mu — a 16-bit Windows 3 emulator. If you’re not sure what this is or why I’m doing it you should read from the start.
This post briefly describes the Windows 3 .exe file format — the basic structure, a little about Win3mu’s implementation and some anomalies discovered along the way.
Basic Structure
Windows 3 .exe files use the “New Executable”, aka “NE” file format. Both .dll and .exe files use this format and it’s the basis of dynamic linking — the mechanism by which Windows loads and links modules together at run-time.
NE files contain:
- MZ Header — a DOS executable header that lets DOS load the file and run a little stub program.
- DOS Stub — a little stub program which simply prints “You need Windows” if you try to run the file directly from DOS.
- NE Header — information required to run the program under Windows as well as pointers to other important parts of the file.
- Referenced Module Table — the names of any other modules used by this module.
- Segment Table — information about each of the program’s code and data segments.
- Entry Table — information about various entry points into the program as well as internally referenced locations.
- Code Relocation Tables — describes how to patch code addresses once the module has been loaded into memory and the addresses of imported function are known.
- A Resource Table — a list of resources and where they reside in the file
- A Resident Name Table — names of functions exported from this file
- A Non-Resident Name Table — names of functions imported from other modules
- The Code and Data Segments
- The Resources
Most of the details for this I found through various online references:
- Microsoft documentation (unfortunately I didn’t find this until I’d figured most of it out).
- www.program-transformation.org (seems to have disappeared in the last couple of days — here’s an archived copy)
- osdev.org wiki page
- Old PC Mag articles — here and here
- Source code for viscons-shell for resource format info
Code and Data Segments
The bulk of the .exe file consists of the code and data segments. Code segments contain the x86 instructions that Sharp86 will be running. The data segments contain other data referenced by the program’s code.
Each segment can be up to 64k in length. Eventually each will be mapped to a selector. Code segments will be marked as read-only+executable, data segments as read/write.
Windows supported other flags on these segments too — like moveable and discardable but Win3mu ignores these since it doesn’t have to deal with the tight memory limits that Windows 3 did.
Relocations
Code segments have a relocation table — a list of places within the code segment that need to be “fixed up” once it’s loaded into memory.
Relocations include things like updating referenced addresses in other segments and filling in the addresses of any imported functions.
(Much of the online documentation neglects to mention that most relocations point to a chain of addresses. The relocation entry holds the address of the first relocation and at that address is the address of the next fix up. The module loader needs to follow the entire chain and fix up each location).
Resources
NE files support resources. Resources are embedded assets such as icons, bitmaps, menus etc… and each resource has a type and a name.
Resources can referenced by a name or by an ordinal (basically a fancy name for id number). For simplicity Win3mu coverts all ordinals to strings eg: ordinal 101 would be renamed to the string “#101". This just makes passing the names around in C# a little easier.
NeFileReader Class
In Win3mu the NeFileReader class is responsible for loading and parsing NE files. It takes the path to a file, reads all the relevant headers and lookup tables, provides methods for loading other parts of the file as required and properties to expose key information:
NeTool
Originally I planned to write a little utility program “NeTool” that would use the NeFileReader class to read an .exe file and dump out all the internal info (ie: a more advanced version of exehdr). I thought it would be a handy tool to have — and it probably would be — but I just haven’t had much need for it.
Instead, whenever Win3mu loads a module it dumps all the important information to its log file. It also generates a map — a top to bottom listing describing the contents of each section of the file. I’ve found that this log combined with a hex dump of the .exe works well enough.
If you’re interested, here’s a typical module listing and here’s a file map.
The Mysterious Resource Type #15
In the couple of week since I originally wrote the NeFileReader class I’ve come across one interesting twist. While trying to get the game Wordzap running I noticed that its menu was missing:
Opening the .exe file in AppStudio showed the menu had a string name of “WORDZAPMENU”:
But when NeFileReader listed out the resources the menu had ordinal number #1 and no string name:
After double checking everything I was left a little stumped — so I just searched the file for the string “WORDZAPMENU”:
Cross referencing these back to the file map shows that the second string (at address 0x00011A19) was in a resource called “15/#1".
Cross referencing this back to the Windows.h shows there’s no such thing as resource type 15:
After searching for a bunch of different phrases that I thought might bring up some information and still not finding anything, just on a whim and guessing that it looked like a name table tried searching for “RT_NAMETABLE”. That led me to this header file from an old Watcom compiler that mentions it:
And that’s where the trail ended. I still haven’t found any documentation on it except one other header file that mentioned it was now obsolete and no longer supported.
However, knowing that it did once exist I decided to look at its contents:
And it wasn’t too hard to figure out the format:
struct
{
WORD lengthEntry; // length of this entry
WORD resourceType; // see RT_xxx constants
WORD resourceId; // ordinal of resource | 0x8000
BYTE paddingZero; // maybe, not sure??
CHAR szName[]; // null-terminated name
}
NeFileReader now checks for a RT_NAMETABLE resource and if found automatically applies the resource name:
and the menu now loads:
To Sum Up
So that’s a quick overview of the NE file format. If you want the nitty-gritty, field by field details see the links above. Otherwise that’s enough to be able to follow along with the rest of these articles.
From here on I’ll be describing the core of the emulator and in the next post I’ll be talking about memory management — specifically the global and local heaps.
Progress Update
ICYMI, my game Clink that I wrote in the ’90s is now fully functional under Win3mu:
Even with no performance tuning and some glaringly obvious areas that need work it runs surprisingly well — maybe 5% higher CPU burn than the same program running under VirtualBox.
I’ve also noticed that running under Win3mu the programs feel a lot better compared to VirtualBox and/or DOSBox. It might be just that that response of the mouse is more natural.
I’m now working on Wordzap (since that’s one of the games Jen likes to play). Unfortunately, but it’s being stubborn… more about that later.
Hi, I’m Brad Robinson — an independent software developer living in Sydney Australia. I write software for musicians and as an indie developer I rely on word of mouth.
If you enjoyed this article please consider sharing it by hitting the “recommend heart” below or by sharing on Facebook/Twitter. It’s a small gesture but makes a real difference.
Also, if your feed is lacking in hex dumps, disassembly listings and screen shots of old Windows 3 games you might like to follow me on Twitter.
Continue reading… Part 6 — Memory Management.
Hacker Noon is how hackers start their afternoons. We’re a part of the @AMI family. We are now accepting submissions and happy to discuss advertising & sponsorship opportunities.
If you enjoyed this story, we recommend reading our latest tech stories and trending tech stories. Until next time, don’t take the realities of the world for granted!