Skip to content

Commit

Permalink
Add README, screenshot, and tool used to produce screenshot
Browse files Browse the repository at this point in the history
  • Loading branch information
PluMGMK committed Dec 15, 2024
1 parent 2fcbfdb commit 6722101
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 0 deletions.
86 changes: 86 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# vbesvga.drv and vddvbe.386
Modern Generic SVGA driver for Windows 3.1

This is a rewrite of the Windows 3.1 SVGA driver, designed to support **ALL** available 8-bit, 16-bit, 24-bit or 32-bit graphic modes on any system providing the [VESA BIOS Extensions](https://en.wikipedia.org/wiki/VESA_BIOS_Extensions) (hence the `VBE` in the name). It is based on the Video 7 SVGA driver included in the [Win16 Driver Development Kit](http://www.win3x.org/win3board/viewtopic.php?t=2776), with most of the hardware-specific code gutted out, and with support added for multi-byte pixels.

## Why on Earth did I make such a thing?

* To demonstrate that it's possible to run Windows 3.1 in True-Colour Full HD
* Because my AMD Radeon RX 5500 XT doesn't support 256-colour modes, rendering the old [VESA Patch](http://www.win3x.org/win3board/viewtopic.php?t=5408&hilit=svga) useless for me
* To help out any fellow enthusiasts who like running old software on new hardware!

## Screenshot

![True-Colour Full HD screenshot of Windows 3.1 desktop showing colour settings (on "Bordeaux"), Program Manager, two MS-DOS prompts of different sizes, Solitaire and Minesweeper](./Screenshots/VBESVGA.CLP.rec0.png)

This True-Colour Full HD screenshot gives you some idea of what's working so far. The colour settings dialogue shows that pretty much the entire Windows GUI renders correctly, and the Program Manager shows icons working well too. The two windowed DOS prompts demonstrate that I am running this on MS-DOS 6.20, and that I'm using a real AMD graphics card with [vendor string](https://fd.lod.bz/rbil/interrup/video/104f00.html) equal to `(C) 1988-2018, Advanced Micro Devices, Inc.` and [product name](https://fd.lod.bz/rbil/interrup/video/104f00.html) equal to `NAVI14`. Minesweeper looks OK, while Solitaire is mostly working, but a few glitches are evident...

See the Issues page for more details of what is _not_ working at the moment...

## Building

### `vbesvga.drv` (needed in both Standard and 386 Enhanced Mode)

* Install both the [Win16 DDK](http://www.win3x.org/win3board/viewtopic.php?t=2776) and [a contemporary version of Visual C++](http://www.win3x.org/win3board/viewtopic.php?t=1375)
* Place the `VBESVGA` folder from this repository in the DDK hierarchy, at `286/DISPLAY/8PLANE/VBESVGA`
* Ensure `286\TOOLS` from the DDK and `MSVC\BIN` from Visual C++ are in your `PATH`
* Go to the `VBESVGA\mak` folder and run `make vbesvga.mak`; this should create the file `VBESVGA.DRV` which can be loaded by Windows

### `vddvbe.386` (needed only in 386 Enhanced Mode)

* Place the `VDDVBE` folder from this repository in the DDK hierarchy, at `386/VDDVBE`
* Ensure `386\TOOLS` from the DDK and `MSVC\BIN` from Visual C++ are in your `PATH`
* Go to the `VDDVBE` folder and run `nmake`; this should create the file `VDDVBE.386` which can be loaded by Windows

### Tip for using the DDK on a modern system

To make the debugger `WDEB386` work, you need to change some bytes:

* At position `63D8`, you need to change `0F 24 F0` to `66 33 C0`
* At position `63DF`, you need to change `0F 24 F8` to `66 33 C0`

This removes references to the [`TR6` and `TR7` registers](https://en.wikipedia.org/wiki/Test_register), which crash the system since they only existed on the 386, 486 and a few other less-well-known chips!

## Usage

The following changes are needed to your `C:\WINDOWS\SYSTEM.INI` file:

* In the `[boot]` section, change the `display.drv=` line to point to `vbesvga.drv`. You should specify the full path, or else copy the file to `C:\WINDOWS\SYSTEM`. (Note that if the path is too long, it can cause the CodeView debugger to crash on startup!)
* In the `[386Enh]` section, change the `display=` line to point to `vddvbe.386`. Again, you should specify the full path, or else copy the file to `C:\WINDOWS\SYSTEM`.
* Create a `[VBESVGA.DRV]` section to configure the driver, as detailed [below](#configuration-parameters).

## Configuration parameters

This table lists the parameters you can specify in the `[VBESVGA.DRV]` section of `SYSTEM.INI`.

|Parameter |Valid values |Meaning |Default value |
--- | --- | --- | ---
|`Width` | 640 - 65535 | Width in pixels of the desired video mode | 1024 |
|`Height` | 480 - 65535 | Height in scanlines of the desired video mode | 768 |
|`Depth` | 8 - 24 | Significant bits per pixel of the desired video mode ("significant" means that padding bits are excluded, so for example if you choose 24, both 24-bit and 32-bit modes will qualify) | 24 |
|`fontsize` | `small` or `large` | Choose whether to use 96dpi or 120dpi fonts | `small` |
|`dacdepth` | 6 or 8 | Significant bits to use per colour in 256-colour modes | 6 |
|`DoubleBufRefreshRate` | 4 - 255 | Number of times per second to swap buffers if double-buffering is enabled | 60 |
|`PMIDcheck` | `disable`, `none`, `sum` or `sanity` | See [below](#protected-mode-interface) | `sum` |

### Protected-Mode Interface

The VESA BIOS Extensions spec allows graphics firmware to provide an interface for Protected-Mode drivers. This driver attempts to use this interface where possible, to avoid needing to allocate DOS memory blocks to communicate with the BIOS. In theory, as per Page 21 of [the spec](http://www.petesqbsite.com/sections/tutorials/tuts/vbe3.pdf), the driver should search the segment `C000h` for a structure beginning with the signature `PMID` and ending with a valid checksum to determine whether or not this interface is supported. However, some firmwares don't set the checksum correctly, and some firmwares "provide" the interface but don't do the QA to ensure it actually works. To account for this, four options are provided:

|Option |Meaning |
--- | ---
|`PMIDcheck=sum` | The default: the driver searches for the structure beginning with `PMID` and verifies the checksum |
|`PMIDcheck=sanity` | A looser setting: the driver searches for `PMID` and makes sure the other values in the structure match the suggested values on Page 21 of the spec; if they don't all match, it falls back to the checksum |
|`PMIDcheck=none` | NO VERIFICATION: the driver just searches for `PMID` and trusts that whatever comes after it is the correct structure |
|`PMIDcheck=disable` | The driver doesn't use the Protected-Mode Interface at all, even if it's available: **Try this if Windows hangs/crashes during boot!** |

### Example configuration

```
[VBESVGA.DRV]
PMIDcheck=sanity
Width=1920
Height=1080
Depth=16
DoubleBufRefreshRate=75
```
Binary file added Screenshots/VBESVGA.CLP.rec0.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
67 changes: 67 additions & 0 deletions Screenshots/clp2png.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/usr/bin/python3
from PIL import Image
from sys import argv
from construct import *
import os.path

if len(argv) < 2:
print("You need to specify the CLP path as an argument!")
exit()

BITMAP = Struct(
"bmType" / Const(0,Int16ul) * "Must be a memory bitmap",
"bmWidth" / Int16ul * "Width in pixels",
"bmHeight" / Int16ul * "Height in rows",
"bmWidthBytes" / Int16ul * "Width in bytes",
"bmPlanes" / Int8ul * "Planes in the bitmap (we only deal with flat ones)",
"bmBitsPixel" / Int8ul * "Bits per pixel",
"bmBits" / Const(0,Int32ul) * "In a file, the pointer is NULL",
"Bits" / Bytes(this.bmHeight * this.bmWidthBytes) * "In a file, the actual data directly follows the header",
)

FORMATRECORD = Struct(
"FormatId" / Int16ul,
"DataLength" / Int32ul,
"DataOffset" / Int32ul,
"FormatName" / CString("ascii"),
"Data" / Pointer(this.DataOffset,
Switch(this.FormatId, {2: BITMAP,}, Bytes(this.DataLength))
),
)

CLPHEADER = Struct(
"FileId" / Const(0xC350,Int16ul),
"FormatCount" / Int16ul,
"ClpRecords" / Array(this.FormatCount, FORMATRECORD),
)

clpfilename = argv[-1]
clpbasename = os.path.basename(clpfilename) # For saving result into cwd
clpfile = CLPHEADER.parse_file(clpfilename)

for idx,record in enumerate(clpfile.ClpRecords):
if "Bitmap" not in record.FormatName:
print(f"Record {idx} not a bitmap, skipping...")
continue
bitmap = record.Data
if bitmap.bmPlanes > 1:
print(f"Record {idx} not a flat bitmap, skipping...")
continue
# We have a good flat bitmap, get its size into a tuple
size = (bitmap.bmWidth, bitmap.bmHeight)
if bitmap.bmBitsPixel == 24:
image = Image.frombytes("RGB",size,bitmap.Bits)
elif bitmap.bmBitsPixel == 32:
# The extra eight bits are just padding
image = Image.frombytes("RGBX",size,bitmap.Bits).convert("RGB")
else:
print(f"Record {idx} is a {bitmap.bmBitsPixel}-bit bitmap, but only 24- or 32-bit are supported, skipping...")
continue
# A lot of the time, these DDBs are actually BGR or BGRA
if "--swapchan" in argv:
b,g,r = image.split()
image = Image.merge("RGB",(r,g,b))
# Save it as a PNG
pngname = f"{clpbasename}.rec{idx}.png"
print(f"Saving record {idx} as {pngname}...")
image.save(pngname)

0 comments on commit 6722101

Please sign in to comment.