Virtualization of Microsoft Windows 9x systems is a bit problematic due to 2 major bugs: TLB invalidation bug and CPU speed limit bug.
This program contains a set of patches to fix these bugs, and can be booted from a floppy on a virtual machine. It either applies the patch to the installed system, or it patches the installation files in order to create (relatively) bug-free installation media.
MS Windows 98 won't run on newer CPU (even in a virtual machine) due to the "TLB invalidation bug". The bug is described here: https://blog.stuffedcow.net/2015/08/win9x-tlb-invalidation-bug/
If you want to run a virtual machine without restriction with Windows 98 on AMD Zen 2 and newer (Ryzen 3000+) or Intel Core 11th generation and newer (code names Rocket Lake, Tiger Lake), you probably need this patch.
Windows 95 and 98 FE (first edition) won't run on any CPU with frequency of ~2GHz and more due to dividing by zero in the CPU speed test. The bug is described and located here: https://www.betaarchive.com/forum/viewtopic.php?t=29224
There is also an older patch for this bug FIX95CPU, but it's only for Windows 95, doesn't integrate the "divide by zero" protection and also it's incrementing the number of cycles in the testing loop only 10 times of original which probably isn't enough for future CPUs. You can test your CPU speed with this command:
patcher9x -cputest
Currently supported operation systems:
Windows 95, 98 and 98 SE have the CPU speed limit bug. Windows 98 SE is a special case because it has "divide by zero" protection, but it also has a short loop test, so its timing information is probably useless and could potentially lead to other bugs.
Windows 98, 98 SE and Me have the TLB invalidation bug. The Millennium edition has this bug in the code, but from my observation, the system calls this code very rarely (for example with driver install/system update), so the bug isn't as obvious as in Windows 98. An older version of my patcher program also has a special mode for Windows Me, but in the current version it is no longer needed.
Some updates install newer version of some files, for example VMM.VXD
: Q242161 (98 FE), Q288430 (98 SE), ME296773 (Me); NTKERN.VXD
, IOS.VXD
, ESDI_506.PDR
, SCSIPORT.PDR
and NDIS.VXD
: Unofficial FIX95CPU. The patcher is compatible with these updates but is required to apply patches again after install. The best way is probably to run patcher from Windows (DOS or 32-bit version) before reboot - Windows 9x does not have any system files protection and you can overwrite system files in the running system. If you do not catch it and the system is rebooted to an error message, you will need to boot from a bootable floppy and start the patcher from it.
If you already applied this patch, you don't to need install patcher update - currently all the changes are for the patcher program, the patched code is the same. If you want to overwrite previous patches, you can safely run the patcher again and it automatically determines which files are suitable for patching and which are already patched.
Binary files and bootable floppy image are in Releases
IMA file is a bootable floppy (FREEDOS) usable in a virtual machine to simple boot and patch the installed system. Binaries for win32 are Windows 98 compatible, so they can be run from safe mode (Hold CTRL on start-up). Binaries for other systems are for creating patched installation (in theory you can mount virtual HDD image and patch installed system on it, but doing it using boot the floppy is much simpler).
The tool has also been packaged for Nix/NixOS:
$ nix develop nixpkgs#patcher9x
or
$ nix-shell -p patcher9x
The simplest way is by downloading bootable floppy image. After booting (you will see A:\
), run:
patch9x
Patch will be run in interactive mode and the default strategy (patch files, VMM32.VXD will be patched directly) is probably the best way even for later updates. After rebooting operation system should start successfully.
This is default mode, the program asking questions and user answear. You can just double click on EXE (or type patch9x
to DOS command prompt) and program guide to you in patching process. For Linux build, the help is prinded if no arguments are given (default behaviour for UNIX programs) so you need specify path.
Same as interactive but don't ask anything. Can be enabled with -auto
switch and path to CAB files or 9x WINDOWS/SYSTEM directory needs to be specified.
In this mode program operate with single steps. Examples:
Extract VMM32.VXD
from instalation media
patcher9x --cabs-extract D:\WIN98 VMM32.VXD
Extract VMM.VXD from VMM32.VXD
patcher9x --vxd-extract VMM32.VXD VMM.VXD
Patch individual File
patcher9x --patch-tlb VMM.VXD
Patch VXD archive
patcher9x --patch-all VMM32.VXD
Boot floppy now contain CD driver and few utilities to prepare system disk. If you wish run Windows Installer from boot floppy, add /NM
switch to setup.exe
(because of different memory manager, the setup cannot determine real RAM size). Utilities are listed in boot/info.txt.
Copy the content of win9x folder (or win95 - for Windows 95 or win98 - for Windows 98) from a CD / extract it from an ISO image. Then run:
patcher9x /path/to/folder/win98
If the patch is successful, you can copy the modified files back to the image. These are the files marked with the 'N' flag in the patcher output. Or it will be one of these:
VMM32.VXD
VMM.VXD
NTKERN.VXD
IOS.VXD
ESDI_506.PDR
SCSIPORT.PDR
NDIS.VXD
The Windows installer primarily takes files from the installation folder and if it can't find them, it'll scan the CAB archives instead.
For Windows 95, it is better to patch the installation media, or you will need to install the patch twice - after the first reboot and again after the installation of the network.
Please note, that file VMM32.VXD
from installation IS NOT THE SAME as the file in Windows/system
folder. Don't interchange them! See the Patching process section to know more about the VMM files.
Check patcher9x thread at Vogons: https://www.vogons.org/viewtopic.php?f=24&t=88284
Check GPU driver for Windows 9x: https://github.com/JHRobotics/softgpu
To build from source you need:
To build the binary for your computer, simply type:
make
To cross compile, specify the HOST_CC
and GUEST_CC
variables to choose compiler, for example cross compiling to 32bit Windows:
make HOST_CC=gcc GUEST_CC=mingw-w64-i686-gcc
To produce a production binary, add RELEASE=1
to make
.
After compiling, you can strip the binary (reduce some space):
make strip
There is a special profile for DOS cross-compilation -- if you have DJGPP compiler, you can produce the DOS executable this way:
make RELEASE=1 PROFILE=djgpp
make strip
MinGW compiled programs are linked with msvcrt.dll
by default. To eliminate this depency (msvcrt.dll
is missing all Windows 9x versions where isn't IE 4 or better), this project can use my NOCRT library. Use make PROFILE=nocrt
or make PROFILE=nocrt64
to compile program for Windows with depency only to kernel32.dll
.
Executable file name for most real operation systems is called patcher9x
. For DOS, it is called patch9x.exe
because file names are limited to 8+3 characters.
The patch itself is relatively simple: injecting 2 instructions (mov ecx,cr3
and mov cr3,ecx
) to the code in the VMM.VXD
driver. The patch totally modifies 29 bytes in this file (for Windows 98).
The problem is that VMM.VXD
isn't in a normal state on the HDD. VMM.VXD
is a part of VMM32.VXD
which is a compressed archive of several VXD
drivers. VMM32.VXD
isn't a generic file -- it is generated by the installer exclusively for your HW configuration.
The system for loading drivers first searches in the SYSTEM\VMM32
folder, and if the driver isn't there, it will search in the VMM32.VXD
file.
If you want to know more about the code, see file FlushMappedCacheBlock.asm
In the beginning, I increased the number of cycles of older patches by 8 times (80 times more than the original code and in the case of NDIS.VXD, 100 times more than the original code). I also injected a small "divide by zero" protection, which looks like this:
VMMcall Get_System_Time ; original - system time (in ms) to EAX
sub eax,esi ; original - compare with last time in ESI
jnz skip_inc ; new - skip if result is non-zero
inc eax ; new - increase zero result by one
skip_inc:
There are two variants of this code - one for NDIS.VXD
and one for all other system files.
The new number of test cycles is now 80 000 000 -- in older patches it was 10 000 000, but a 4-year-old Ryzen 5 1400 CPU only needs 3 ms to run it through, so if you have a CPU which is 3 times faster[^1] you could have a problem here.
If you want to know more about the code, see file speed.inc
[^1]: Single thread performance in LOOP
instruction, number of cores, special instructions etc. are irrelevant.
In future I would like include patch "CPU speed limit" (95, 98 FE) and patch 48-bit LBA (95, 98, ME). Memory limit patch I want include too.