Atlas - SDL_dosmodes.c
Home / ext / SDL / src / video / dos Lines: 1 | Size: 25479 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2026 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21 22#include "SDL_internal.h" 23 24#ifdef SDL_VIDEO_DRIVER_DOSVESA 25 26// SDL internals 27#include "../../events/SDL_mouse_c.h" 28#include "../SDL_sysvideo.h" 29#include "SDL_dosframebuffer_c.h" 30 31// DOS declarations 32#include "SDL_dosmodes.h" 33#include "SDL_dosvideo.h" 34#include <sys/movedata.h> // for dosmemput (banked framebuffer access) 35 36// Some VESA usage information: 37// https://delorie.com/djgpp/doc/ug/graphics/vesa.html.en 38// https://delorie.com/djgpp/doc/ug/graphics/vbe20.html 39// https://wiki.osdev.org/User:Omarrx024/VESA_Tutorial 40// https://wiki.osdev.org/VESA_Video_Modes 41// https://www.phatcode.net/res/221/files/vbe20.pdf 42 43// VBE mode attribute bits (ModeAttributes field) 44#define VBE_MODEATTR_SUPPORTED 0x01 // bit 0: mode supported in hardware 45#define VBE_MODEATTR_COLOR 0x08 // bit 3: color mode (vs. monochrome) 46#define VBE_MODEATTR_GRAPHICS 0x10 // bit 4: graphics mode (vs. text) 47#define VBE_MODEATTR_LFB 0x80 // bit 7: linear framebuffer available 48 49// Minimum required mode attributes for usable graphics modes 50#define VBE_MODEATTR_REQUIRED (VBE_MODEATTR_SUPPORTED | VBE_MODEATTR_COLOR | VBE_MODEATTR_GRAPHICS) 51 52// VBE memory model types (MemoryModel field) 53#define VBE_MEMMODEL_PACKED_PIXEL 4 // packed pixel (includes 8-bit indexed) 54#define VBE_MEMMODEL_DIRECT_COLOR 6 // direct color (RGB with masks) 55 56// VBE set-mode flag 57#define VBE_SETMODE_LFB 0x4000 // request linear framebuffer when setting mode 58 59// VBE mode list sentinel 60#define VBE_MODELIST_END 0xFFFF 61 62// VGA mode 13h (320x200x256) is universally supported, but most VESA 63// BIOSes do not include it in their VESA mode list. This sentinel 64// value is used to identify the legacy fallback. 65#define VGA_MODE_13H_SENTINEL 0xFFEE 66#define VGA_MODE_13H_SEGMENT 0xA000 67 68#pragma pack(push, 1) 69// this is the struct from the hardware; we save only a few parts from this, later, in SDL_VESAInfo. 70typedef struct SDL_VESAHardwareInfo 71{ 72 Uint8 VESASignature[4]; 73 Uint16 VESAVersion; 74 Uint32 OEMStringPtr; // segment:offset 75 Uint8 Capabilities[4]; 76 Uint32 VideoModePtr; // segment:offset 77 Uint16 TotalMemory; 78 Uint16 OEMSoftwareRev; 79 Uint32 OEMVendorNamePtr; // segment:offset 80 Uint32 OEMProductNamePtr; // segment:offset 81 Uint32 OEMProductRevPtr; // segment:offset 82 Uint8 Reserved[222]; 83 Uint8 OemData[256]; 84} SDL_VESAHardwareInfo; 85 86typedef struct SDL_VESAModeHardwareInfo 87{ 88 Uint16 ModeAttributes; 89 Uint8 WinAAttributes; 90 Uint8 WinBAttributes; 91 Uint16 WinGranularity; 92 Uint16 WinSize; 93 Uint16 WinASegment; 94 Uint16 WinBSegment; 95 Uint32 WinFuncPtr; 96 Uint16 BytesPerScanLine; 97 Uint16 XResolution; 98 Uint16 YResolution; 99 Uint8 XCharSize; 100 Uint8 YCharSize; 101 Uint8 NumberOfPlanes; 102 Uint8 BitsPerPixel; 103 Uint8 NumberOfBanks; 104 Uint8 MemoryModel; 105 Uint8 BankSize; 106 Uint8 NumberOfImagePages; 107 Uint8 Reserved_page; 108 Uint8 RedMaskSize; 109 Uint8 RedMaskPos; 110 Uint8 GreenMaskSize; 111 Uint8 GreenMaskPos; 112 Uint8 BlueMaskSize; 113 Uint8 BlueMaskPos; 114 Uint8 ReservedMaskSize; 115 Uint8 ReservedMaskPos; 116 Uint8 DirectColorModeInfo; 117 Uint32 PhysBasePtr; 118 Uint32 OffScreenMemOffset; 119 Uint16 OffScreenMemSize; 120 Uint8 Reserved[206]; 121} SDL_VESAModeHardwareInfo; 122#pragma pack(pop) 123 124typedef struct SDL_VESAInfo 125{ 126 Uint16 version; // 0x200 == 2.0, etc. 127 Uint32 total_memory; // in bytes (SDL_VESAHardwareInfo::TotalMemory does it in 64k pages). 128 Uint32 video_addr_segoffset; // real mode segment:offset (only valid while VBE info block is allocated!) 129 Uint16 *mode_list; // copied out of conventional memory before freeing 130 int num_modes; 131 Uint16 oem_software_revision; 132 char *oem_string; 133 char *oem_vendor; 134 char *oem_product; 135 char *oem_revision; 136} SDL_VESAInfo; 137 138static SDL_VESAInfo *vesa_info = NULL; 139 140void DOSVESA_FreeVESAInfo(void) 141{ 142 if (vesa_info) { 143 SDL_free(vesa_info->mode_list); 144 SDL_free(vesa_info->oem_string); 145 SDL_free(vesa_info->oem_vendor); 146 SDL_free(vesa_info->oem_product); 147 SDL_free(vesa_info->oem_revision); 148 SDL_free(vesa_info); 149 vesa_info = NULL; 150 } 151} 152 153static const SDL_VESAInfo *GetVESAInfo(void) 154{ 155 if (vesa_info) { 156 return vesa_info; 157 } 158 159 _go32_dpmi_seginfo hwinfo_seginfo; 160 SDL_VESAHardwareInfo *hwinfo = (SDL_VESAHardwareInfo *)DOS_AllocateConventionalMemory(sizeof(*hwinfo), &hwinfo_seginfo); 161 if (!hwinfo) { 162 return NULL; 163 } 164 165 SDL_zerop(hwinfo); 166 SDL_memcpy(hwinfo->VESASignature, "VBE2", 4); 167 168 __dpmi_regs regs; 169 regs.x.ax = 0x4F00; 170 regs.x.es = DOS_LinearToPhysical(hwinfo) / 16; 171 regs.x.di = DOS_LinearToPhysical(hwinfo) & 0xF; 172 __dpmi_int(0x10, ®s); 173 174 // al is 0x4F if VESA is supported, ah is 0x00 if this specific call succeeded. 175 // If the interrupt call didn't replace VESASignature with "VESA" then something went wrong, too. 176 if ((regs.x.ax != 0x004F) || (SDL_memcmp(hwinfo->VESASignature, "VESA", 4) != 0)) { 177 SDL_SetError("VESA video not supported on this system"); 178 } else { 179 vesa_info = (SDL_VESAInfo *)SDL_calloc(1, sizeof(*vesa_info)); 180 if (vesa_info) { 181 vesa_info->version = hwinfo->VESAVersion; 182 vesa_info->total_memory = ((Uint32)hwinfo->TotalMemory) * (64 * 1024); // TotalMemory is 64k chunks, convert to bytes. 183 vesa_info->video_addr_segoffset = hwinfo->VideoModePtr; 184 vesa_info->oem_software_revision = hwinfo->OEMSoftwareRev; 185 // these strings are often empty (or maybe NULL), but it's fine. We don't _actually_ need them. 186 vesa_info->oem_string = DOS_GetFarPtrCString(hwinfo->OEMStringPtr); 187 vesa_info->oem_vendor = DOS_GetFarPtrCString(hwinfo->OEMVendorNamePtr); 188 vesa_info->oem_product = DOS_GetFarPtrCString(hwinfo->OEMProductNamePtr); 189 vesa_info->oem_revision = DOS_GetFarPtrCString(hwinfo->OEMProductRevPtr); 190 191 // Copy the mode list out of conventional memory BEFORE freeing 192 // the VBE info block. Some VESA BIOSes store the mode list 193 // inside the 512-byte info block itself. If we free the block 194 // first, the mode list pointer becomes dangling and we read 195 // garbage, silently losing modes. 196 { 197 Uint32 segoffset = vesa_info->video_addr_segoffset; 198 int count = 0; 199 while (DOS_PeekUint16(segoffset + count * sizeof(Uint16)) != VBE_MODELIST_END) { 200 count++; 201 if (count > 64) 202 break; // sanity limit 203 } 204 vesa_info->num_modes = count; 205 vesa_info->mode_list = (Uint16 *)SDL_malloc(count * sizeof(Uint16)); 206 if (vesa_info->mode_list) { 207 for (int i = 0; i < count; i++) { 208 vesa_info->mode_list[i] = DOS_PeekUint16(segoffset + i * sizeof(Uint16)); 209 } 210 } else { 211 vesa_info->num_modes = 0; 212 } 213 } 214 } 215 } 216 217 DOS_FreeConventionalMemory(&hwinfo_seginfo); 218 219 return vesa_info; 220} 221 222// Test by writing and reading back the DAC Pixel Mask register 223// On VGA this is a read/write register, on EGA/CGA the port either 224// doesn't exist (reads 0xFF) or isn't writable. 225static bool DetectVGA(void) 226{ 227 const Uint8 original = inportb(VGA_DAC_PIXEL_MASK); 228 outportb(VGA_DAC_PIXEL_MASK, 0xA5); 229 (void)inportb(0x80); // small I/O delay 230 const Uint8 readback = inportb(VGA_DAC_PIXEL_MASK); 231 outportb(VGA_DAC_PIXEL_MASK, original); 232 233 return (readback == 0xA5); 234} 235 236bool DOSVESA_SupportsVESA(void) 237{ 238 // We need at least VGA hardware (for mode 13h). EGA and CGA cards 239 // do not support 256 colors or programmable palettes. 240 if (!DetectVGA()) { 241 return SDL_SetError("No VGA card detected"); 242 } 243 244 // Cache VESA info if available (NULL mean we only have VGA). 245 (void)GetVESAInfo(); 246 247 return true; 248} 249 250const char *DOSVESA_GetGPUName(void) 251{ 252 const SDL_VESAInfo *vinfo = GetVESAInfo(); 253 if (!vinfo) { 254 return "VGA"; 255 } 256 257 if (vinfo->oem_product && *vinfo->oem_product) { 258 return vinfo->oem_product; 259 } 260 if (vinfo->oem_vendor && *vinfo->oem_vendor) { 261 return vinfo->oem_vendor; 262 } 263 if (vinfo->oem_string && *vinfo->oem_string) { 264 return vinfo->oem_string; 265 } 266 267 return "VESA"; 268} 269 270Uint32 DOSVESA_GetVESATotalMemory(void) 271{ 272 const SDL_VESAInfo *info = GetVESAInfo(); 273 return info ? info->total_memory : 0; 274} 275 276static bool GetVESAModeInfo(Uint16 mode_id, SDL_DisplayModeData *info) 277{ 278 _go32_dpmi_seginfo hwinfo_seginfo; 279 SDL_VESAModeHardwareInfo *hwinfo = (SDL_VESAModeHardwareInfo *)DOS_AllocateConventionalMemory(sizeof(*hwinfo), &hwinfo_seginfo); 280 if (!hwinfo) { 281 return false; 282 } 283 284 SDL_zerop(hwinfo); 285 286 __dpmi_regs regs; 287 regs.x.ax = 0x4F01; 288 regs.x.es = DOS_LinearToPhysical(hwinfo) / 16; 289 regs.x.di = DOS_LinearToPhysical(hwinfo) & 0xF; 290 regs.x.cx = mode_id; 291 __dpmi_int(0x10, ®s); 292 293 const bool retval = (regs.x.ax == 0x004F); 294 if (retval) { 295 SDL_zerop(info); 296 info->mode_id = mode_id; 297 info->attributes = hwinfo->ModeAttributes; 298 info->pitch = hwinfo->BytesPerScanLine; 299 info->w = hwinfo->XResolution; 300 info->h = hwinfo->YResolution; 301 info->num_planes = hwinfo->NumberOfPlanes; 302 info->bpp = hwinfo->BitsPerPixel; 303 info->memory_model = hwinfo->MemoryModel; 304 info->num_image_pages = hwinfo->NumberOfImagePages; 305 info->red_mask_size = hwinfo->RedMaskSize; 306 info->red_mask_pos = hwinfo->RedMaskPos; 307 info->green_mask_size = hwinfo->GreenMaskSize; 308 info->green_mask_pos = hwinfo->GreenMaskPos; 309 info->blue_mask_size = hwinfo->BlueMaskSize; 310 info->blue_mask_pos = hwinfo->BlueMaskPos; 311 info->physical_base_addr = hwinfo->PhysBasePtr; 312 313 // VBE 1.2 banked framebuffer fields 314 info->has_lfb = (hwinfo->ModeAttributes & VBE_MODEATTR_LFB) != 0; 315 info->win_granularity = hwinfo->WinGranularity; 316 info->win_size = hwinfo->WinSize; 317 info->win_a_segment = hwinfo->WinASegment; 318 info->win_func_ptr = hwinfo->WinFuncPtr; 319 info->win_a_attributes = hwinfo->WinAAttributes; 320 } 321 322 DOS_FreeConventionalMemory(&hwinfo_seginfo); 323 324 return retval; 325} 326 327bool DOSVESA_GetDisplayModes(SDL_VideoDevice *device, SDL_VideoDisplay *sdl_display) 328{ 329 const SDL_VESAInfo *vinfo = GetVESAInfo(); 330 331 // Enumerate VESA modes if we have a VBE 1.2+ BIOS. Older VBE versions 332 // (1.0, 1.1) or no VESA at all just skip this loop and fall through to 333 // the VGA mode 13h fallback below. 334 int num_vesa_modes = (vinfo && vinfo->version >= 0x102) ? vinfo->num_modes : 0; 335 336 for (int mi = 0; mi < num_vesa_modes; mi++) { 337 const Uint16 modeid = vinfo->mode_list[mi]; 338 339 SDL_DisplayModeData info; 340 if (!GetVESAModeInfo(modeid, &info)) { 341 continue; 342 } 343 344 // Skip 320x200x8 from VESA. We always want VGA mode 13h for this 345 // resolution (no page flip, no LFB overhead, universal compatibility). 346 if (info.bpp == 8 && info.w == 320 && info.h == 200) { 347 continue; 348 } 349 350 if ((info.attributes & VBE_MODEATTR_REQUIRED) != VBE_MODEATTR_REQUIRED) { 351 continue; 352 } 353 354 if (!(info.attributes & VBE_MODEATTR_LFB) && (info.win_a_attributes & VBE_WINATTR_USABLE) != VBE_WINATTR_USABLE) { 355 continue; 356 } 357 358 if (info.num_planes != 1) { 359 continue; // skip planar pixel layouts. 360 } else if (info.bpp < 8) { 361 continue; // skip anything below 8-bit. 362 } else if (!info.w || !info.h) { 363 continue; // zero-area display mode?! 364 } else if (!info.has_lfb && !info.win_granularity) { 365 continue; // banked mode with zero granularity would cause division by zero. 366 } else if ((info.memory_model != VBE_MEMMODEL_PACKED_PIXEL) && (info.memory_model != VBE_MEMMODEL_DIRECT_COLOR)) { 367 continue; // must be either packed pixel or Direct Color. 368 // Note: 8-bit indexed modes are packed pixel. 369 } 370 371 SDL_PixelFormat format = SDL_PIXELFORMAT_UNKNOWN; 372 if (info.memory_model == VBE_MEMMODEL_PACKED_PIXEL) { 373 switch (info.bpp) { 374 case 8: 375 format = SDL_PIXELFORMAT_INDEX8; 376 break; 377 case 15: 378 format = SDL_PIXELFORMAT_XRGB1555; 379 break; 380 case 16: 381 format = SDL_PIXELFORMAT_RGB565; 382 break; 383 case 24: 384 format = SDL_PIXELFORMAT_RGB24; 385 break; 386 case 32: 387 format = SDL_PIXELFORMAT_XRGB8888; 388 break; 389 default: 390 break; 391 } 392 } else { 393 SDL_assert(info.memory_model == VBE_MEMMODEL_DIRECT_COLOR); 394 const Uint32 rmask = ((((Uint32)1) << info.red_mask_size) - 1) << info.red_mask_pos; 395 const Uint32 gmask = ((((Uint32)1) << info.green_mask_size) - 1) << info.green_mask_pos; 396 const Uint32 bmask = ((((Uint32)1) << info.blue_mask_size) - 1) << info.blue_mask_pos; 397 format = SDL_GetPixelFormatForMasks(info.bpp, rmask, gmask, bmask, 0x00000000); 398 } 399 400 if (format == SDL_PIXELFORMAT_UNKNOWN) { 401 continue; // don't know what to do with this one. 402 } 403 404 SDL_DisplayModeData *internal = (SDL_DisplayModeData *)SDL_malloc(sizeof(*internal)); 405 if (!internal) { 406 continue; // oof. 407 } 408 409 SDL_copyp(internal, &info); 410 411 SDL_DisplayMode mode; 412 SDL_zero(mode); 413 mode.format = format; 414 mode.w = (int)info.w; 415 mode.h = (int)info.h; 416 mode.pixel_density = 1.0f; // no HighDPI scaling here. 417 418 // !!! FIXME: we need to parse EDID data (VESA function 0x4F15, subfunction 0x01) to get refresh rates. Leaving as 0 for now. 419 // float refresh_rate; /**< refresh rate (or 0.0f for unspecified) */ 420 // int refresh_rate_numerator; /**< precise refresh rate numerator (or 0 for unspecified) */ 421 // int refresh_rate_denominator; /**< precise refresh rate denominator */ 422 423 mode.internal = internal; 424 425 if (!SDL_AddFullscreenDisplayMode(sdl_display, &mode)) { 426 SDL_free(internal); // oh well, carry on without it. 427 } 428 } 429 430 // Always add VGA mode 13h for 320x200x8. We skipped any VESA 320x200x8 431 // modes above so this is the only entry at that resolution. 432 { 433 SDL_DisplayModeData *internal = (SDL_DisplayModeData *)SDL_malloc(sizeof(*internal)); 434 if (internal) { 435 SDL_zerop(internal); 436 internal->mode_id = VGA_MODE_13H_SENTINEL; 437 internal->attributes = VBE_MODEATTR_REQUIRED; 438 internal->pitch = 320; 439 internal->w = 320; 440 internal->h = 200; 441 internal->num_planes = 1; 442 internal->bpp = 8; 443 internal->memory_model = VBE_MEMMODEL_PACKED_PIXEL; 444 internal->num_image_pages = 0; // no page flipping in mode 13h 445 internal->physical_base_addr = 0; 446 internal->has_lfb = false; 447 internal->win_granularity = 64; 448 internal->win_size = 64; 449 internal->win_a_segment = VGA_MODE_13H_SEGMENT; 450 internal->win_func_ptr = 0; 451 internal->win_a_attributes = VBE_WINATTR_USABLE; 452 453 SDL_DisplayMode mode; 454 SDL_zero(mode); 455 mode.format = SDL_PIXELFORMAT_INDEX8; 456 mode.w = 320; 457 mode.h = 200; 458 mode.pixel_density = 1.0f; 459 mode.internal = internal; 460 461 if (!SDL_AddFullscreenDisplayMode(sdl_display, &mode)) { 462 SDL_free(internal); 463 } 464 } 465 } 466 467 // Sort modes descending: largest to smallest (by width first, then height, then bpp). 468 for (int i = 0; i < sdl_display->num_fullscreen_modes - 1; i++) { 469 for (int j = i + 1; j < sdl_display->num_fullscreen_modes; j++) { 470 const SDL_DisplayMode *a = &sdl_display->fullscreen_modes[i]; 471 const SDL_DisplayMode *b = &sdl_display->fullscreen_modes[j]; 472 bool swap = false; 473 if (b->w > a->w) { 474 swap = true; 475 } else if (b->w == a->w) { 476 if (b->h > a->h) { 477 swap = true; 478 } else if (b->h == a->h) { 479 if (SDL_BITSPERPIXEL(b->format) > SDL_BITSPERPIXEL(a->format)) { 480 swap = true; 481 } 482 } 483 } 484 if (swap) { 485 SDL_DisplayMode tmp = sdl_display->fullscreen_modes[i]; 486 sdl_display->fullscreen_modes[i] = sdl_display->fullscreen_modes[j]; 487 sdl_display->fullscreen_modes[j] = tmp; 488 } 489 } 490 } 491 492 return true; 493} 494 495bool DOSVESA_SetDisplayMode(SDL_VideoDevice *device, SDL_VideoDisplay *sdl_display, SDL_DisplayMode *mode) 496{ 497 SDL_VideoData *data = device->internal; 498 const SDL_DisplayModeData *modedata = mode->internal; 499 500 if (data->current_mode.internal && (data->current_mode.internal->mode_id == modedata->mode_id)) { 501 return true; 502 } 503 504 DOSVESA_InvalidateCachedFramebuffer(); 505 506 if (data->mapping.size) { 507 __dpmi_free_physical_address_mapping(&data->mapping); // dump existing video mapping. 508 SDL_zero(data->mapping); 509 } 510 511 __dpmi_regs regs; 512 513 if (modedata->mode_id == VGA_MODE_13H_SENTINEL) { 514 // Set VGA mode 13h (320x200x256) via legacy BIOS call. 515 SDL_zero(regs); 516 regs.x.ax = 0x0013; 517 __dpmi_int(0x10, ®s); 518 // Mode 13h always succeeds on VGA hardware; no status to check. 519 520 data->banked_mode = true; // uses A000:0000 segment, no LFB 521 522 SDL_copyp(&data->current_mode, mode); 523 524 data->page_flip_available = false; 525 data->current_page = 0; 526 data->page_offset[0] = 0; 527 data->page_offset[1] = 0; 528 529 // Clear the framebuffer 530 { 531 Uint8 zero_buf[320]; 532 SDL_memset(zero_buf, 0, sizeof(zero_buf)); 533 Uint32 vga_base = (Uint32)VGA_MODE_13H_SEGMENT << 4; 534 for (int row = 0; row < 200; row++) { 535 dosmemput(zero_buf, 320, vga_base + row * 320); 536 } 537 } 538 539 if (SDL_GetMouse()->internal != NULL) { 540 regs.x.ax = 0x7; 541 regs.x.cx = 0; 542 regs.x.dx = (Uint16)(mode->w - 1); 543 __dpmi_int(0x33, ®s); 544 545 regs.x.ax = 0x8; 546 regs.x.cx = 0; 547 regs.x.dx = (Uint16)(mode->h - 1); 548 __dpmi_int(0x33, ®s); 549 } 550 551 return true; 552 } 553 554 // When the direct-FB hint is active, prefer banked mode. This needs 555 // to be set explicitly for some cards (Intel 740). 556 const bool is_banked_usable = modedata->win_a_segment && 557 modedata->win_size > 0 && 558 (modedata->win_a_attributes & VBE_WINATTR_USABLE) == VBE_WINATTR_USABLE; 559 const bool use_lfb = modedata->has_lfb && 560 (!is_banked_usable || !SDL_GetHintBoolean(SDL_HINT_DOS_ALLOW_DIRECT_FRAMEBUFFER, false)); 561 562 regs.x.ax = 0x4F02; 563 regs.x.bx = modedata->mode_id | (use_lfb ? VBE_SETMODE_LFB : 0); 564 __dpmi_int(0x10, ®s); 565 566 if (regs.x.ax != 0x004F) { 567 return SDL_SetError("Failed to set VESA video mode"); 568 } 569 570 data->banked_mode = !use_lfb; 571 572 if (use_lfb) { 573 data->mapping.address = modedata->physical_base_addr; 574 data->mapping.size = DOSVESA_GetVESATotalMemory(); 575 if (__dpmi_physical_address_mapping(&data->mapping) != 0) { 576 SDL_zero(data->mapping); 577 regs.x.ax = 0x03; // try to dump us back into text mode. Not sure if this is a good idea, though. 578 __dpmi_int(0x10, ®s); 579 SDL_zero(data->current_mode); 580 return SDL_SetError("Failed to map VESA video memory"); 581 } 582 583 // make sure framebuffer is blanked out. 584 SDL_memset(DOS_PhysicalToLinear(data->mapping.address), '\0', (Uint32)modedata->h * (Uint32)modedata->pitch); 585 } else { 586 // Banked mode: no physical address mapping needed. 587 // Blank the visible framebuffer through the banked window. 588 Uint32 total_bytes = (Uint32)modedata->h * (Uint32)modedata->pitch; 589 Uint32 win_gran_bytes = (Uint32)modedata->win_granularity * 1024; 590 Uint32 win_size_bytes = (Uint32)modedata->win_size * 1024; 591 Uint32 win_base = (Uint32)modedata->win_a_segment << 4; 592 Uint8 zero_buf[1024]; 593 SDL_memset(zero_buf, 0, sizeof(zero_buf)); 594 595 Uint32 offset = 0; 596 int current_bank = -1; 597 while (offset < total_bytes) { 598 int bank = (int)(offset / win_gran_bytes); 599 Uint32 off_in_win = offset % win_gran_bytes; 600 Uint32 n = win_size_bytes - off_in_win; 601 if (n > total_bytes - offset) { 602 n = total_bytes - offset; 603 } 604 605 if (bank != current_bank) { 606 __dpmi_regs bregs; 607 SDL_zero(bregs); 608 bregs.x.bx = 0; // Window A 609 bregs.x.dx = (Uint16)bank; 610 if (modedata->win_func_ptr) { 611 // Call WinFuncPtr directly — faster than INT 10h. 612 bregs.x.cs = (Uint16)(modedata->win_func_ptr >> 16); 613 bregs.x.ip = (Uint16)(modedata->win_func_ptr & 0xFFFF); 614 __dpmi_simulate_real_mode_procedure_retf(&bregs); 615 } else { 616 bregs.x.ax = 0x4F05; 617 __dpmi_int(0x10, &bregs); 618 } 619 current_bank = bank; 620 } 621 622 // Zero in 1KB chunks via dosmemput 623 Uint32 written = 0; 624 while (written < n) { 625 Uint32 chunk = n - written; 626 if (chunk > sizeof(zero_buf)) { 627 chunk = sizeof(zero_buf); 628 } 629 dosmemput(zero_buf, chunk, win_base + off_in_win + written); 630 written += chunk; 631 } 632 offset += n; 633 } 634 } 635 636 SDL_copyp(&data->current_mode, mode); 637 638 // Set up page-flipping if the mode has at least 1 image page (meaning 2 total) 639 // Note: page-flipping is only supported with LFB modes. With banked modes, 640 // we would still need to bank-switch through the same 64KB window to write 641 // to the back page, so the performance benefit is minimal (just tear-free). 642 // For simplicity, disable page-flipping in banked mode for now. 643 if (!data->banked_mode && modedata->num_image_pages >= 1) { 644 data->page_flip_available = true; 645 data->current_page = 0; 646 data->page_offset[0] = 0; 647 data->page_offset[1] = (Uint32)modedata->pitch * (Uint32)modedata->h; 648 649 // Check that both pages fit within the mapped region. 650 Uint32 page1_end = data->page_offset[1] + (Uint32)modedata->pitch * (Uint32)modedata->h; 651 if (page1_end > data->mapping.size) { 652 data->page_flip_available = false; 653 data->page_offset[1] = 0; 654 } else { 655 // Also blank the second page 656 SDL_memset((Uint8 *)DOS_PhysicalToLinear(data->mapping.address) + data->page_offset[1], 657 '\0', (Uint32)modedata->pitch * (Uint32)modedata->h); 658 659 // Start display at page 0 660 regs.x.ax = 0x4F07; 661 regs.x.bx = 0x0000; // set display start, wait for retrace 662 regs.x.cx = 0; // first pixel in scan line 663 regs.x.dx = 0; // first scan line 664 __dpmi_int(0x10, ®s); 665 } 666 } else { 667 data->page_flip_available = false; 668 data->current_page = 0; 669 data->page_offset[0] = 0; 670 data->page_offset[1] = 0; 671 } 672 673 if (SDL_GetMouse()->internal != NULL) { // internal != NULL) == int 33h services available. 674 regs.x.ax = 0x7; // set mouse min/max horizontal position. 675 regs.x.cx = 0; 676 regs.x.dx = (Uint16)(mode->w - 1); 677 __dpmi_int(0x33, ®s); 678 679 regs.x.ax = 0x8; // set mouse min/max vertical position. 680 regs.x.cx = 0; 681 regs.x.dx = (Uint16)(mode->h - 1); 682 __dpmi_int(0x33, ®s); 683 } 684 685 return true; 686} 687 688#endif // SDL_VIDEO_DRIVER_DOSVESA 689[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.