Windows 3.1 and Windows NT 3.51 on Fujitsu Siemens S210 thin client
My Fujitsu S210 thin client, bought in 2022, is still working well on Windows 98, with drivers for the SIS315E graphics card, VT8233 sound card and VT6102 Ethernet adapter. Still, I missed the Windows 3.1 user interface and decided to get it fully working on Windows 3.1 once again. When I tried in 2022, although I was able to get SVGA (1024×768) to work, I could not find any working Windows 3.1 drivers for the VT8233 AC97 codec or the LAN card and gave up.
Three years later, the situation has changed. There are now quite a few options for sound blaster emulation for DOS. Using Baron-von-Riedesel’s VSBHDA, a Sound Blaster and MPU401 emulator, my sound card (VT8233) was detected right away and Adlib’s TEST.EXE worked fine:
Although DOSMID crashed when used with VSBHDA, GSPLAY1 worked out of the box:
Audio in DOS games such as Prince of Persia 2, Super Angelo, Charlie the Duck or Charlie II worked fine. Prince of Persia 1.3 would hang if Sound Blaster was selected for audio output, but would work in MT32 mode. For the life of me I couldn’t find out what the problem was. I tried multiple versions of Prince of Persia 1 but the exact same issue persisted.
VSBHDA Sound Blaster emulation in Windows
Because VSBHDA required the JEMMEX memory manager, which did not implement the GEMMIS API, I had to run Windows in standard mode to get the emulated Sound Blaster 16 working. If the default Sound Blaster driver installer doesn’t work on your setup, try to install the drivers on DOSBox, extract the relevant SYSTEM.INI entries, and copy it to your S210. Of note, VerifyInt=0 may need to be added to SYSTEM.INI, otherwise card initialization may fail.
Initially I encountered noisy audio using VSBHDA 1.8 driver on Windows. The author was very helpful and provided detailed instructions for me to compile a patched version of the driver using OpenWatcom 1.9. With the patch, I was finally able to get audio to play properly in Windows.
This screenshot shows MIDI playback using MediaSauce player. WinProbe is in the background showing sound card configuration. The WAV/MIDI/CD tabs worked well, while the Video tab would hang Windows as soon as video playback was attempted. I suspect it didn’t like my standard mode setup.
To further improve MIDI playback quality, install FM MIDI Synth driver Version 2.14 by Jamie O’Connell. Otherwise, on some MIDI files, many notes won’t be audible using the default MIDIMAP.CFG that comes with the Sound Blaster 16 driver.
In Windows 3.1, only one application can use the sound card for PCM playback at any time. Trying to use MediaSauce to play WAV files with IIS WinPlay3 already running will result in an error. It would work however if you play MIDI in MediaSauce and MP3 in IIS WinPlay3.
When I first got the emulated Sound Blaster to work in Windows, mono WAV/MP3 files would play normally while stereo files would play twice as fast, as if the sampling rate had been doubled. The same WAV file would play correctly using Creative’s sample player. It took me a while to trace this behaviour to the mono output of the Sound Blaster 2.0 card. Likely, playing a stereo file (2 channels) will result in the driver dumping PCM audio data on the only output channel on the card, making it look as if the sampling rate had been doubled. I fixed the issue by installing the Sound Blaster 16 driver. Take note that the Sound Blaster 16 DOS installer (which installs the Windows drivers too) requires at least 600KB of free conventional memory; otherwise it might crash complaining “out of memory” when modifying Windows configuration.
Networking in Windows 3.11
After some searching I was able to find the Windows 3.1 driver for the VT6102 network card. Microsoft TCP/IP-3.2b then installed successfully. Internet Explorer 3.0 and Samba file sharing also worked well. However, my Windows network driver then refused to load in standard mode and I needed to create different CONFIG.SYS menu to load Windows in standard or enhanced mode to use either VSBHDA sound or networking.
This is IE 3.0 in enhanced mode, showing www.textfiles.com. WS-FTP is connected to my home FTP server. I have no issues browsing Samba shares using Total Commander:
There is this weird issue with the driver for our VIA Ethernet adapter. Once in a while, the NDIS driver will refuse to initialize, complaining about an error in PROTOCOL.INI. To recover, one must disconnect power to the thin client, disconnect the LAN cable, and press the POWER button a few times to discharge any redisual voltage, before powering it on again. A mere warm reboot will not work. The issue will usually happen after I switch between DOS, Windows 3.1, Windows 98, and Windows NT 3.51 in quick succession. Likely one of the drivers has set one or more flags which does not get cleared and results in the card being stuck in an undefined state until a cold reboot.
The screenshot below shows the issue as it occured on the NDIS2 DOS driver. If the issue happens in Windows 3.1, Windows will complain that the card cannot be detected. I have not seen this issue on Windows 98 or NT 3.51.
Starting Windows with the Microsoft Network Client 3.0 for DOS fully loaded (e.g. with all the net initialize stuff) will result in an error: “Invalid VxD dynamic link call to device number 0028, service 8031. Your Windows configuration is invalid”. You should not load both the Windows 3.11 and DOS network stack at the same time. If the Windows 3.11 network stack is loaded, AUTOEXEC.BAT should only contain a single “net start” line, leaving the rest to Windows. I guess for this reason Windows setup loves to comment out any lines in AUTOEXEC.BAT which it thinks are trying to initialize the network, marking those with “REM – By Windows Setup”. In all likelihood, this will just mess up your AUTOEXEC.BAT and CONFIG.SYS, especially if MS-DOS multiple configuration is used.
Standard mode in Windows 3.11 for Workgroups
By default, Windows 3.11 for Workgroups will not run in standard mode, only enhanced mode. To force it to run in either standard mode or enhanced mode, copy WIN.COM, SYSTEM\DSWAP.EXE, SYSTEM\SWAP.EXE from a Windows 3.1 installation, as well as SYSTEM\DOSX.EXE from the Win31 subfolder of VSBHDA package, and overwrite the original files. Then set 286grabber=vgacolor.ini in SYSTEM.INI. Take note that WIN.COM may be reverted to the original version if you run Windows Setup, either from within Windows or by running SETUP.EXE, causing the ability to run standard mode to be lost. Occasionally, if running an MS-DOS session in 286 standard mode results in Jemmex exception 06, you might need to reboot the machine.
I tried using Trumpet Winsock with WINPKT.COM on top of the DOS packet driver to access the network in standard mode. Unfortunately, although Trumpet is able to communicate with WINPKT.COM and the packet driver, showing the correct MAC address, pinging any IP would just timed out. I tweaked many settings such as IRQ or DMA channel but still could not get any response from ping.
Configuring VGA driver
PluMGMK‘s modern SVGA driver for Windows 3.1 worked well on my S210 in enhanced mode. In standard mode, it worked fine insofar as the Program Manager was able to display 1280×1024, the maximum supported resolution. However, the system froze after around 15 seconds whenever I played any audio file using VSBHDA driver. I suspected this was likely interrupt or DMA related, but did not have the time to probe this further. After installing this driver, my Windows 3.1’s SETUP.EXE program also started to complain about insufficient memory while changing options. To fix this, I had to delete old OEMxxx.INF files in C:\WINDOWS (which were copies of old graphic drivers). Eventually, I decided to just use Microsoft’s generic SVGA driver for Windows 3.1, supporting up to 1024x768x256.
With the SVGA driver, Encarta 95 worked well, allowing me to read tens of thousands of articles. Here you can see how it did not have an article for the Internet, which didn’t become popular until the mid 1990s. CalmiraXP is also installed, not for the Windows XP interface, but just to give me a taskbar to conveniently switch between running programs. For this reason I did not set CalmiraXP as the default program manager, like what the manual suggested.
Adding a second storage device
I tried to install NT 3.51 on a new partition on the CF card and triple-boot with DOS 6.22 and Windows 98. This created a mess as neither DOS 6.22, NT 3.51 or Windows 98 would be happy if its partition is located beyond the 8GB barrier. I eventually decided to just leave MS-DOS 6.22 and Windows 98 alone and install NT 3.51 on its own disk drive.
There is a 40-pin IDE connector on the board for me to install an additional disk. There is however no dedicated pin to tap 5V from. Although there is a 5V connector near the PCI slot which outputs 5V, its current capability is very low. In the end, I tapped 5V from the one of the pin on the PCI riser card, fed it to a CF-to-IDE adapter with another 32GB CF card:
The new card was detected right away in BIOS. My S210 now has 2 x 32GB disk drives:
The S210’s BIOS has options to configure HDD timing. I did not bother with this as my CF cards are just 133x (max speed 20MB/s). When tested using a card reader, the actual max speed is around 14-15MB/s. Bandwidth limit for Fast PIO (the default option) is around 16MB/s.
On a side note, the PCI slot on this board does not supply 12V or -12V. A parallel port PCI card would work, while a serial port card (which needs -12V) or a sound card (which needs 12V for the amplifier) might have issues.
Although this BIOS can boot from a USB CD/DVD drive, it can only do so in floppy mode, as the emulated drive will not appear on the IDE bus. To cater for this, I routed the IDE cable for the secondary slave IDE channel through the PCI slot, allowing me to conveniently connect an IDE CD-ROM if needed. I am not planning to use the PCI slot anytime soon.
When I played with the S210 in 2022, legacy USB support for hard disk drives was flaky and would not work well unless booting from a USB floppy drive. It took me three years to find out that the issues were most likely due to some corrupted BIOS configuration. The issue disappeared after I reset the BIOS settings from the Exit menu. USB thumbdrives are now recognized properly as IDE drives and work well in MS-DOS, Windows 3.1 and NT 3.51.
Installing Windows NT 3.51
I first installed Windows NT 3.51 SP5 on VirtualBox in IDE mode using the bootable ISO from here. Then, I used VBoxManage to convert the disk to raw, and used Win32DiskImage to write the image to the CF card. To make use of the full 32GB space on the CF card, only a 2GB NTFS partition was created during installation. PQMAGIC for DOS was later used to extend this partition to 32GB. NT 3.51 installer only supports NTFS partitions up to 4GB, but once installed, NT 3.51 can run off 32GB without issues. There is a delay of 15 seconds at boot but after that it works fine:
It goes without saying that you should not use modern tools (GParted, CloneZilla, NTFS-3G or even Windows XP) to work with NT 3.51 NTFS. If you are lucky, it will just complain that the NTFS version (1.1) is not supported. If you are not, it will attempt to upgrade the partition to the latest NTFS format, making your data unreadable to Windows NT 3.51.
SIS315E comes with NT 4.0 drivers but not NT 3.51. You can tell this by the lack of OEMSETUP.INF in the driver folder. However, using VBEMP for Windows NT 3.51, I was able to get 1280×1024 with 16-bit colors. Take note that FRAMEBUF.DLL might need to be copied to the Windows directory for the driver to work.
Setting up networking was a breeze using VT6102 NT 3.51’s driver. Web browsing, file sharing, and FTP worked well too. Netscape Communicator 4.5 32-bit worked fine, showing http://web.textfiles.com
Now that Windows NT 3.51 is on the second hard drive, I use GRUB4DOS to allow me to select which OS to boot from without tweaking the BIOS. I did not have any E-IDE issues (like what I encountered on the Pocket 386), since the BIOS on the S210 is presumably much more modern:
This is the partition structure I used. The first CF card contains DOS 6.22 (FAT16 primary), Windows 98 (FAT32 primary), and an extended data partition (FAT32). The second CF card contains a FAT16 primary data partition (which DOS 6.22 can see), a NTFS primary partition (for NT 3.51) and a FAT32 data partition (for extra storage under Windows 98). GRUB4DOS is configure to hide/unhide the primary partitions depending on which OS is selected for booting
Patching VT8233 driver for NT 3.51
There is a Windows NT 3.51 driver for the Windows 8233 which just needs to be installed from Control Panel > Drivers to enable sound support. Or so I thought. Unfortunately, attempting to install the driver returned the following error message:
“Insufficient system resources exist to complete the requested service.”
Now, you should not read too much into this and start to probe DMA, IRQ or even conventional memory, like what I did. All it means is that the kernel mode driver (VIAUDIO.SYS) has decided to return code 0xC000009A (STATUS_INSUFFICIENT_RESOURCES) from DriverEntry, causing Windows to display the above user-friendly (but generic) error message. In other words, it just means the driver initialization failed for some reasons, not necessarily a lack of system resources.
Using the Interactive Disassembler (IDA) v5.2, I generated the assembly listing for the VIAUDIO.SYS driver. This is the DriverEntry function, with auto-comment enabled to make it easier to understand the code:
INIT:000182C0 ; Attributes: bp-based frame INIT:000182C0 INIT:000182C0 ; __stdcall DriverEntry(x, x) INIT:000182C0 public _DriverEntry@8 INIT:000182C0 _DriverEntry@8 proc near INIT:000182C0 INIT:000182C0 P= dword ptr -8 INIT:000182C0 var_4= dword ptr -4 INIT:000182C0 arg_0= dword ptr 8 INIT:000182C0 arg_4= dword ptr 0Ch INIT:000182C0 INIT:000182C0 push ebp INIT:000182C1 mov eax, [esp+arg_0] INIT:000182C5 mov ebp, esp INIT:000182C7 sub esp, 8 ; Integer Subtraction INIT:000182CA mov dword ptr [eax+34h], offset _SoundUnload@4 ; SoundUnload(x) INIT:000182D1 push esi INIT:000182D2 mov ecx, offset _SoundDispatch@8 ; SoundDispatch(x,x) INIT:000182D7 push edi INIT:000182D8 mov [ebp+var_4], eax INIT:000182DB xor edi, edi ; Logical Exclusive OR INIT:000182DD mov [eax+38h], ecx INIT:000182E0 mov [ebp+P], edi INIT:000182E3 mov [eax+40h], ecx INIT:000182E6 mov [eax+70h], ecx INIT:000182E9 mov [eax+44h], ecx INIT:000182EC lea edx, [ebp+P] ; Load Effective Address INIT:000182EF mov [eax+48h], ecx INIT:000182F2 push edx ; int INIT:000182F3 mov [eax+80h], ecx INIT:000182F9 mov dword ptr [eax+78h], offset _SoundShutdown@8 ; SoundShutdown(x,x) INIT:00018300 push offset _SoundCardInstanceInit@8 ; int INIT:00018305 push offset ??_C@_1BG@KEAJ@?$AAP?$AAa?$AAr?$AAa?$AAm?$AAe?$AAt?$AAe?$AAr?$AAs?$AA?$AA?$AA?$AA?$AA?$AH?$AA?$AA?$AA?$AA?$AA?$AA?7_?$AE_?$AA?$BG?$AA?$AB@ ; "Parameters" INIT:0001830A push [ebp+arg_4] ; int INIT:0001830D call _SoundEnumSubkeys@16 ; SoundEnumSubkeys(x,x,x,x) INIT:00018312 mov esi, eax INIT:00018314 cmp [ebp+P], edi ; Compare Two Operands INIT:00018317 jz short loc_1833A ; Jump if Zero (ZF=1) INIT:00018319 mov edi, [ebp+P] INIT:0001831C INIT:0001831C loc_1831C: ; ValueData INIT:0001831C push dword ptr [edi+0A74h] INIT:00018322 push offset ??_C@_1CI@CPHG@?$AAC?$AAo?$AAn?$AAf?$AAi?$AAg?$AAu?$AAr?$AAa?$AAt?$AAi?$AAo?$AAn?$AA?5?$AAE?$AAr?$AAr?$AAo?$AAr?$AA?$AA@ ; "Configuration Error" INIT:00018327 push dword ptr [edi+0A70h] ; Path INIT:0001832D call _SoundWriteRegistryDWORD@12 ; SoundWriteRegistryDWORD(x,x,x) INIT:00018332 mov edi, [edi+4] INIT:00018335 cmp edi, [ebp+P] ; Compare Two Operands INIT:00018338 jnz short loc_1831C ; Jump if Not Zero (ZF=0) INIT:0001833A INIT:0001833A loc_1833A: ; Logical Compare INIT:0001833A test esi, esi INIT:0001833C jge short loc_18350 ; Jump if Greater or Equal (SF=OF) INIT:0001833E cmp [ebp+P], 0 ; Compare Two Operands INIT:00018342 jz short loc_1834C ; Jump if Zero (ZF=1) INIT:00018344 push [ebp+P] ; P INIT:00018347 call _SoundCleanup@4 ; SoundCleanup(x) INIT:0001834C INIT:0001834C loc_1834C: INIT:0001834C mov eax, esi INIT:0001834E jmp short loc_18352 ; Jump INIT:00018350 INIT:00018350 loc_18350: ; Logical Exclusive OR INIT:00018350 xor eax, eax INIT:00018352 INIT:00018352 loc_18352: INIT:00018352 pop edi INIT:00018353 pop esi INIT:00018354 mov esp, ebp INIT:00018356 pop ebp INIT:00018357 retn 8 ; Return Near from Procedure INIT:00018357 _DriverEntry@8 endp
Searching the listing file, I located 12 instances where the driver might return 0x0C000009A (STATUS_INSUFFICIENT_RESOURCES). Many of these belonged to mixer or MIDI setup methods, which wouldn’t be called during initial initialization. The most suspicious instance was in _SoundCardInstanceInit, which was called from DriverEntry:
INIT:000183BD public _SoundCardInstanceInit@8 INIT:000183BD _SoundCardInstanceInit@8 proc near INIT:000183BD INIT:000183BD ValueData= byte ptr -0B8h INIT:000183BD var_B4= dword ptr -0B4h INIT:000183BD var_B0= dword ptr -0B0h INIT:000183BD var_AC= dword ptr -0ACh INIT:000183BD var_A8= dword ptr -0A8h INIT:000183BD var_A4= dword ptr -0A4h INIT:000183BD var_A0= dword ptr -0A0h INIT:000183BD var_9C= dword ptr -9Ch INIT:000183BD var_94= byte ptr -94h INIT:000183BD var_4= dword ptr -4 INIT:000183BD arg_0= dword ptr 8 INIT:000183BD arg_4= dword ptr 0Ch INIT:000183BD INIT:000183BD push ebp INIT:000183BE mov ebp, esp INIT:000183C0 sub esp, 0B8h ; Integer Subtraction INIT:000183C6 push ebx INIT:000183C7 push esi INIT:000183C8 push edi INIT:000183C9 push 206B6444h ; Tag INIT:000183CE push 0C20h ; NumberOfBytes INIT:000183D3 push 0 ; PoolType INIT:000183D5 call ds:__imp__ExAllocatePoolWithTag@12 ; ExAllocatePoolWithTag(x,x,x) INIT:000183DB mov esi, eax INIT:000183DD test esi, esi ; Logical Compare INIT:000183DF jnz short loc_183EB ; Jump if Not Zero (ZF=0) INIT:000183E1 mov eax, 0C000009Ah INIT:000183E6 jmp loc_18632 ; Jump INIT:000183EB INIT:000183EB loc_183EB: INIT:000183EB mov edi, esi INIT:000183ED xor eax, eax ; Logical Exclusive OR INIT:000183EF mov ecx, 308h INIT:000183F4 rep stosd ; Store String INIT:000183F6 mov eax, ??_C@_04DIHA@GDI?5?$AA@ ; `string' INIT:000183FB lea edi, [esi+2B0h] ; Load Effective Address INIT:00018401 mov [esi], eax INIT:00018403 mov eax, [ebp+arg_0] INIT:00018406 mov ecx, ??_C@_04KBND@Hw?5?5?$AA@ ; `string' INIT:0001840C mov ebx, [ebp+arg_4] INIT:0001840F push 2 ; Level INIT:00018411 mov [edi], ecx INIT:00018413 mov [esi+0A70h], eax INIT:00018419 lea eax, [esi+30h] ; Load Effective Address INIT:0001841C mov byte ptr [esi+72h], 55h INIT:00018420 mov byte ptr [esi+71h], 55h INIT:00018424 mov ecx, [ebx+4] INIT:00018427 push eax ; Mutex INIT:00018428 mov [esi+98h], ecx INIT:0001842E call ds:__imp__KeInitializeMutex@8 ; KeInitializeMutex(x,x) INIT:00018434 push 1 ; Level INIT:00018436 lea ecx, [esi+50h] ; Load Effective Address INIT:00018439 push ecx ; Mutex INIT:0001843A call ds:__imp__KeInitializeMutex@8 ; KeInitializeMutex(x,x) INIT:00018440 push 3 ; Level INIT:00018442 lea ecx, [esi+2C0h] ; Load Effective Address INIT:00018448 push ecx ; Mutex INIT:00018449 call ds:__imp__KeInitializeMutex@8 ; KeInitializeMutex(x,x) INIT:0001844F mov ecx, [ebx] INIT:00018451 test ecx, ecx ; Logical Compare INIT:00018453 jnz short loc_1845A ; Jump if Not Zero (ZF=0) INIT:00018455 mov [esi+4], esi INIT:00018458 jmp short loc_18463 ; Jump INIT:0001845A INIT:0001845A loc_1845A: INIT:0001845A mov eax, [ecx+4] INIT:0001845D mov [ecx+4], esi INIT:00018460 mov [esi+4], eax INIT:00018463 INIT:00018463 loc_18463: ; Load Effective Address INIT:00018463 lea eax, [esi+0Ch] INIT:00018466 mov [ebx], esi INIT:00018468 push eax ; int INIT:00018469 push 5 ; BusType INIT:0001846B call _SoundGetBusNumber@8 ; SoundGetBusNumber(x,x) INIT:00018470 test eax, eax ; Logical Compare INIT:00018472 jl loc_18632 ; Jump if Less (SF!=OF) INIT:00018478 mov dword ptr [esi+8], 5 INIT:0001847F mov dword ptr [ebp+ValueData], 220h INIT:00018489 mov [ebp+var_B4], 388h INIT:00018493 mov [ebp+var_B0], 7 INIT:0001849D mov [ebp+var_A4], 2000h INIT:000184A7 mov [ebp+var_A0], 330h INIT:000184B1 mov eax, 0FFFFFFFFh INIT:000184B6 push esi INIT:000184B7 mov [ebp+var_AC], eax INIT:000184BD mov [ebp+var_A8], eax INIT:000184C3 xor eax, eax ; Logical Exclusive OR INIT:000184C5 mov [ebp+var_9C], eax INIT:000184CB mov byte ptr [ebp+var_4], al INIT:000184CE call _PCITest@4 ; PCITest(x) INIT:000184D3 test al, al ; Logical Compare INIT:000184D5 jnz short loc_184E1 ; Jump INIT:000184D7 mov eax, 0C000009Ah INIT:000184DC jmp loc_18632 ; Jump INIT:000184E1 INIT:000184E1 loc_184E1: INIT:000184E1 mov ax, [esi+0A80h] INIT:000184E8 cmp ax, 0FFFFh ; Compare Two Operands INIT:000184EC jnz short loc_184FA ; Jump if Not Zero (ZF=0) INIT:000184EE mov [ebp+var_A0], 0FFFFFFFFh INIT:000184F8 jmp short loc_18503 ; Jump INIT:000184FA INIT:000184FA loc_184FA: ; Move with Zero-Extend INIT:000184FA movzx eax, ax INIT:000184FD mov [ebp+var_A0], eax INIT:00018503 INIT:00018503 loc_18503: INIT:00018503 mov eax, [esi+24h] INIT:00018506 mov [ebp+var_B0], eax INIT:0001850C push esi INIT:0001850D movzx ecx, word ptr [esi+0A78h] ; Move with Zero-Extend INIT:00018514 mov dword ptr [ebp+ValueData], ecx INIT:0001851A mov edx, [esi+28h] INIT:0001851D mov [ebp+var_AC], edx INIT:00018523 mov eax, [esi+2Ch] INIT:00018526 mov [ebp+var_A8], eax INIT:0001852C call _HwInitialize@4 ; HwInitialize(x) INIT:00018531 push 0 INIT:00018533 push esi INIT:00018534 call _SBCreateDevice@8 ; SBCreateDevice(x,x) INIT:00018539 test eax, eax ; Logical Compare INIT:0001853B jl loc_18632 ; Jump if Less (SF!=OF) INIT:00018541 push 1 INIT:00018543 push esi INIT:00018544 call _SBCreateDevice@8 ; SBCreateDevice(x,x) INIT:00018549 test eax, eax ; Logical Compare INIT:0001854B jl loc_18632 ; Jump if Less (SF!=OF) INIT:00018551 push 4 INIT:00018553 push esi INIT:00018554 call _SBCreateDevice@8 ; SBCreateDevice(x,x) INIT:00018559 test eax, eax ; Logical Compare INIT:0001855B jl loc_18632 ; Jump if Less (SF!=OF) INIT:00018561 push 5 INIT:00018563 push esi INIT:00018564 call _SBCreateDevice@8 ; SBCreateDevice(x,x) INIT:00018569 test eax, eax ; Logical Compare INIT:0001856B jl loc_18632 ; Jump if Less (SF!=OF) INIT:00018571 push 6 INIT:00018573 push esi INIT:00018574 call _SBCreateDevice@8 ; SBCreateDevice(x,x) INIT:00018579 test eax, eax ; Logical Compare INIT:0001857B jl loc_18632 ; Jump if Less (SF!=OF) INIT:00018581 push dword ptr [esi+7Ch] ; DeviceObject INIT:00018584 call ds:__imp__IoRegisterShutdownNotification@4 ; IoRegisterShutdownNotification(x) INIT:0001858A test eax, eax ; Logical Compare INIT:0001858C jl loc_18632 ; Jump if Less (SF!=OF) INIT:00018592 mov byte ptr [esi+1Dh], 1 INIT:00018596 lea eax, [ebp+ValueData] ; Load Effective Address INIT:0001859C push eax ; int INIT:0001859D push esi ; ServiceContext INIT:0001859E call _SoundInitHardwareConfig@8 ; SoundInitHardwareConfig(x,x) INIT:000185A3 test eax, eax ; Logical Compare INIT:000185A5 jl loc_18632 ; Jump if Less (SF!=OF) INIT:000185AB push edi INIT:000185AC lea eax, [esi+0A0h] ; Load Effective Address INIT:000185B2 push offset _SoundQueryFormat@8 ; SoundQueryFormat(x,x) INIT:000185B7 push 1 INIT:000185B9 push eax INIT:000185BA call _SoundInitializeWaveInfo@16 ; SoundInitializeWaveInfo(x,x,x,x) INIT:000185BF mov eax, [esi+94h] INIT:000185C5 test eax, eax ; Logical Compare INIT:000185C7 jz short loc_185DF ; Jump if Zero (ZF=1) INIT:000185C9 push [ebp+var_4] INIT:000185CC lea ecx, [ebp+var_94] ; Load Effective Address INIT:000185D2 push ecx INIT:000185D3 push dword ptr [eax+28h] INIT:000185D6 call _SoundMixerInit@12 ; SoundMixerInit(x,x,x) INIT:000185DB test eax, eax ; Logical Compare INIT:000185DD jl short loc_18632 ; Jump if Less (SF!=OF) INIT:000185DF INIT:000185DF loc_185DF: INIT:000185DF mov eax, [esi+0B0h] INIT:000185E5 mov cl, [esi+346h] INIT:000185EB cmp dword ptr [esi+338h], 1 ; Compare Two Operands INIT:000185F2 push eax ; int INIT:000185F3 push ecx ; char INIT:000185F4 sbb al, al ; Integer Subtraction with Borrow INIT:000185F6 inc al ; Increment by 1 INIT:000185F8 push eax ; char INIT:000185F9 push [ebp+var_A0] ; int INIT:000185FF push [ebp+var_B0] ; int INIT:00018605 push [ebp+var_A8] ; int INIT:0001860B push [ebp+var_AC] ; int INIT:00018611 push dword ptr [ebp+ValueData] ; ValueData INIT:00018617 push dword ptr [esi+0A70h] ; Path INIT:0001861D call _SoundSaveConfig@36 ; SoundSaveConfig(x,x,x,x,x,x,x,x,x) INIT:00018622 test eax, eax ; Logical Compare INIT:00018624 jl short loc_18632 ; Jump if Less (SF!=OF) INIT:00018626 mov dword ptr [esi+0A74h], 0FFFFFFFFh INIT:00018630 xor eax, eax ; Logical Exclusive OR INIT:00018632 INIT:00018632 loc_18632: INIT:00018632 pop edi INIT:00018633 pop esi INIT:00018634 pop ebx INIT:00018635 mov esp, ebp INIT:00018637 pop ebp INIT:00018638 retn 8 ; Return Near from Procedure INIT:00018638 _SoundCardInstanceInit@8 endp
ExAllocatePoolWithTag would only fail if there was not enough memory. But since only 0C20h (3104) bytes were requested, a failure here was highly unlikely. The next candidate was the call to PCITest(), which would return true if the card was detected (the test al, al instruction made it obvious that the return was boolean). The driver initialization would halt immediately if the card was not detected. According to this Windows NT 3.51 article, there was a PCI enumeration bug on Windows NT 3.51, albeit for SCSIPORT.SYS. Maybe my situation was similar and VIAUDIO.SYS just didn’t find what it wanted from the S210 PCI bus.
To test if this was indeed the case, I needed to patch _SoundCardInstanceInit to always proceed regardless of the result of PCITest by changing jnz to jmp:
INIT:000184B7 mov [ebp+var_AC], eax INIT:000184BD mov [ebp+var_A8], eax INIT:000184C3 xor eax, eax ; Logical Exclusive OR INIT:000184C5 mov [ebp+var_9C], eax INIT:000184CB mov byte ptr [ebp+var_4], al INIT:000184CE call _PCITest@4 ; PCITest(x) INIT:000184D3 test al, al ; Logical Compare INIT:000184D5 jmp short loc_184E1 ; Jump INIT:000184D7 mov eax, 0C000009Ah INIT:000184DC jmp loc_18632 ; Jump
The next thing to do was to find out exactly where to perform the hex patch. To do this, I just used Biew to hex search for 88 03, a value which appears in the preceding instructions:
INIT:00018489 mov [ebp+var_B4], 388h INIT:00018493 mov [ebp+var_B0], 7
Using Biew’s disasembler mode, instructions which resembled the IDA listing were located, highlighted in the screenshot below for clarity. It turned out that the raw offset was already shown in IDA listing, for example test al, al was listed at INIT:000184D3 for a raw offset of 0x84D3:
To bypass the result of PCITest, patch 75 0A (JNE/JNZ) to EB 0A (JMP, always jump regardless of result):
For the patched driver to be usable, update the checksum of VIAUDIO.SYS using PECHKSUM.EXE. The updated checksum field can be viewed from CFF Explorer (00 01 2A 61 for the new driver):
Using Hex Comparison we can see where the checksum is actually located (hint: around offset 0xD0), by comparing the original and patched executable. The original checksum was 00 01 B4 60:
The following C code will also update the checksum of a PE executable, by using the MapFileAndCheckSum Win32 API:
int main() {
DWORD headersum = 0;
DWORD checksum = 0;
if (MapFileAndCheckSumW(L"VIAUDIO.SYS", &headersum, &checksum) == CHECKSUM_SUCCESS) {
printf("Old checksum: %08X\n", headersum);
printf("New checksum: %08X\n", checksum);
} else {
printf("Checksum calculation failed\n");
}
getc(stdin);
return 0;
}
To update the driver to the patched version, remove the old one from Control Panel > Drivers, reboot, and readd the new driver. If you do not reboot, Windows NT 3.51 may report that the driver has been successfully added while still using the old version.
After rebooting, I was finally able to hear the Windows startup sound! So it was indeed a PCI enumeration issue, making the driver think my card was not connected. The VIA AC97 Audio Controller entry now appeared in Control Panel > Drivers. If after patching your VIA sound card still cannot work, try to enable viaudio under Control Panel > Devices. Also check Administrative Tools > Event Viewer for any errors.
MIDI support on NT 3.51
VT8233’s NT 3.51 driver supports WAVE playback but not MIDI. I modified OEMSETUP.INF to use VIAMIDI.DLL from NT 4.0 but NT 3.51 kept complaining that the DLL was missing. The occurence of several MIDI methods in the NT 3.51 version of VIAUDIO.DLL, and the fact that the NT 4.0 driver does not require a kernel mode driver (e.g. VIAMIDI.SYS) to enable MIDI support suggests that it is perhaps not too hard to patch the NT 3.51 driver to at least get a few MIDI files to play. Likely, both the NT 3.51 and NT 4.0 drivers were compiled from the same codebase. However, the rudimentary nature of MIDI support in NT 3.51 might have resulted in the decision to exclude the MIDI interface from the production build for NT 3.51.
Meanwhile, WinGroove Player 0.9E worked well for MIDI playback. The MIDI synthesizer driver (WINGROOV.DRV) it came with did not work on NT 3.51, as it was a 16-bit driver designed for Windows 3.1.
This is Yamaha SoftSynthesizer S-YXG50 running on NT 3.51. Despite what the user manual claimed, I could not get it to work as a system-wide MIDI synthesizer, although the built-in player worked fine. If the “Song” button does not work, drag and drop your MIDI files to the player instead. I did not have any such issues on Windows 98. MIDI just worked out of the box.
Multimedia software
With the audio driver installed, Netscape Communicator could finally play SWF Flash movies with audio using Shockwave Flash 6.0 r79 plugin. Do not however associate the SWF extension with Netscape thinking you can just double click a SWF file to play in Netscape. Due to an intermediate conversion step in the playback process, associating SWF with Netscape will cause Netscape to be launched again and again whenever you open an SWF file.
Microsoft Encarta 97 worked well on Windows NT 3.51. It finally contained articles for the Internet and related stuff such as TCP/IP:
In case you are wondering, Entarta 97 supports Windows 3.1 but refuses to install in standard mode. Encarta 96 installed in standard mode but crashed with “Abnormal Program Termination” on my S210. I could only get Encarta 95 to work well on my Windows 3.11 standard mode configuration. I guess I don’t need to read another article about the Internet using Windows 3.11 in standard mode on my S210.
This is Microsoft Encarta 97, Internet Explorer 3.0 and Winamp 2.64 running on my S210 at 1280×1024, with network and audio driver installed. IE is showing gopher://gopher.floodgap.com, one of the few remaining Gophers servers. NT Shell (not NewShell), a beta port of Calmira for Windows NT 3.51, is also installed. As a portable program, NT Shell still brings the taskbar to Windows NT 3.51 without messing with system files or changing the internal Windows version to NT 4.0. I didn’t bother with IE 5.0 for NT 3.51, which consumes a lot of resources and still can’t open most modern web pages anyway. There are better browsers (such as RetroZilla) out there.
MP3 playback works great in Winamp, although adding a directory to the playlist doesn’t work. You can still add many MP3 files in one go using multi-selection. I believe the feature uses SHBrowseForFolder which is only available in Windows 95 / NT 4.0. Well, at least it doesn’t crash.
Downloads
You can download the Windows 3.11 and Windows NT 3.51 drivers I used for my S210 here. Inside the archive you will find the patched VIAUDIO.SYS for NT 3.51 as well as the original file (renamed VIAUDIO_OLD.SYS, 61568 bytes, dated 16 September 2002). This version of VIADIO.SYS worked well with my S210. Several other versions of the same SYS file did not work, even with the PCITest patch applied.
The ZIP file also contains VSBHDA, Calmira, NT Shell, Netscape and the Flash player plugin for testing.
See also
Fujitsu Siemens FSC Futro S210 thin client



























