Repository: dosbox-staging/dosbox-staging
Pull request #4245: VESA 2.0 mode list regression fix
Author: login: johnnovak
Labels: name: game compatibility
description: Issues related to a specific game
name: regression
description: We broke something 😊
name: backport
description: Important fixes that should be backported
name: video
description: Graphics and video related issues
Created: 2025-03-21T09:03:19Z | Merged: 2025-03-26T23:51:24Z

# Description

## Problem description

Starting from the VESA 2.0 standard, applications were required to query the list of available VESA modes instead of relying of card-specific mode numbers which was a common pre-VESA-2.0 practice. In fact, VESA 2.0 does not specify any fixed video modes, only _hints_ at it that manufacturers _may_ implement some common VESA 1.2 modes, but no guarantees whatsoever. Basically, everything is up to the graphics card/chipset implementors, and applications should not assume anything.

This is all good and well, but some questionably coded games still did not handle VESA 2.0 mode querying quite correctly, despite the standard (including the famous Quake). The coders made certain assumptions on the maximum number of entries the mode list can contain, and this can cause programs to misbehave or outright crash if the list is "too large" (e.g., by triggering buffer overflows, in which case pretty much anything can happen).

I added three extra VESA 2.0 text modes as part of my `MODE` command implementations for some custom 132-column modes, and while this was not a problem during my spot testing of a few VESA-enabled programs, it turns out  **Jane's Combat Simulations AH-64D Longbow** (non-Gold edition) gets confused by the presence of these modes (see the related [issue ticket](https://github.com/dosbox-staging/dosbox-staging/issues/4238) for details). Interestingly, the Gold version is not affected--presumably, the developers realised the error of their ways and fixed their VESA mode querying routine in the later Gold re-release...

## Solution

The solution is to not make these special 132-column VESA 2.0 text modes available when `vesa_modes = compatible` is set, which is our default. That's the safest thing to do because who knows what else might be broken due to this slightly longer mode list. People wishing to use these modes will need to set `vesa_modes = all`, but that can trigger the "mode list too large" problem with certain games.

I think this is the best compromise as these special text modes are only nice-to-haves and hardly essential. Being compatible with as many games as possible by default is our primary goal, so something had to give. Plus it's best to avoid introducing regressions when something non-essential like this is introduced.

## Alternatives considered

None of the below options were to my liking, I'm just listing them for completeness:

1. Simply document on the Wiki that the game runs fine with with `vmemsize` set to 2 MB (the default is 4 MB for the S3 Trio64). This reduces the number of available VESA modes, so the game bug is not triggered.

2. Convert these special 132-column VESA modes into non-VESA text modes. I tried that briefly, but ran into issues (I got 40-column modes instead), and I'm not interested in spending too much time investigating this. Probably VESA is needed for some reason to support 132-column modes--why exactly, I'm not sure.

3. Introduce various mode list culling options [like DOSBox X](https://dosbox-x.com/wiki/Guide%3AVideo-card-support-in-DOSBox%E2%80%90X#_vesa_modelist_cap).  For example, we could disallow resolutions larger than 1280x1024 or something if a special config option is set. But that doesn't help with out-of-the-box compatibility and I'm a bit averse to introducing new config settings for these workarounds. There is a large number of these VESA-culling config options in X, but I'd say let's not go there.

4. Remove these three DOSBox-special 132-column modes. Nah, I wanna keep them! 😎 

## Related issues

Fixes https://github.com/dosbox-staging/dosbox-staging/issues/4238

The `MODE` command and the new 132-column VESA modes were introduced in this PR of mine: https://github.com/dosbox-staging/dosbox-staging/pull/3741


# Release notes

- Fixed **Jane's Combat Simulations AH-64D Longbow** (original non-Gold version) regression where the in-game options menu could not be opened.

- The 132x28, 132x30, and 132x34 DOSBox-special VESA text modes are now only available if `vesa_modes = all` is set.


# Manual testing

- Confirmed the game now works with the stock settings.

- Confirmed setting the special 132-column modes with the `MODE` command works with `vesa_modes = all`, and in `compatible` mode an error is displayed.

The change has been manually tested on:

- [x] Windows
- [ ] macOS
- [ ] Linux


# Checklist

_Please tick the items as you have addressed them. Don't remove items; leave the ones that are not applicable unchecked._

I have:

- [x] followed the project's [contributing guidelines](https://github.com/dosbox-staging/dosbox-staging/blob/master/CONTRIBUTING.md) and [code of conduct](https://github.com/dosbox-staging/dosbox-staging/blob/master/CODE_OF_CONDUCT.md).
- [x] performed a self-review of my code.
- [x] commented on the particularly hard-to-understand areas of my code.
- [x] split my work into well-defined, bisectable commits, and I [named my commits well](https://github.com/dosbox-staging/dosbox-staging/blob/main/CONTRIBUTING.md#commit-messages).
- [x] applied the appropriate labels (bug, enhancement, refactoring, documentation, etc.)
- [x] [checked](https://github.com/dosbox-staging/dosbox-staging/blob/main/scripts/compile_commits.sh) that all my commits can be built.
- [ ] my change has been manually tested on Windows, macOS, and Linux.
- [ ] confirmed that my code does not cause performance regressions (e.g., by running the Quake benchmark).
- [ ] added unit tests where applicable to prove the correctness of my code and to avoid future regressions.
- [x] provided the release notes draft (for significant user-facing changes).
- [ ] made corresponding changes to the documentation or the website according to the [documentation guidelines](https://github.com/dosbox-staging/dosbox-staging/blob/main/DOCUMENTATION.md).
- [ ] [locally verified](https://github.com/dosbox-staging/dosbox-staging/blob/main/DOCUMENTATION.md#previewing-documentation-changes-locally) my website or documentation changes.




Diff:
diff --git a/src/dos/program_mode.cpp b/src/dos/program_mode.cpp
index 6040df59fb4..0807730f514 100644
--- a/src/dos/program_mode.cpp
+++ b/src/dos/program_mode.cpp
@@ -166,10 +166,15 @@ void MODE::HandleSetDisplayMode(const std::string& _mode_str)
 
 				const auto& mode = it->second;
 
-				if (mode < MinVesaBiosModeNumber) {
-					INT10_SetVideoMode(mode);
+				if (VESA_IsVesaMode(mode)) {
+					if (INT10_FindSvgaVideoMode(mode)) {
+						VESA_SetSVGAMode(mode);
+					} else {
+						WriteOut(MSG_Get("PROGRAM_MODE_UNSUPPORTED_VESA_MODE"),
+						         mode_str.c_str());
+					}
 				} else {
-					VESA_SetSVGAMode(mode);
+					INT10_SetVideoMode(mode);
 				}
 			} else {
 				WriteOut(MSG_Get("PROGRAM_MODE_UNSUPPORTED_DISPLAY_MODE"),
@@ -288,6 +293,9 @@ void MODE::AddMessages()
 	        "      SVGA (non-S3)      40x25, 80x25, 80x28, 80x30, 80x34, 80x43, 80x50\n"
 	        "      SVGA (S3)          40x25, all 80 and 132-column modes\n"
 	        "\n"
+	        "  - The 132x28, 132x30, and 132x34 modes are only available if `vesa_modes`\n"
+	        "    is set to `all`.\n"
+	        "\n"
 	        "Examples:\n"
 	        "  [color=light-green]mode[reset] [color=white]132x50\n"
 	        "  [color=light-green]mode[reset] [color=white]80x43[reset]\n"
@@ -299,6 +307,9 @@ void MODE::AddMessages()
 	MSG_Add("PROGRAM_MODE_UNSUPPORTED_DISPLAY_MODE",
 	        "Display mode [color=white]%s[reset] is not supported on this graphics adapter.");
 
+	MSG_Add("PROGRAM_MODE_UNSUPPORTED_VESA_MODE",
+	        "VESA display mode [color=white]%s[reset] is not supported; set `vesa_modes = all` to enable it.");
+
 	MSG_Add("PROGRAM_MODE_INVALID_TYPEMATIC_RATE",
 	        "Invalid typematic rate setting.");
 }
diff --git a/src/hardware/vga_s3.cpp b/src/hardware/vga_s3.cpp
index b717d96e49e..b7d9b572b14 100644
--- a/src/hardware/vga_s3.cpp
+++ b/src/hardware/vga_s3.cpp
@@ -751,9 +751,14 @@ void filter_compatible_s3_vesa_modes()
 	}
 
 	auto mode_allowed = [&](const VideoModeBlock& m) {
-		// Allow all text modes
+		// Only allow standard text modes
 		if (m.type == M_TEXT) {
-			return true;
+			constexpr auto _132x28 = 0x230;
+			constexpr auto _132x30 = 0x231;
+			constexpr auto _132x34 = 0x232;
+
+			return !contains(std::vector({_132x28, _132x30, _132x34}),
+			                 m.mode);
 		}
 
 		// Allow all non-VESA modes (standard VGA modes, and CGA and EGA
@@ -763,7 +768,8 @@ void filter_compatible_s3_vesa_modes()
 		}
 
 		// Allow common standard VESA modes, except 320x200 hi-color
-		// modes that were rarely properly supported until the late 90s.
+		// modes that were rarely properly supported until the late 90s,
+		// and the DOSBox-specific widescreen modes.
 		constexpr auto s3_vesa_modes_start = 0x150;
 
 		if (m.mode < s3_vesa_modes_start) {
@@ -771,8 +777,20 @@ void filter_compatible_s3_vesa_modes()
 			constexpr auto _320x200_16bit = 0x10e;
 			constexpr auto _320x200_32bit = 0x10f;
 
-			return !(m.mode == _320x200_15bit || m.mode == _320x200_16bit ||
-			         m.mode == _320x200_32bit);
+			// Additional DOSBox-specific widescreen modes
+			constexpr auto _848x480_8bit  = 0x222;
+			constexpr auto _848x480_15bit = 0x223;
+			constexpr auto _848x480_16bit = 0x224;
+			constexpr auto _848x480_32bit = 0x225;
+
+			return !contains(std::vector({_320x200_15bit,
+			                              _320x200_16bit,
+			                              _320x200_32bit,
+			                              _848x480_8bit,
+			                              _848x480_15bit,
+			                              _848x480_16bit,
+			                              _848x480_32bit}),
+			                 m.mode);
 		}
 
 		// Selectively allow S3-specific VESA modes.
diff --git a/src/ints/int10.h b/src/ints/int10.h
index 71a9c128eac..093f390dd77 100644
--- a/src/ints/int10.h
+++ b/src/ints/int10.h
@@ -21,6 +21,7 @@
 
 #include "dosbox.h"
 
+#include <optional>
 #include <vector>
 
 #include "bit_view.h"
@@ -333,6 +334,7 @@ inline uint8_t CURSOR_POS_ROW(const uint8_t page)
 
 void INT10_SetupPalette();
 
+std::optional<const VideoModeBlock> INT10_FindSvgaVideoMode(uint16_t mode);
 bool INT10_SetVideoMode(uint16_t mode);
 void INT10_SetCurMode(void);
 bool INT10_VideoModeChangeInProgress();
diff --git a/src/ints/int10_modes.cpp b/src/ints/int10_modes.cpp
index 09905e10c34..108a6335466 100644
--- a/src/ints/int10_modes.cpp
+++ b/src/ints/int10_modes.cpp
@@ -19,6 +19,7 @@
 
 #include "int10.h"
 
+#include <algorithm>
 #include <array>
 #include <cassert>
 #include <cstring>
@@ -2202,10 +2203,11 @@ bool INT10_SetVideoMode(uint16_t mode)
 }
 
 uint32_t VideoModeMemSize(uint16_t mode) {
-	if (!IS_VGA_ARCH)
+	if (!IS_VGA_ARCH) {
 		return 0;
+	}
 
-	// lanmda function to return a reference to the modelist based on the
+	// Lambda function to return a reference to the modelist based on the
 	// svgaCard switch
 	auto get_mode_list = [](SVGACards card) -> const std::vector<VideoModeBlock> & {
 		switch (card) {
@@ -2257,6 +2259,22 @@ uint32_t VideoModeMemSize(uint16_t mode) {
 	return static_cast<uint32_t>(mem_bytes);
 }
 
+std::optional<const VideoModeBlock> INT10_FindSvgaVideoMode(uint16_t mode)
+{
+	assert(ModeList_VGA.size());
+
+	const auto it = std::find_if(ModeList_VGA.begin(),
+	                             ModeList_VGA.end(),
+	                             [&](const VideoModeBlock& v) {
+		                             return v.mode == mode;
+	                             });
+
+	if (it == ModeList_VGA.end()) {
+		return {};
+	}
+	return *it;
+}
+
 static cga_colors_t handle_cga_colors_prefs_tandy(const std::string& cga_colors_setting)
 {
 	constexpr auto default_brown_level = 0.5f;
diff --git a/src/ints/int10_vesa.cpp b/src/ints/int10_vesa.cpp
index d1fc91aa3f6..4e808d95d9b 100644
--- a/src/ints/int10_vesa.cpp
+++ b/src/ints/int10_vesa.cpp
@@ -1,5 +1,5 @@
 /*
- *  Copyright (C) 2019-2024  The DOSBox Staging Team
+ *  Copyright (C) 2019-2025  The DOSBox Staging Team
  *  Copyright (C) 2002-2021  The DOSBox Team
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -19,6 +19,7 @@
 
 #include "int10.h"
 
+#include <algorithm>
 #include <cstddef>
 #include <cstring>
 
@@ -36,8 +37,9 @@
 #define VESA_FAIL             0x01
 #define VESA_HW_UNSUPPORTED   0x02
 #define VESA_MODE_UNSUPPORTED 0x03
-// internal definition to pass to the caller
-#define VESA_UNIMPLEMENTED    0xFF
+
+// Internal definition to pass to the caller
+#define VESA_UNIMPLEMENTED 0xFF
 
 static struct {
 	callback_number_t rmWindow  = 0;
@@ -52,10 +54,11 @@ static const std::string string_productname = DOSBOX_NAME;
 static const std::string string_productrev  = DOSBOX_VERSION;
 
 #ifdef _MSC_VER
-#pragma pack (1)
+#pragma pack(1)
 #endif
-struct MODE_INFO{
+struct MODE_INFO {
 	uint16_t ModeAttributes;
+
 	uint8_t WinAAttributes;
 	uint8_t WinBAttributes;
 	uint16_t WinGranularity;
@@ -63,11 +66,14 @@ struct MODE_INFO{
 	uint16_t WinASegment;
 	uint16_t WinBSegment;
 	uint32_t WinFuncPtr;
+
 	uint16_t BytesPerScanLine;
+
 	uint16_t XResolution;
 	uint16_t YResolution;
 	uint8_t XCharSize;
 	uint8_t YCharSize;
+
 	uint8_t NumberOfPlanes;
 	uint8_t BitsPerPixel;
 	uint8_t NumberOfBanks;
@@ -75,6 +81,7 @@ struct MODE_INFO{
 	uint8_t BankSize;
 	uint8_t NumberOfImagePages;
 	uint8_t Reserved_page;
+
 	uint8_t RedMaskSize;
 	uint8_t RedMaskPos;
 	uint8_t GreenMaskSize;
@@ -83,10 +90,12 @@ struct MODE_INFO{
 	uint8_t BlueMaskPos;
 	uint8_t ReservedMaskSize;
 	uint8_t ReservedMaskPos;
+
 	uint8_t DirectColorModeInfo;
 	uint32_t PhysBasePtr;
 	uint32_t OffScreenMemOffset;
 	uint16_t OffScreenMemSize;
+
 	uint8_t Reserved[206];
 } GCC_ATTRIBUTE(packed);
 #ifdef _MSC_VER
@@ -115,7 +124,7 @@ uint8_t VESA_GetSVGAInformation(const uint16_t segment, const uint16_t offset)
 	constexpr auto vesa_v1_2 = 0x0102;
 	constexpr auto vesa_v2_0 = 0x0200;
 
-	const auto vesa_version  = int10.vesa_oldvbe ? vesa_v1_2 : vesa_v2_0;
+	const auto vesa_version = int10.vesa_oldvbe ? vesa_v1_2 : vesa_v2_0;
 	mem_writew(buffer + 0x04, vesa_version);
 
 	if (vbe2) {
@@ -163,10 +172,11 @@ uint8_t VESA_GetSVGAInformation(const uint16_t segment, const uint16_t offset)
 	return VESA_SUCCESS;
 }
 
-// Ref: https://android.googlesource.com/kernel/msm/+/android-msm-bullhead-3.10-marshmallow-dr/Documentation/svga.txt
+// Ref:
+// https://android.googlesource.com/kernel/msm/+/android-msm-bullhead-3.10-marshmallow-dr/Documentation/svga.txt
 //
-// "2.7 (09-Apr-96) Accepted all VESA modes in range 0x100 to 0x7ff, because some
-// cards use very strange mode numbers.'
+// "2.7 (09-Apr-96) Accepted all VESA modes in range 0x100 to 0x7ff, because
+// some cards use very strange mode numbers.'
 bool VESA_IsVesaMode(const uint16_t bios_mode_number)
 {
 	return (bios_mode_number >= MinVesaBiosModeNumber &&
@@ -175,10 +185,11 @@ bool VESA_IsVesaMode(const uint16_t bios_mode_number)
 
 // Build-engine games have problem timing some non-standard,
 // low-resolution, 8-bit, linear-framebuffer VESA modes
-static bool on_build_engine_denylist(const VideoModeBlock &m)
+static bool on_build_engine_denylist(const VideoModeBlock& m)
 {
-	if (m.type != M_LIN8)
+	if (m.type != M_LIN8) {
 		return false;
+	}
 
 	const bool is_denied = (m.swidth == 320 && m.sheight == 240) ||
 	                       (m.swidth == 400 && m.sheight == 300) ||
@@ -186,7 +197,7 @@ static bool on_build_engine_denylist(const VideoModeBlock &m)
 	return is_denied;
 }
 
-static bool can_triple_buffer_8bit(const VideoModeBlock &m)
+static bool can_triple_buffer_8bit(const VideoModeBlock& m)
 {
 	assert(m.type == M_LIN8);
 	const auto padding = m.htotal;
@@ -194,46 +205,46 @@ static bool can_triple_buffer_8bit(const VideoModeBlock &m)
 	return vga.vmemsize >= needed_bytes;
 }
 
-uint8_t VESA_GetSVGAModeInformation(uint16_t mode,uint16_t seg,uint16_t off) {
+
+uint8_t VESA_GetSVGAModeInformation(uint16_t mode, uint16_t seg, uint16_t off)
+{
 	MODE_INFO minfo;
-	memset(&minfo,0,sizeof(minfo));
-	PhysPt buf=PhysicalMake(seg,off);
+	memset(&minfo, 0, sizeof(minfo));
+
+	PhysPt buf = PhysicalMake(seg, off);
+
 	int modePageSize = 0;
 	uint8_t modeAttributes;
 
-	mode&=0x3fff;	// vbe2 compatible, ignore lfb and keep screen content bits
+	mode &= 0x3fff; // vbe2 compatible, ignore lfb and keep screen content bits
 	if (mode < MinVesaBiosModeNumber) {
-		return 0x01;
+		return VESA_FAIL;
 	}
 	if (svga.accepts_mode) {
-		if (!svga.accepts_mode(mode)) return 0x01;
-	}
-
-	// Find the requested mode in our table of VGA modes
-	bool found_mode = false;
-	assert(ModeList_VGA.size());
-	auto mblock = ModeList_VGA.back();
-	for (auto &v : ModeList_VGA) {
-		if (v.mode == mode) {
-			mblock = v;
-			found_mode = true;
-			break;
+		if (!svga.accepts_mode(mode)) {
+			return VESA_FAIL;
 		}
 	}
-	if (!found_mode)
+
+	// Find the requested mode in our table of SVGA modes
+	const auto maybe_mode_block = INT10_FindSvgaVideoMode(mode);
+	if (!maybe_mode_block) {
 		return VESA_FAIL;
+	}
+	const auto mblock = *maybe_mode_block;
 
 	// Was the found mode VESA 2.0 but the user requested VESA 1.2?
-	if (mblock.mode >= vesa_2_0_modes_start && int10.vesa_oldvbe)
+	if (mblock.mode >= vesa_2_0_modes_start && int10.vesa_oldvbe) {
 		return VESA_FAIL;
+	}
 
 	switch (mblock.type) {
 	case M_LIN4:
-		modePageSize = mblock.sheight * mblock.swidth/8;
+		modePageSize           = mblock.sheight * mblock.swidth / 8;
 		minfo.BytesPerScanLine = host_to_le16(mblock.swidth / 8);
-		minfo.NumberOfPlanes = 0x4;
-		minfo.BitsPerPixel = 4u;
-		minfo.MemoryModel = 3u; // ega planar mode
+		minfo.NumberOfPlanes   = 0x4;
+		minfo.BitsPerPixel     = 4u;
+		minfo.MemoryModel      = 3u; // ega planar mode
 		modeAttributes = 0x1b; // Color, graphics, no linear buffer
 		break;
 
@@ -265,39 +276,45 @@ uint8_t VESA_GetSVGAModeInformation(uint16_t mode,uint16_t seg,uint16_t off) {
 	} break;
 
 	case M_LIN15:
-		modePageSize = mblock.sheight * mblock.swidth*2;
+		modePageSize           = mblock.sheight * mblock.swidth * 2;
 		minfo.BytesPerScanLine = host_to_le16(mblock.swidth * 2);
-		minfo.NumberOfPlanes = 0x1;
-		minfo.BitsPerPixel = 15u;
-		minfo.MemoryModel = 6u; // HiColour
-		minfo.RedMaskSize = 5u;
-		minfo.RedMaskPos = 10u;
-		minfo.GreenMaskSize = 5u;
-		minfo.GreenMaskPos = 5u;
-		minfo.BlueMaskSize = 5u;
-		minfo.BlueMaskPos = 0u;
+		minfo.NumberOfPlanes   = 0x1;
+		minfo.BitsPerPixel     = 15u;
+		minfo.MemoryModel      = 6u; // HiColour
+		minfo.RedMaskSize      = 5u;
+		minfo.RedMaskPos       = 10u;
+		minfo.GreenMaskSize    = 5u;
+		minfo.GreenMaskPos     = 5u;
+		minfo.BlueMaskSize     = 5u;
+		minfo.BlueMaskPos      = 0u;
 		minfo.ReservedMaskSize = 0x01;
-		minfo.ReservedMaskPos = 0x0f;
-		modeAttributes = 0x1b; // Color, graphics
-		if (!int10.vesa_nolfb)
+		minfo.ReservedMaskPos  = 0x0f;
+		modeAttributes         = 0x1b; // Color, graphics
+
+		if (!int10.vesa_nolfb) {
 			modeAttributes |= 0x80; // linear framebuffer
+		}
 		break;
+
 	case M_LIN16:
-		modePageSize = mblock.sheight * mblock.swidth*2;
+		modePageSize           = mblock.sheight * mblock.swidth * 2;
 		minfo.BytesPerScanLine = host_to_le16(mblock.swidth * 2);
-		minfo.NumberOfPlanes = 0x1;
-		minfo.BitsPerPixel = 16u;
-		minfo.MemoryModel = 6u; // HiColour
-		minfo.RedMaskSize = 5u;
-		minfo.RedMaskPos = 11u;
-		minfo.GreenMaskSize = 6u;
-		minfo.GreenMaskPos = 5u;
-		minfo.BlueMaskSize = 5u;
-		minfo.BlueMaskPos = 0u;
-		modeAttributes = 0x1b; // Color, graphics
-		if (!int10.vesa_nolfb)
+		minfo.NumberOfPlanes   = 0x1;
+		minfo.BitsPerPixel     = 16u;
+		minfo.MemoryModel      = 6u; // HiColour
+		minfo.RedMaskSize      = 5u;
+		minfo.RedMaskPos       = 11u;
+		minfo.GreenMaskSize    = 6u;
+		minfo.GreenMaskPos     = 5u;
+		minfo.BlueMaskSize     = 5u;
+		minfo.BlueMaskPos      = 0u;
+		modeAttributes         = 0x1b; // Color, graphics
+
+		if (!int10.vesa_nolfb) {
 			modeAttributes |= 0x80; // linear framebuffer
+		}
 		break;
+
 	case M_LIN24:
 		// Mode 0x212 has 128 extra bytes per scan line for
 		// compatibility with Windows 640x480 24-bit S3 Trio drivers
@@ -309,175 +326,220 @@ uint8_t VESA_GetSVGAModeInformation(uint16_t mode,uint16_t seg,uint16_t off) {
 			minfo.BytesPerScanLine = host_to_le16(mblock.swidth * 3);
 		}
 		minfo.NumberOfPlanes = 0x1u;
-		minfo.BitsPerPixel = 24u;
-		minfo.MemoryModel = 6u; // HiColour
-		minfo.RedMaskSize = 8u;
-		minfo.RedMaskPos = 0x10;
-		minfo.GreenMaskSize = 0x8;
-		minfo.GreenMaskPos = 0x8;
-		minfo.BlueMaskSize = 0x8;
-		minfo.BlueMaskPos = 0x0;
-		modeAttributes = 0x1b; // Color, graphics
-		if (!int10.vesa_nolfb)
+		minfo.BitsPerPixel   = 24u;
+		minfo.MemoryModel    = 6u; // HiColour
+		minfo.RedMaskSize    = 8u;
+		minfo.RedMaskPos     = 0x10;
+		minfo.GreenMaskSize  = 0x8;
+		minfo.GreenMaskPos   = 0x8;
+		minfo.BlueMaskSize   = 0x8;
+		minfo.BlueMaskPos    = 0x0;
+		modeAttributes       = 0x1b; // Color, graphics
+
+		if (!int10.vesa_nolfb) {
 			modeAttributes |= 0x80; // linear framebuffer
+		}
 		break;
+
 	case M_LIN32:
-		modePageSize = mblock.sheight * mblock.swidth*4;
+		modePageSize           = mblock.sheight * mblock.swidth * 4;
 		minfo.BytesPerScanLine = host_to_le16(mblock.swidth * 4);
-		minfo.NumberOfPlanes = 0x1u;
-		minfo.BitsPerPixel = 32u;
-		minfo.MemoryModel = 6u; // HiColour
-		minfo.RedMaskSize = 8u;
-		minfo.RedMaskPos = 0x10;
-		minfo.GreenMaskSize = 0x8;
-		minfo.GreenMaskPos = 0x8;
-		minfo.BlueMaskSize = 0x8;
-		minfo.BlueMaskPos = 0x0;
+		minfo.NumberOfPlanes   = 0x1u;
+		minfo.BitsPerPixel     = 32u;
+		minfo.MemoryModel      = 6u; // HiColour
+		minfo.RedMaskSize      = 8u;
+		minfo.RedMaskPos       = 0x10;
+		minfo.GreenMaskSize    = 0x8;
+		minfo.GreenMaskPos     = 0x8;
+		minfo.BlueMaskSize     = 0x8;
+		minfo.BlueMaskPos      = 0x0;
 		minfo.ReservedMaskSize = 0x8;
-		minfo.ReservedMaskPos = 0x18;
-		modeAttributes = 0x1b; // Color, graphics
-		if (!int10.vesa_nolfb)
+		minfo.ReservedMaskPos  = 0x18;
+		modeAttributes         = 0x1b; // Color, graphics
+
+		if (!int10.vesa_nolfb) {
 			modeAttributes |= 0x80; // linear framebuffer
+		}
 		break;
+
 	case M_TEXT:
-		modePageSize = 0;
+		modePageSize           = 0;
 		minfo.BytesPerScanLine = host_to_le16(mblock.twidth * 2);
-		minfo.NumberOfPlanes = 0x4;
-		minfo.BitsPerPixel = 4u;
-		minfo.MemoryModel = 0u; // text
-		modeAttributes = 0x0f; // Color, text, bios output
+		minfo.NumberOfPlanes   = 0x4;
+		minfo.BitsPerPixel     = 4u;
+		minfo.MemoryModel      = 0u;   // text
+		modeAttributes         = 0x0f; // Color, text, bios output
 		break;
-	default:
-		return VESA_FAIL;
+
+	default: return VESA_FAIL;
 	}
+
 	if (modePageSize & 0xFFFF) {
-		// It is documented that many applications assume 64k-aligned page sizes
-		// VBETEST is one of them
+		// It is documented that many applications assume 64k-aligned
+		// page sizes VBETEST is one of them
 		modePageSize += 0x10000;
 		modePageSize &= ~0xFFFF;
 	}
+
 	int modePages = 0;
+
 	if (modePageSize > static_cast<int>(vga.vmemsize)) {
 		// mode not supported by current hardware configuration
 		modeAttributes &= ~0x1;
 	} else if (modePageSize) {
-		modePages = (vga.vmemsize / modePageSize)-1;
-	}	
+		modePages = (vga.vmemsize / modePageSize) - 1;
+	}
 	assert(modePages <= UINT8_MAX);
+
 	minfo.NumberOfImagePages = static_cast<uint8_t>(modePages);
-	minfo.ModeAttributes = host_to_le16(modeAttributes);
-	minfo.WinAAttributes = 0x7; // Exists/readable/writable
+	minfo.ModeAttributes     = host_to_le16(modeAttributes);
+	minfo.WinAAttributes     = 0x7; // Exists/readable/writable
 
-	if (mblock.type==M_TEXT) {
+	if (mblock.type == M_TEXT) {
 		minfo.WinGranularity = host_to_le16(32u);
-		minfo.WinSize = host_to_le16(32u);
-		minfo.WinASegment = host_to_le16(0xb800);
-		minfo.XResolution = host_to_le16(mblock.twidth);
-		minfo.YResolution = host_to_le16(mblock.theight);
+		minfo.WinSize        = host_to_le16(32u);
+		minfo.WinASegment    = host_to_le16(0xb800);
+		minfo.XResolution    = host_to_le16(mblock.twidth);
+		minfo.YResolution    = host_to_le16(mblock.theight);
 	} else {
 		minfo.WinGranularity = host_to_le16(64u);
-		minfo.WinSize = host_to_le16(64u);
-		minfo.WinASegment = host_to_le16(0xa000);
-		minfo.XResolution = host_to_le16(mblock.swidth);
-		minfo.YResolution = host_to_le16(mblock.sheight);
+		minfo.WinSize        = host_to_le16(64u);
+		minfo.WinASegment    = host_to_le16(0xa000);
+		minfo.XResolution    = host_to_le16(mblock.swidth);
+		minfo.YResolution    = host_to_le16(mblock.sheight);
 	}
-	minfo.WinFuncPtr = host_to_le32(int10.rom.set_window);
+
+	minfo.WinFuncPtr    = host_to_le32(int10.rom.set_window);
 	minfo.NumberOfBanks = 0x1;
 	minfo.Reserved_page = 0x1;
-	minfo.XCharSize = mblock.cwidth;
-	minfo.YCharSize = mblock.cheight;
-	if (!int10.vesa_nolfb)
+	minfo.XCharSize     = mblock.cwidth;
+	minfo.YCharSize     = mblock.cheight;
+
+	if (!int10.vesa_nolfb) {
 		minfo.PhysBasePtr = host_to_le32(PciGfxLfbBase);
+	}
 
-	MEM_BlockWrite(buf,&minfo,sizeof(MODE_INFO));
+	MEM_BlockWrite(buf, &minfo, sizeof(MODE_INFO));
 	return VESA_SUCCESS;
 }
 
-uint8_t VESA_SetSVGAMode(uint16_t mode) {
+uint8_t VESA_SetSVGAMode(uint16_t mode)
+{
 	if (INT10_SetVideoMode(mode)) {
-		int10.vesa_setmode=mode&0x7fff;
+		int10.vesa_setmode = mode & 0x7fff;
 		return VESA_SUCCESS;
 	}
 	return VESA_FAIL;
 }
 
-uint8_t VESA_GetSVGAMode(uint16_t & mode) {
-	if (int10.vesa_setmode!=0xffff) mode=int10.vesa_setmode;
-	else mode=CurMode->mode;
+uint8_t VESA_GetSVGAMode(uint16_t& mode)
+{
+	if (int10.vesa_setmode != 0xffff) {
+		mode = int10.vesa_setmode;
+	} else {
+		mode = CurMode->mode;
+	}
 	return VESA_SUCCESS;
 }
 
-uint8_t VESA_SetCPUWindow(uint8_t window,uint8_t address) {
-	if (window) return VESA_FAIL;
-	if ((uint32_t)(address)*64*1024 < vga.vmemsize) {
-		IO_Write(0x3d4,0x6a);
+uint8_t VESA_SetCPUWindow(uint8_t window, uint8_t address)
+{
+	if (window) {
+		return VESA_FAIL;
+	}
+	if ((uint32_t)(address) * 64 * 1024 < vga.vmemsize) {
+		IO_Write(0x3d4, 0x6a);
 		IO_Write(0x3d5, address);
 		return VESA_SUCCESS;
-	} else return VESA_FAIL;
+	} else {
+		return VESA_FAIL;
+	}
 }
 
-uint8_t VESA_GetCPUWindow(uint8_t window,uint16_t & address) {
-	if (window) return VESA_FAIL;
-	IO_Write(0x3d4,0x6a);
-	address=IO_Read(0x3d5);
+uint8_t VESA_GetCPUWindow(uint8_t window, uint16_t& address)
+{
+	if (window) {
+		return VESA_FAIL;
+	}
+	IO_Write(0x3d4, 0x6a);
+	address = IO_Read(0x3d5);
 	return VESA_SUCCESS;
 }
 
+uint8_t VESA_SetPalette(PhysPt data, Bitu index, Bitu count, bool wait)
+{
+	// Structure is (vesa 3.0 doc): blue,green,red,alignment
+	uint8_t r, g, b;
 
-uint8_t VESA_SetPalette(PhysPt data,Bitu index,Bitu count,bool wait) {
-//Structure is (vesa 3.0 doc): blue,green,red,alignment
-	uint8_t r,g,b;
-	if (index>255) return VESA_FAIL;
-	if (index+count>256) return VESA_FAIL;
+	if (index > 255) {
+		return VESA_FAIL;
+	}
+	if (index + count > 256) {
+		return VESA_FAIL;
+	}
 
 	// Wait for retrace if requested
-	if (wait) CALLBACK_RunRealFar(RealSegment(int10.rom.wait_retrace),RealOffset(int10.rom.wait_retrace));
+	if (wait) {
+		CALLBACK_RunRealFar(RealSegment(int10.rom.wait_retrace),
+		                    RealOffset(int10.rom.wait_retrace));
+	}
+
+	IO_Write(0x3c8, (uint8_t)index);
 
-	IO_Write(0x3c8,(uint8_t)index);
 	while (count) {
 		b = mem_readb(data++);
 		g = mem_readb(data++);
 		r = mem_readb(data++);
+
 		data++;
-		IO_Write(0x3c9,r);
-		IO_Write(0x3c9,g);
-		IO_Write(0x3c9,b);
+
+		IO_Write(0x3c9, r);
+		IO_Write(0x3c9, g);
+		IO_Write(0x3c9, b);
+
 		count--;
 	}
 	return VESA_SUCCESS;
 }
 
+uint8_t VESA_GetPalette(PhysPt data, Bitu index, Bitu count)
+{
+	uint8_t r, g, b;
+
+	if (index > 255) {
+		return VESA_FAIL;
+	}
+	if (index + count > 256) {
+		return VESA_FAIL;
+	}
+
+	IO_Write(0x3c7, (uint8_t)index);
 
-uint8_t VESA_GetPalette(PhysPt data,Bitu index,Bitu count) {
-	uint8_t r,g,b;
-	if (index>255) return VESA_FAIL;
-	if (index+count>256) return VESA_FAIL;
-	IO_Write(0x3c7,(uint8_t)index);
 	while (count) {
 		r = IO_Read(0x3c9);
 		g = IO_Read(0x3c9);
 		b = IO_Read(0x3c9);
-		mem_writeb(data++,b);
-		mem_writeb(data++,g);
-		mem_writeb(data++,r);
+
+		mem_writeb(data++, b);
+		mem_writeb(data++, g);
+		mem_writeb(data++, r);
+
 		data++;
 		count--;
 	}
+
 	return VESA_SUCCESS;
 }
 
-uint8_t VESA_ScanLineLength(uint8_t subcall,
-                            uint16_t val,
-                            uint16_t &bytes,
-                            uint16_t &pixels,
-                            uint16_t &lines)
+uint8_t VESA_ScanLineLength(uint8_t subcall, uint16_t val, uint16_t& bytes,
+                            uint16_t& pixels, uint16_t& lines)
 {
 	// offset register: virtual scanline length
-	auto new_offset = static_cast<int>(vga.config.scan_len);
-	auto screen_height = CurMode->sheight;
-	auto usable_vmem_bytes = vga.vmemsize;
-	uint8_t bits_per_pixel = 0;
-	uint8_t bytes_per_offset = 8;
+	auto new_offset                 = static_cast<int>(vga.config.scan_len);
+	auto screen_height              = CurMode->sheight;
+	auto usable_vmem_bytes          = vga.vmemsize;
+	uint8_t bits_per_pixel          = 0;
+	uint8_t bytes_per_offset        = 8;
 	bool align_to_nearest_4th_pixel = false;
 
 	// LOG_MSG("VESA_ScanLineLength: s-%lux%lu, t-%lux%lu, c-%lux%lu,
@@ -491,27 +553,33 @@ uint8_t VESA_ScanLineLength(uint8_t subcall,
 	case M_TEXT:
 		// In text mode we only have a 32 KB window to operate on
 		usable_vmem_bytes = 32 * 1024;
-		screen_height = CurMode->theight;
-		bytes_per_offset = 4;   // 2 characters + 2 attributes
-		bits_per_pixel = 4;
+		screen_height     = CurMode->theight;
+		bytes_per_offset  = 4; // 2 characters + 2 attributes
+		bits_per_pixel    = 4;
 		break;
+
 	case M_LIN4:
 		bytes_per_offset = 2;
-		bits_per_pixel = 4;
+		bits_per_pixel   = 4;
 		usable_vmem_bytes /= 4; // planar mode
 		break;
+
 	case M_LIN8: bits_per_pixel = 8; break;
-	case M_LIN15:
+
+	case M_LIN15: [[fallthrough]];
 	case M_LIN16: bits_per_pixel = 16; break;
+
 	case M_LIN24:
 		align_to_nearest_4th_pixel = true;
-		bits_per_pixel = 24;
+		bits_per_pixel             = 24;
 		break;
+
 	case M_LIN32: bits_per_pixel = 32; break;
-	default:
-		return VESA_MODE_UNSUPPORTED;
+	default: return VESA_MODE_UNSUPPORTED;
 	}
-	constexpr int gcd = 8 * 8; // greatest common dividsor
+
+	// greatest common dividsor
+	constexpr int gcd = 8 * 8;
 
 	// The 'bytes' and 'pixels' return values
 	// (reference-assigns) are multiplied up from the offset length, so here
@@ -524,11 +592,13 @@ uint8_t VESA_ScanLineLength(uint8_t subcall,
 	switch (subcall) {
 	case 0x00: // set scan length in pixels
 		new_offset = val * bits_per_pixel / gcd;
-		if (align_to_nearest_4th_pixel)
+		if (align_to_nearest_4th_pixel) {
 			new_offset -= (new_offset % 3);
+		}
 
-		if (new_offset > max_offset)
+		if (new_offset > max_offset) {
 			return VESA_HW_UNSUPPORTED; // scanline too long
+		}
 		vga.config.scan_len = check_cast<uint16_t>(new_offset);
 		VGA_CheckScanLength();
 		break;
@@ -539,8 +609,9 @@ uint8_t VESA_ScanLineLength(uint8_t subcall,
 
 	case 0x02: // set scan length in bytes
 		new_offset = ceil_udivide(val, bytes_per_offset);
-		if (new_offset > max_offset)
+		if (new_offset > max_offset) {
 			return VESA_HW_UNSUPPORTED; // scanline too long
+		}
 		vga.config.scan_len = check_cast<uint16_t>(new_offset);
 		VGA_CheckScanLength();
 		break;
@@ -550,16 +621,17 @@ uint8_t VESA_ScanLineLength(uint8_t subcall,
 		// the limit to get full y resolution of this mode
 		new_offset = max_offset;
 		if ((new_offset * bytes_per_offset * screen_height) >
-		    static_cast<int>(usable_vmem_bytes))
-			new_offset = usable_vmem_bytes / (bytes_per_offset * screen_height);
+		    static_cast<int>(usable_vmem_bytes)) {
+			new_offset = usable_vmem_bytes /
+			             (bytes_per_offset * screen_height);
+		}
 		break;
 
-	default:
-		return VESA_UNIMPLEMENTED;
+	default: return VESA_UNIMPLEMENTED;
 	}
 
 	// set up the return values
-	bytes = check_cast<uint16_t>(new_offset * bytes_per_offset);
+	bytes  = check_cast<uint16_t>(new_offset * bytes_per_offset);
 	pixels = check_cast<uint16_t>(new_offset * gcd / bits_per_pixel);
 	if (!bytes) {
 		// return failure on division by zero
@@ -567,15 +639,17 @@ uint8_t VESA_ScanLineLength(uint8_t subcall,
 		return VESA_FAIL;
 	}
 	const auto supported_lines = usable_vmem_bytes / bytes;
-	const auto gap = supported_lines % screen_height;
-	constexpr uint8_t max_gap = 8;
-	lines = gap < max_gap ? screen_height
-	                      : check_cast<uint16_t>(supported_lines);
+	const auto gap             = supported_lines % screen_height;
+	constexpr uint8_t max_gap  = 8;
+	lines                      = gap < max_gap ? screen_height
+	                                           : check_cast<uint16_t>(supported_lines);
 
-	if (CurMode->type==M_TEXT)
+	if (CurMode->type == M_TEXT) {
 		lines *= CurMode->cheight;
+	}
 
-	// LOG_MSG("VESA_ScanLineLength subcall=%u, val=%u, vga.config.scan_len = %lu,"
+	// LOG_MSG("VESA_ScanLineLength subcall=%u, val=%u, vga.config.scan_len
+	// = %lu,"
 	//         "pixels = %u, bytes = %u, lines = %u, supported_lines = %d ",
 	//         subcall, val, vga.config.scan_len, pixels,
 	//         bytes, lines, supported_lines);
@@ -583,144 +657,180 @@ uint8_t VESA_ScanLineLength(uint8_t subcall,
 	return VESA_SUCCESS;
 }
 
-uint8_t VESA_SetDisplayStart(uint16_t x,uint16_t y,bool wait) {
+uint8_t VESA_SetDisplayStart(uint16_t x, uint16_t y, bool wait)
+{
 	uint8_t panning_factor = 1;
 	uint8_t bits_per_pixel = 0;
+
 	bool align_to_nearest_4th_pixel = false;
+
 	switch (CurMode->type) {
-	case M_TEXT:
+	case M_TEXT: [[fallthrough]];
 	case M_LIN4: bits_per_pixel = 4; break;
+
 	case M_LIN8:
 		bits_per_pixel = 8;
-		panning_factor = 2; // the panning register ignores bit0 in this mode
+		panning_factor = 2; // the panning register ignores bit0 in this
+		                    // mode
 		break;
-	case M_LIN15:
+
+	case M_LIN15: [[fallthrough]];
 	case M_LIN16:
 		bits_per_pixel = 16;
 		panning_factor = 2; // this may be DOSBox specific
 		break;
+
 	case M_LIN24:
 		align_to_nearest_4th_pixel = true;
-		bits_per_pixel = 24;
+		bits_per_pixel             = 24;
 		break;
+
 	case M_LIN32: bits_per_pixel = 32; break;
 	default: return VESA_MODE_UNSUPPORTED;
 	}
-	constexpr uint8_t lcf = 32; // least common factor
+
+	// least common factor
+	constexpr uint8_t lcf = 32;
+
 	uint32_t start = (vga.config.scan_len * lcf * 2 * y + x * bits_per_pixel) / lcf;
-	if (align_to_nearest_4th_pixel)
+	if (align_to_nearest_4th_pixel) {
 		start -= (start % 3);
+	}
+
 	vga.config.display_start = start;
 
 	// Setting the panning register is nice as it allows for super smooth
 	// scrolling, but if we hit the retrace pulse there may be flicker as
-	// panning and display start are latched at different times. 
+	// panning and display start are latched at different times.
 
-	IO_Read(0x3da);              // reset attribute flipflop
-	IO_Write(0x3c0,0x13 | 0x20); // panning register, screen on
+	// reset attribute flipflop
+	IO_Read(0x3da);
+	// panning register, screen on
+	IO_Write(0x3c0, 0x13 | 0x20);
 
 	const auto new_panning = x % (lcf / bits_per_pixel);
 	IO_Write(0x3c0, static_cast<uint8_t>(new_panning * panning_factor));
 
 	// Wait for retrace if requested
-	if (wait) CALLBACK_RunRealFar(RealSegment(int10.rom.wait_retrace),RealOffset(int10.rom.wait_retrace));
+	if (wait) {
+		CALLBACK_RunRealFar(RealSegment(int10.rom.wait_retrace),
+		                    RealOffset(int10.rom.wait_retrace));
+	}
 
 	return VESA_SUCCESS;
 }
 
-uint8_t VESA_GetDisplayStart(uint16_t & x,uint16_t & y) {
+uint8_t VESA_GetDisplayStart(uint16_t& x, uint16_t& y)
+{
 	Bitu pixels_per_offset;
 	Bitu panning_factor = 1;
 
 	switch (CurMode->type) {
-	case M_TEXT:
-	case M_LIN4:
-		pixels_per_offset = 16;
-		break;
+	case M_TEXT: [[fallthrough]];
+	case M_LIN4: pixels_per_offset = 16; break;
+
 	case M_LIN8:
-		panning_factor = 2;
+		panning_factor    = 2;
 		pixels_per_offset = 8;
 		break;
-	case M_LIN15:
+
+	case M_LIN15: [[fallthrough]];
 	case M_LIN16:
-		panning_factor = 2;
+		panning_factor    = 2;
 		pixels_per_offset = 4;
 		break;
-	case M_LIN32:
-		pixels_per_offset = 2;
-		break;
-	default:
-		return VESA_MODE_UNSUPPORTED;
+
+	case M_LIN32: pixels_per_offset = 2; break;
+	default: return VESA_MODE_UNSUPPORTED;
 	}
 
-	IO_Read(0x3da);              // reset attribute flipflop
-	IO_Write(0x3c0,0x13 | 0x20); // panning register, screen on
+	// reset attribute flipflop
+	IO_Read(0x3da);
+	// panning register, screen on
+	IO_Write(0x3c0, 0x13 | 0x20);
+
 	uint8_t panning = IO_Read(0x3c1);
 
 	Bitu virtual_screen_width = vga.config.scan_len * pixels_per_offset;
-	Bitu start_pixel = vga.config.display_start * (pixels_per_offset/2) 
-		+ panning / panning_factor;
-	
+	Bitu start_pixel = vga.config.display_start * (pixels_per_offset / 2) +
+	                   panning / panning_factor;
+
 	y = start_pixel / virtual_screen_width;
 	x = start_pixel % virtual_screen_width;
+
 	return VESA_SUCCESS;
 }
 
-static Bitu VESA_SetWindow(void) {
-	if (reg_bh) reg_ah=VESA_GetCPUWindow(reg_bl,reg_dx);
-	else reg_ah=VESA_SetCPUWindow(reg_bl,(uint8_t)reg_dx);
-	reg_al=0x4f;
+static Bitu VESA_SetWindow(void)
+{
+	if (reg_bh) {
+		reg_ah = VESA_GetCPUWindow(reg_bl, reg_dx);
+	} else {
+		reg_ah = VESA_SetCPUWindow(reg_bl, (uint8_t)reg_dx);
+	}
+	reg_al = 0x4f;
+
 	return CBRET_NONE;
 }
 
-static Bitu VESA_PMSetWindow(void) {
-	IO_Write(0x3d4,0x6a);
-	IO_Write(0x3d5,reg_dl);
+static Bitu VESA_PMSetWindow(void)
+{
+	IO_Write(0x3d4, 0x6a);
+	IO_Write(0x3d5, reg_dl);
 	return CBRET_NONE;
 }
-static Bitu VESA_PMSetPalette(void) {
-	PhysPt data=SegPhys(es)+reg_edi;
-	uint32_t count=reg_cx;
-	IO_Write(0x3c8,reg_dl);
+static Bitu VESA_PMSetPalette(void)
+{
+	PhysPt data    = SegPhys(es) + reg_edi;
+	uint32_t count = reg_cx;
+
+	IO_Write(0x3c8, reg_dl);
+
 	do {
-		IO_Write(0x3c9,mem_readb(data+2));
-		IO_Write(0x3c9,mem_readb(data+1));
-		IO_Write(0x3c9,mem_readb(data));
-		data+=4;
+		IO_Write(0x3c9, mem_readb(data + 2));
+		IO_Write(0x3c9, mem_readb(data + 1));
+		IO_Write(0x3c9, mem_readb(data));
+		data += 4;
 	} while (--count);
+
 	return CBRET_NONE;
 }
-static Bitu VESA_PMSetStart(void) {
-	uint32_t start = (reg_dx << 16) | reg_cx;
+static Bitu VESA_PMSetStart(void)
+{
+	uint32_t start           = (reg_dx << 16) | reg_cx;
 	vga.config.display_start = start;
+
 	return CBRET_NONE;
 }
 
+void INT10_SetupVESA(void)
+{
+	// Put the mode list somewhere in memory */
+	Bitu i = 0;
 
+	int10.rom.vesa_modes = RealMake(0xc000, int10.rom.used);
 
-
-void INT10_SetupVESA(void) {
-	/* Put the mode list somewhere in memory */
-	Bitu i;
-	i=0;
-	int10.rom.vesa_modes=RealMake(0xc000,int10.rom.used);
-//TODO Maybe add normal vga modes too, but only seems to complicate things
-	while (ModeList_VGA[i].mode!=0xffff) {
-		bool canuse_mode=false;
-		if (!svga.accepts_mode) canuse_mode=true;
-		else {
-			if (svga.accepts_mode(ModeList_VGA[i].mode)) canuse_mode=true;
+	// TODO Maybe add normal vga modes too, but only seems to complicate things
+	while (ModeList_VGA[i].mode != 0xffff) {
+		bool canuse_mode = false;
+		if (!svga.accepts_mode) {
+			canuse_mode = true;
+		} else {
+			if (svga.accepts_mode(ModeList_VGA[i].mode)) {
+				canuse_mode = true;
+			}
 		}
 		if (ModeList_VGA[i].mode >= MinVesaBiosModeNumber && canuse_mode) {
 			if (!int10.vesa_oldvbe || ModeList_VGA[i].mode < 0x120) {
-				phys_writew(PhysicalMake(0xc000, int10.rom.used), ModeList_VGA[i].mode);
+				phys_writew(PhysicalMake(0xc000, int10.rom.used),
+				            ModeList_VGA[i].mode);
 				int10.rom.used += 2;
 			}
 		}
 		i++;
 	}
-	phys_writew(PhysicalMake(0xc000,int10.rom.used),0xffff);
-	int10.rom.used+=2;
+	phys_writew(PhysicalMake(0xc000, int10.rom.used), 0xffff);
+	int10.rom.used += 2;
 
 	int10.rom.oemstring = RealMake(0xc000, int10.rom.used);
 	for (const char& c : string_oem) {
@@ -728,35 +838,75 @@ void INT10_SetupVESA(void) {
 	}
 	phys_writeb(0xc0000 + int10.rom.used++, 0);
 
-	/* Prepare the real mode interface */
-	int10.rom.wait_retrace=RealMake(0xc000,int10.rom.used);
-	int10.rom.used += (uint16_t)CALLBACK_Setup(0, nullptr, CB_VESA_WAIT, PhysicalMake(0xc000,int10.rom.used), "");
-	callback.rmWindow=CALLBACK_Allocate();
-	int10.rom.set_window=RealMake(0xc000,int10.rom.used);
-	int10.rom.used += (uint16_t)CALLBACK_Setup(callback.rmWindow, VESA_SetWindow, CB_RETF, PhysicalMake(0xc000,int10.rom.used), "VESA Real Set Window");
-	/* Prepare the pmode interface */
-	int10.rom.pmode_interface=RealMake(0xc000,int10.rom.used);
-	int10.rom.used += 8;		//Skip the byte later used for offsets
-	/* PM Set Window call */
-	int10.rom.pmode_interface_window = int10.rom.used - RealOffset( int10.rom.pmode_interface );
-	phys_writew( RealToPhysical(int10.rom.pmode_interface) + 0, int10.rom.pmode_interface_window );
-	callback.pmWindow=CALLBACK_Allocate();
-	int10.rom.used += (uint16_t)CALLBACK_Setup(callback.pmWindow, VESA_PMSetWindow, CB_RETN, PhysicalMake(0xc000,int10.rom.used), "VESA PM Set Window");
-	/* PM Set start call */
-	int10.rom.pmode_interface_start = int10.rom.used - RealOffset( int10.rom.pmode_interface );
-	phys_writew( RealToPhysical(int10.rom.pmode_interface) + 2, int10.rom.pmode_interface_start);
-	callback.pmStart=CALLBACK_Allocate();
+	// Prepare the real mode interface
+	int10.rom.wait_retrace = RealMake(0xc000, int10.rom.used);
+	int10.rom.used += (uint16_t)CALLBACK_Setup(
+	        0, nullptr, CB_VESA_WAIT, PhysicalMake(0xc000, int10.rom.used), "");
+
+	callback.rmWindow    = CALLBACK_Allocate();
+	int10.rom.set_window = RealMake(0xc000, int10.rom.used);
+
+	int10.rom.used += (uint16_t)CALLBACK_Setup(callback.rmWindow,
+	                                           VESA_SetWindow,
+	                                           CB_RETF,
+	                                           PhysicalMake(0xc000,
+	                                                        int10.rom.used),
+	                                           "VESA Real Set Window");
+	// Prepare the pmode interface
+	int10.rom.pmode_interface = RealMake(0xc000, int10.rom.used);
+	// Skip the byte later used for offsets
+	int10.rom.used += 8;
+
+	// PM Set Window call
+	int10.rom.pmode_interface_window = int10.rom.used -
+	                                   RealOffset(int10.rom.pmode_interface);
+
+	phys_writew(RealToPhysical(int10.rom.pmode_interface) + 0,
+	            int10.rom.pmode_interface_window);
+
+	callback.pmWindow = CALLBACK_Allocate();
+	int10.rom.used += (uint16_t)CALLBACK_Setup(callback.pmWindow,
+	                                           VESA_PMSetWindow,
+	                                           CB_RETN,
+	                                           PhysicalMake(0xc000,
+	                                                        int10.rom.used),
+	                                           "VESA PM Set Window");
+	// PM Set start call
+	int10.rom.pmode_interface_start = int10.rom.used -
+	                                  RealOffset(int10.rom.pmode_interface);
+
+	phys_writew(RealToPhysical(int10.rom.pmode_interface) + 2,
+	            int10.rom.pmode_interface_start);
+
+	callback.pmStart = CALLBACK_Allocate();
 	int10.rom.used += (uint16_t)CALLBACK_Setup(callback.pmStart,
-	                                         VESA_PMSetStart, CB_VESA_PM,
-	                                         PhysicalMake(0xc000, int10.rom.used),
-	                                         "VESA PM Set Start");
-	/* PM Set Palette call */
-	int10.rom.pmode_interface_palette = int10.rom.used - RealOffset( int10.rom.pmode_interface );
-	phys_writew( RealToPhysical(int10.rom.pmode_interface) + 4, int10.rom.pmode_interface_palette);
-	callback.pmPalette=CALLBACK_Allocate();
-	int10.rom.used += (uint16_t)CALLBACK_Setup(0, nullptr, CB_VESA_PM, PhysicalMake(0xc000,int10.rom.used), "");
-	int10.rom.used += (uint16_t)CALLBACK_Setup(callback.pmPalette, VESA_PMSetPalette, CB_RETN, PhysicalMake(0xc000,int10.rom.used), "VESA PM Set Palette");
-	/* Finalize the size and clear the required ports pointer */
-	phys_writew( RealToPhysical(int10.rom.pmode_interface) + 6, 0);
-	int10.rom.pmode_interface_size=int10.rom.used - RealOffset( int10.rom.pmode_interface );
+	                                           VESA_PMSetStart,
+	                                           CB_VESA_PM,
+	                                           PhysicalMake(0xc000,
+	                                                        int10.rom.used),
+	                                           "VESA PM Set Start");
+	// PM Set Palette call
+	int10.rom.pmode_interface_palette = int10.rom.used -
+	                                    RealOffset(int10.rom.pmode_interface);
+
+	phys_writew(RealToPhysical(int10.rom.pmode_interface) + 4,
+	            int10.rom.pmode_interface_palette);
+
+	callback.pmPalette = CALLBACK_Allocate();
+
+	int10.rom.used += (uint16_t)CALLBACK_Setup(
+	        0, nullptr, CB_VESA_PM, PhysicalMake(0xc000, int10.rom.used), "");
+
+	int10.rom.used += (uint16_t)CALLBACK_Setup(callback.pmPalette,
+	                                           VESA_PMSetPalette,
+	                                           CB_RETN,
+	                                           PhysicalMake(0xc000,
+	                                                        int10.rom.used),
+	                                           "VESA PM Set Palette");
+
+	// Finalize the size and clear the required ports pointer
+	phys_writew(RealToPhysical(int10.rom.pmode_interface) + 6, 0);
+
+	int10.rom.pmode_interface_size = int10.rom.used -
+	                                 RealOffset(int10.rom.pmode_interface);
 }


Reviews:
user: login: shermp
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-21T22:20:05Z
user: login: johnnovak
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-21T22:33:12Z
user: login: shermp
body: Haven't tested, but looks ok. `contains()` is definitely easier to read.
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-22T04:55:49Z

Review comments:
diff_hunk: @@ -751,9 +751,18 @@ void filter_compatible_s3_vesa_modes()
 	}
 
 	auto mode_allowed = [&](const VideoModeBlock& m) {
-		// Allow all text modes
+		// Only allow standard text modes
 		if (m.type == M_TEXT) {
-			return true;
+			constexpr auto _132x28 = 0x230;
+			constexpr auto _132x30 = 0x231;
+			constexpr auto _132x34 = 0x232;
+
+			const std::vector disallowed_modes = {_132x28, _132x30, _132x34};
+
+			const auto is_allowed = (std::ranges::find(disallowed_modes,
+			                                           m.mode) ==
+			                         disallowed_modes.end());
+			return is_allowed;
path: src/hardware/vga_s3.cpp
user: login: shermp
body: We have a `contains()` convenience template function in `support.h` that could be used?

It could become as compact as something like:

```cpp
return !contains(std::array{_132x28, _132x30, _132x34}, m.mode);
```
created_at: 2025-03-21T22:18:01Z
author_association: COLLABORATOR
diff_hunk: @@ -772,16 +772,33 @@ void filter_compatible_s3_vesa_modes()
 		}
 
 		// Allow common standard VESA modes, except 320x200 hi-color
-		// modes that were rarely properly supported until the late 90s.
+		// modes that were rarely properly supported until the late 90s,
+		// and the DOSBox-specific widescreen modes.
 		constexpr auto s3_vesa_modes_start = 0x150;
 
 		if (m.mode < s3_vesa_modes_start) {
 			constexpr auto _320x200_15bit = 0x10d;
 			constexpr auto _320x200_16bit = 0x10e;
 			constexpr auto _320x200_32bit = 0x10f;
 
-			return !(m.mode == _320x200_15bit || m.mode == _320x200_16bit ||
-			         m.mode == _320x200_32bit);
+			// Additional DOSBox-specific widescreen modes
+			constexpr auto _848x480_8bit  = 0x222;
+			constexpr auto _848x480_15bit = 0x223;
+			constexpr auto _848x480_16bit = 0x224;
+			constexpr auto _848x480_32bit = 0x225;
+
+			const std::vector disallowed_modes = {_320x200_15bit,
+			                                      _320x200_16bit,
+			                                      _320x200_32bit,
+			                                      _848x480_8bit,
+			                                      _848x480_15bit,
+			                                      _848x480_16bit,
+			                                      _848x480_32bit};
+
+			const auto is_allowed = (std::ranges::find(disallowed_modes,
+			                                           m.mode) ==
+			                         disallowed_modes.end());
+			return is_allowed;
path: src/hardware/vga_s3.cpp
user: login: shermp
body: `contains()` from `support.h`?
created_at: 2025-03-21T22:18:38Z
author_association: COLLABORATOR
diff_hunk: @@ -751,9 +751,18 @@ void filter_compatible_s3_vesa_modes()
 	}
 
 	auto mode_allowed = [&](const VideoModeBlock& m) {
-		// Allow all text modes
+		// Only allow standard text modes
 		if (m.type == M_TEXT) {
-			return true;
+			constexpr auto _132x28 = 0x230;
+			constexpr auto _132x30 = 0x231;
+			constexpr auto _132x34 = 0x232;
+
+			const std::vector disallowed_modes = {_132x28, _132x30, _132x34};
+
+			const auto is_allowed = (std::ranges::find(disallowed_modes,
+			                                           m.mode) ==
+			                         disallowed_modes.end());
+			return is_allowed;
path: src/hardware/vga_s3.cpp
user: login: johnnovak
body: Ah, cheers, forgot about that. Yeah I don't like it either how verbose this is.
created_at: 2025-03-21T22:33:12Z
author_association: MEMBER

PR comments:
user: login: johnnovak
created_at: 2025-03-21T13:30:51Z
body: @jordigo If interested, you can test the game with the CI build of this branch.
author_association: MEMBER
user: login: jordigo
created_at: 2025-03-21T15:06:56Z
body: @johnnovak I don't have the required environment for compiling myself so I've downloaded the available ZIP (dosbox-staging-windows-x64-0.83.0-alpha-19aca) from https://github.com/dosbox-staging/dosbox-staging/actions/runs/13988180088 and it works beautifully. Thank you very much!
author_association: NONE
user: login: johnnovak
created_at: 2025-03-22T04:40:48Z
body: Thanks for the review @shermp. I've addressed your comments.
author_association: MEMBER
user: login: johnnovak
created_at: 2025-03-22T14:30:45Z
body: @jordigo Confirming this also fixes the exit to DOS problem when opening the options screen in-game in 1.08F.
author_association: MEMBER
user: login: jordigo
created_at: 2025-03-22T14:40:10Z
body: @johnnovak Indeed, sorry I forgot to mention I tested it with the 1.08F updated version of the game. Thanks for the effort and fix!
author_association: NONE